Live data from Hacker News

A Failed Experiment with Python Type Annotations

mortoray.com

41–50 of 82 posts

Re: A Failed Experiment with Python Type Annotations

#41
post #26

Why would you keep Python if you want static typing? I mean, there are several modern, performant (more than Python actually) statically typed languages. Rust, Go, Kotlin, Scala, Swift… All of them are mature and have a good library ecosystem. So, except if you need some math/AI/ML packages available only for Python, why bother? Choose the right tool for the job, no?

Scala: Python + dataclasses [+ mypy] offers a programming experience close to pragmatic Scala. The tradeoff becomes: * worse performance * lack of immutable vectors, alleviated via style conventions * rarely, awkward lambdas vs. * wide pool of people familiar with the language * lack of JVM lockin: Scala native is not there yet, the library ecosystem is all JVM. * [good] batteries included * much better reflection *…

> * lack of immutable vectors, alleviated via style conventions

Note that mypy/typed python fixes this: if you annotate a function as returning (or accepting) a Sequence/Collection/Iterable instead of a List, and attempt to mutate it, you'll get type errors. This is good practice anyway.

Re: A Failed Experiment with Python Type Annotations

#42
post #30
post #28

Earlier quoted context omitted.

It looks like the constructor is being typechecked, but probably not attribute access. `val.child.junk` will pass because `val.child` is `Any` and you can do any-thing to an Any.

That seems to work too.. a = Foo(Foo(Foo(None))) b = Bar(Bar(Bar(None))) c = Hep(Hep(Hep(None))) a.child.junk = 1 # error: Item "Foo" of "Optional[Foo]" has no attribute "junk" # error: Item "None" of "Optional[Foo]" has no attribute "junk" b.child.junk = 'a' # error: Item "Bar" of "Optional[Bar]" has no attribute "junk" # error: Item "None" of "Optional[Bar]" has no attribute "junk" c.child.junk = 1.1 # error: Item…

Hey, thanks a lot for pointing this out! Yet another reason to use data classes vs. named tuples.

Looks like just plain class level attribute declaration also works:

    class Node:
        child: 'Node'
I wonder why `typing.NamedTuple` is unique as far as it not working. I know they don't use `eval` and templating to create it anymore. From looking at the code [0], they're using metaclass / __new__. But other than the fact that it uses metaclasses, I'm not sure why it'd have an error. Obviously there's cycle handling in mypy, otherwise none of the examples would work. If I use an example with a metaclass, that also typechecks. So, it's not metaclasses that trigger the error.

    class NodeBase(type):
        def __new__(cls, name, bases, attrs):
            return super().__new__(cls, name, bases, attrs)


    class Node(metaclass=NodeBase):
        child: 'Node'  # works
Edit: it looks like there is now work going into this and other cases where recursive types don't work [1][2]. For example: `Callback = typing.Callable[[str], Callback]` and `Foo = Union[str, List['Foo']]`.

[0] https://github.com/python/cpython/blob/3.8/Lib/typing.py#L15...

[1] https://github.com/python/mypy/issues/731

[2] https://github.com/python/mypy/issues/6204

Re: A Failed Experiment with Python Type Annotations

#44
post #26

Why would you keep Python if you want static typing? I mean, there are several modern, performant (more than Python actually) statically typed languages. Rust, Go, Kotlin, Scala, Swift… All of them are mature and have a good library ecosystem. So, except if you need some math/AI/ML packages available only for Python, why bother? Choose the right tool for the job, no?

Scala: Python + dataclasses [+ mypy] offers a programming experience close to pragmatic Scala. The tradeoff becomes: * worse performance * lack of immutable vectors, alleviated via style conventions * rarely, awkward lambdas vs. * wide pool of people familiar with the language * lack of JVM lockin: Scala native is not there yet, the library ecosystem is all JVM. * [good] batteries included * much better reflection *…

> Swift: Apple lock-in.

Swift runs on Linux and has done since it was made open source in 2015, a year after its initial announcement. Windows support is being worked on but, for the time being, it runs fine in WSL. No lock in.

Re: A Failed Experiment with Python Type Annotations

#46

What's possibly so cumbersome about defining return types? It does seem a little embarrassing that the annotations don't even support circular references.

Circular references like that are called "recursive types" in formal type theory. They are one of those big issues that academics like to write papers about.

Even if you just want to write a practical interpreter, and choose to gloss over the issues, they will still come back in some disguised form and either by requiring some sort of implementation kludge, or just by creating weird edge cases.

Re: A Failed Experiment with Python Type Annotations

#47
post #26

Earlier quoted context omitted.

Scala: Python + dataclasses [+ mypy] offers a programming experience close to pragmatic Scala. The tradeoff becomes: * worse performance * lack of immutable vectors, alleviated via style conventions * rarely, awkward lambdas vs. * wide pool of people familiar with the language * lack of JVM lockin: Scala native is not there yet, the library ecosystem is all JVM. * [good] batteries included * much better reflection *…

No immutable vectors in Python? Are you not aware of tuples? Lambdas in Python are quite limited, but offset by natural nested local functions. Regarding performance, that's not always an issue, and depending on the use case can be addressed with async/await or using a multiprocessing pool. I have had issues with multiprocessing and some DB libs ib the past, but recently have had pretty good success. Multithreading i…

Sorry, I meant persistent vectors. Data structures with type Vector[T] instead of Tuple[T0, T1, ...], amortized O(1) updates and list-like syntax for literals, e.g. i[x0, x1]. It's a minor annoyance, in most cases using vanilla Python lists with the convention 'any list that is part of a dataclass / is passed across function boundaries should never be mutated' is good enough. As u/joshuamorton noticed in this thread, the convention can be enforced with mypy and Sequence[T], which is great!

Re: A Failed Experiment with Python Type Annotations

#48
post #26

Earlier quoted context omitted.

Scala: Python + dataclasses [+ mypy] offers a programming experience close to pragmatic Scala. The tradeoff becomes: * worse performance * lack of immutable vectors, alleviated via style conventions * rarely, awkward lambdas vs. * wide pool of people familiar with the language * lack of JVM lockin: Scala native is not there yet, the library ecosystem is all JVM. * [good] batteries included * much better reflection *…

> Swift: Apple lock-in. Swift runs on Linux and has done since it was made open source in 2015, a year after its initial announcement. Windows support is being worked on but, for the time being, it runs fine in WSL. No lock in.

How's the cross-platform library ecosystem? My distant sentiment is that the bulk of libraries are tied to / provided by Apple, but I'd be happy to be proven wrong.

Re: A Failed Experiment with Python Type Annotations

#49
post #26

Earlier quoted context omitted.

Scala: Python + dataclasses [+ mypy] offers a programming experience close to pragmatic Scala. The tradeoff becomes: * worse performance * lack of immutable vectors, alleviated via style conventions * rarely, awkward lambdas vs. * wide pool of people familiar with the language * lack of JVM lockin: Scala native is not there yet, the library ecosystem is all JVM. * [good] batteries included * much better reflection *…

> * lack of immutable vectors, alleviated via style conventions Note that mypy/typed python fixes this: if you annotate a function as returning (or accepting) a Sequence/Collection/Iterable instead of a List, and attempt to mutate it, you'll get type errors. This is good practice anyway.

Thanks for the tip!

Re: A Failed Experiment with Python Type Annotations

#50

Earlier quoted context omitted.

> But you should still annotate your public apis. The good thing about inference (especially with a REPL) is you can write it without the annotation, and then use the inferred type (in Haskell, I usually find that when I resist the temptation to explicitly annotate types, the actual types are more general than I would have specified.)

In Haskell, I usually find the quality of error messages to be much worse without top-level annotations.

I agree with that, too. My usual practice with Haskell is leave types off to leverage the information gained from type inference (with the intent of annotating signatures when I'm done), but then tell Haskell what I'm thinking the types should be if things break with impenetrable error messages.

But my coding in Haskell is pretty much personal and toy projects; I like the approach, but it may not be ideal for coding in anger.

Post reply on HN