
Type hints – a mediocre programmer's reaction (2015) - ingve
https://mail.python.org/pipermail/python-dev/2015-April/139267.html
======
pierrebai
That's what you get from duck typing.

Python API are defined solely by how the API uses its argument. The
requirement is already encoded as far as the computer is concerned, except the
check are by default all done at run-time. A Python compiler could work its
way backward to try to verify them at compilation time. Making them explicit
would solely be as a helper for the programmer, which might as well just be in
straight documentation, which can ba as brief as the name of the parameter.

It's all a feature, and this is being actively exploited in code, providing
exceptional flexibility. Its no worse than other language with type inference:
their types can be extremely complex and reading them would not help most of
the time. You're better off with a succint, high-level description, like a
URL-like string, a file-like object, an object indexable by such and such.

~~~
marcosdumay
Well, the problem is on annotating types, when you only actually care about
behavior.

Duck typing isn't a bad thing. In fact, it's great. You just need to annotate
the ducks instead of the types. For a great example of how to do it, look at
Haskell's type classes.

(In fact, Haskell's easyness of integrating generic code is visible even to a
beginner, but I never notice that this is because the entire language is based
on duck typing.)

~~~
aplusbi
To be pedantic, Haskell technically doesn't use duck typing, it uses
structural typing. Duck typing is run-time coercion of types, structural
typing is compile-time inference of minimum interfaces.

~~~
marcosdumay
Noticed.

Interface based typing may be a better generic term.

------
imh
This is totally crazy. With a slightly different syntax it's really not so
bad:

{a:b} for mappings from a to b

(a,b) for tuples of a and b

[a] for iterables of a

a | b for unions of a and b

Then factor out that nasty common thing and you end up with:

    
    
        Optional[    {basestring: StringThing}
                  | [(basestring, StringThing)]
                ]
    
        StringThing = (basestring, Optional[basestring | file]])
                    | (basestring, Optional[basestring | file]], Optional[basestring])
                    | (basestring, Optional[basestring | file]], Optional[basestring], Optional[Headers])    
    
        Headers =  {basestring: basestring}
                | [(basestring, basestring)]
    

It's not inherently complicated nor does it have anything to do with duck
typing, it's just that the syntax chosen is awful.

------
seivan
Couldn't they use helper methods to convert all those types he brought up to a
single common type that they annotated with? It would simplify the API a lot.
I come from a full day of working with associated types, generics, type
erasures and protocol extensions, there is a darker side to it.

I wanted to have a generic protocol that could have some logic of its own
without enforcing you to subclass it. I would regret the decision so much, if
I wasn't busy regretting other decisions even more.

Just enforce a type that you accept that's large enough to be used by many and
convertable from other common types you want to use.

I swear, soon they'll classify programmers urge to write as generic and
extendable as a mental illness.

~~~
snuxoll
> Couldn't they use helper methods to convert all those types he brought up to
> a single common type that they annotated with?

But then it wouldn't be "pythonic". To be frank though, I think being _too_
generous with your inputs is just asking for maintenance hell. I don't see why
requests feels the need to allow data in so many different formats when one or
two + conversion functions for anything else would be sufficient.

~~~
seivan
I've come to realize it's an addiction/urge, that's the only /reasonable/
answer I can give.

~~~
seanp2k2
[https://en.m.wikipedia.org/wiki/Robustness_principle](https://en.m.wikipedia.org/wiki/Robustness_principle)

""" Be conservative in what you do, be liberal in what you accept from others
(often reworded as "Be conservative in what you send, be liberal in what you
accept"). """

~~~
taneq
Didn't we decide later that that's a bad idea? I mean, in the early days of
the web, Internet Explorer was the very embodiment of this principle and a
couple of decades later the internet is still full of the resulting non-
standard crud.

~~~
lomnakkus
Yes we did. It can also lead to really surprising security issues in protocol-
land where such "liberalism" interacts badly with security-sensitive portions
of protocols.

(Security is non-trivial and mostly there are very good reasons that things
are _exactly_ as they are in such protocols.)

------
stuartaxelowen
First reply is a very practical response:

> At this point, you may want to just stop caring about the exact type.

> Part of the point of gradual typing is that you can short-cut a lot of

> this. And quite frankly, this isn't really helping anything. Just skip

> it and say that it's Union[Mapping, Iterable, None].

~~~
pdonis
And the reply to this reply shows the problem:

"This change doesn't catch any of the bugs anyone was going to hit (no-one is
passing a callable to this parameter), and it doesn't catch any of the bugs
someone might actually hit (getting the tuple elements in the wrong order, for
example). At this point the type signature is worse than useless."

------
LyndsySimon
The public interface of a library isn't where type hints will shine, and I'm
not sure why someone would think they would.

Type hints are going to be awesome for nailing down the behavior of the core
parts of your business application. I think of it more like writing good tests
than anything else.

Like everything else in development; 80% of the problem is choosing the right
tool for the job.

~~~
jstimpfle
This. Types, except for those built-in to the language, are not for APIs. But
they can be great for internals if you don't overdo it.

For example, Perl6 has Subset (types), which is a great feature. You could
make a subset type of Str (a built-in type) which contains only strings that
match a given pattern (e.g. URLs, user-ids, or shell-safe strings) and still
benefit from all generic string code.

~~~
nazgul17
I think there is such option in Python ABCs.

------
mangeletti
Thank god I'm not the only one who feels that way about PEP 0484. No offense
to Guido or the other authors, but I don't know what was going on in their
minds.

~~~
toyg
Some PEPs are just wrong. This is one of them. It's kowtowing to dumb editors
when the point of the language is to be as human-friendly as possible. If we
wanted an IDE-friendly language we'd just use Java.

------
lxe
This isn't just the problem of 'duck-typing' vs 'strong typing.' There is
always going to be data whose shape is very hard to define and annotate
explicitly with intent of machine type checking or verification, yet much
easier to describe to a programmer.

Take for example a spec for a "Set-Cookie" header syntax. It can be described
as
"[https://tools.ietf.org/html/rfc6265#section-4.1.1"](https://tools.ietf.org/html/rfc6265#section-4.1.1"),
which would be a rather unwieldy thing to show, looking similarly to the
pythonic polymorphic argument described in this message.

Type hinting (and strong typing) helps machines 100% of the time, and users
some of the time. It's helpful if your API has an interface that is easy for a
human to understand, even if you describe your data as 'a URI', "truthy" or
even "Any", even if their shapes are not strictly defined.

~~~
duaneb
> 'duck-typing' vs 'strong typing.'

For the n'th time, python is a strong typed AND duck typed language. Did you
mean static?

------
skybrian
Yes, it's going to be hard to retrofit Python libraries that use this style of
parameter passing.

But if you're starting fresh, it's not that hard to use keyword parameters to
support a lot of different options while also having reasonable types. (Take a
look at how Dart libraries do it.)

------
mchahn
Why can't Python have transpilers like JS does? Adding types to Python like
Typescript could be a good thing.

~~~
tdb7893
I feel like JS mainly has transpilers because people are locked in to the
language, which is not true of python

~~~
mchahn
People that choose Python are "locked in" to a language that doesn't have
static typing.

~~~
jorams
You are missing the point. People that choose Python do just that, they
_choose_ Python. The use of JS is very often not a matter of choice, because
it's the only language supported in browsers.

~~~
koder2016
Exactly, there is a difference between torture (JavaScript) and masochism
(Python).

------
raziel2p
Obviously old and feature-full libraries aren't going to benefit much from
type-hinting. More than anything, I see it as a way to encourage new projects
to re-think how they design their APIs - i.e. keeping it simple. That huge
polymorphic argument example just seems like convenience was picked over API
design by the Requests authors, and if you decide on that order of priority,
type-hints aren't going to be useful to you.

~~~
munificent
> That huge polymorphic argument example just seems like convenience was
> picked over API design by the Requests authors

Convenience _is_ API design. In a fully dynamically-typed language, there's no
real downside for heavily polymorphic APIs and a lot of upside in terms of
brevity and flexibility.

I believe pretty strongly that you can design great dynamically-typed APIs for
dynamically-typed languages or great statically-typed APIs for statically-
typed ones, but I don't think you can easily layer static types on a great
dynamic API. The affordances are just too different.

One obvious challenge is that statically typed languages almost always support
overloading. This makes it dramatically easier to have these kinds of
polymorphic APIs while having sane types. Most dynamically typed language
support overloading at all, which is where these giant union types come from
when you try to cram a single static type onto what is effectively a number of
different method overloads.

~~~
duaneb
> In a fully dynamically-typed language, there's no real downside for heavily
> polymorphic APIs and a lot of upside in terms of brevity and flexibility.

Wow, I'd say that's a major pain point for me—reasoning about polymorphic code
with only vague guidelines:

"give me something that looks like a string".

"Well, which types are those?"

"Hell if I know! But it needs these methods, but it can also change its
behavior if it doesn't have them"

"stabs self in neck because this was solved in the 60s"

Seeing a hard-to-skim polymorphic API for anything but a quick script is a
massive problem for me—the time sink in just parsing its behavior isn't worth
the inevitable problems that come with the edge cases at runtime.

That said, requests is great and I use it to do all my web scraping because
it's super idiomatic. I had no idea the interface was that complicated,
though, and it reduces my confidence in the library.

This PEP reminds me of typed racket: pretty awesome, but a completely separate
language to write with separate considerations.

------
dbcurtis
Python isn't about types, it is about protocols. If you are thinking about the
type of something while writing the code, then you are transcoding C++ or Java
into Python. The important thing about a Python instance is not the type, but
the protocols that the instance supplies.

Switching gears and thinking about code validation for a minute, I agree,
protocol conformance checking is a much more difficult problem to resolve at
compile time, at least they way Python duck types support protocol
definitions.

~~~
solidsnack9000
Aren't protocols just a particular kind of abstract type? In Swift,
"interfaces" or "traits" are literally called protocols.

~~~
zaro
If you go that route, anything can be abstract type.

I tend to see it like: type is what this object actually is, and protocol is
how we interact with it.

~~~
solidsnack9000
I see where you are going. But I'm sure we can agree that both concrete types
and protocols would be handled by a "type checker".

If ever there were a language where you'd want to be looking at protocols and
not concrete types most of the time, it would be Python.

------
hcarvalhoalves
Type hints for Python would be useful if used like structural typing from
Prismatic's Schema for Clojure [1], where you can pattern match against
tuples/lists/maps/keys but also against arbitrary predicate functions (such
that you could do `instanceof('foo')` or `hasattr('foo')`).

[1] [https://github.com/plumatic/schema](https://github.com/plumatic/schema)

------
z3t4
In for example JavaScript or other high level programming language: Instead of
implementing type check/hints, your functions should validate the input data
and throw an error if it doesn't meet the requirements. The compiler/VM should
chose the optimal type. Types are for low level systems programming. In high
level programming, think form design and validation instead of types. Example:
Argument "yearBorn" should take a numeric value of two or four numbers. Not a
"unsigned short int".

