Live data from Hacker News

A Failed Experiment with Python Type Annotations

mortoray.com

21–30 of 82 posts

Re: A Failed Experiment with Python Type Annotations

#21
post #16

Other people have mentioned that the self-referential case from this post is doable. But what is not doable (to my knowledge) is this kind of self-reference: class Node(typing.NamedTuple): # or dataclass child: 'Node' The error is example.py:4: error: Recursive types not fully supported yet, nested types replaced with "Any" . This error has been in there for several years at least. And it always bites me when I forge…

Just tried this myself, and it seems to only be an issue for typing.NamedTuple. Though, it still manages to do type checking correctly...

    from __future__ import annotations
    from typing import Optional, NamedTuple
    from dataclasses import dataclass

    class Foo:
        def __init__(self, foo: Foo = None) -> None:
            self.child: Optional[Foo] = foo

    Foo(Foo(Foo(None))) # Works
    Foo(Foo(Foo(1))) # error: Argument 1 to "Foo" has incompatible type "int"; expected "Optional[Foo]"

    @dataclass
    class Bar:
        child: Optional[Bar]

    Bar(Bar(Bar(None))) # Works
    Bar(Bar(Bar('bar'))) # error: Argument 1 to "Bar" has incompatible type "str"; expected "Optional[Bar]"

    class Hep(NamedTuple): # error: Recursive types not fully supported yet, nested types replaced with "Any"
        child: Optional[Hep]

    Hep(Hep(Hep(None))) # Works
    Hep(Hep(Hep({'a': 'b'}))) # error: Argument 1 to "Hep" has incompatible type "Dict[str, str]"; expected "Optional[Hep]"

Re: A Failed Experiment with Python Type Annotations

#22
post #13

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?

> Choose the right tool for the job, no? The thing is, static typing is never the job by itself.

"job" doesn't refer to static typing but the project you are working on.

- script of 2,000 LOC to clean data => dynamic typing is fine => Python

- web service of 1,000,000 LOC => static typing seems a better choice => Rust, Go, Kotlin, Scala, Swift, C#, Java…

Re: A Failed Experiment with Python Type Annotations

#23

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?

Sometimes the right tool for the job is python + mypy.

Re: A Failed Experiment with Python Type Annotations

#24

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?

In general, for a new project of any size, or significance, or where performance/concurrency was a concern; hands down, yes, I would absolutely reach for a strong, statically typed, compiled language.

The fact remains though that Python is an excellent general purpose language, easy to learn, almost universally known, has libraries for everything (and that’s just the stdlib). It is well suited to rapid prototyping, scripting, and data wrangling. Adding incremental typing makes it even better.

Re: A Failed Experiment with Python Type Annotations

#25
The first point can be rather easily circumvented by using strings as forward references and it only takes a bit of googling to figure that out. The second doesn't seem remotely like a deal breaker to me. Half the reason for having type annotations is to provide a description of what a function or method does at a glance. This is idiomatic even in hardcore functional languages like Haskell. With return type inference, you don't get that benefit.

Re: A Failed Experiment with Python Type Annotations

#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

* access to modern ML

* gradual typing

* no actors

Rust: Manual memory management is verbose and distracting.

Go: Manual exception handling is verbose and distracting.

Kotlin: JVM lock-in.

Swift: Apple lock-in.

Re: A Failed Experiment with Python Type Annotations

#28
post #21
post #16

Other people have mentioned that the self-referential case from this post is doable. But what is not doable (to my knowledge) is this kind of self-reference: class Node(typing.NamedTuple): # or dataclass child: 'Node' The error is example.py:4: error: Recursive types not fully supported yet, nested types replaced with "Any" . This error has been in there for several years at least. And it always bites me when I forge…

Just tried this myself, and it seems to only be an issue for typing.NamedTuple. Though, it still manages to do type checking correctly... from __future__ import annotations from typing import Optional, NamedTuple from dataclasses import dataclass class Foo: def __init__(self, foo: Foo = None) -> None: self.child: Optional[Foo] = foo Foo(Foo(Foo(None))) # Works Foo(Foo(Foo(1))) # error: Argument 1 to "Foo" has incompa…

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.

Re: A Failed Experiment with Python Type Annotations

#29

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?

Sometimes the job is "maintaining existing code," and nobody is going to pay for a ground up rewrite.

Re: A Failed Experiment with Python Type Annotations

#30
post #28
post #21

Earlier quoted context omitted.

Just tried this myself, and it seems to only be an issue for typing.NamedTuple. Though, it still manages to do type checking correctly... from __future__ import annotations from typing import Optional, NamedTuple from dataclasses import dataclass class Foo: def __init__(self, foo: Foo = None) -> None: self.child: Optional[Foo] = foo Foo(Foo(Foo(None))) # Works Foo(Foo(Foo(1))) # error: Argument 1 to "Foo" has incompa…

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 "Hep" of "Optional[Hep]" has no attribute "junk"
    # error: Item "None" of "Optional[Hep]" has no attribute "junk"

    a.child = 1
    # error: Incompatible types in assignment (expression has type "int", variable has type "Optional[Foo]")

    b.child = 1
    # error: Incompatible types in assignment (expression has type "int", variable has type "Optional[Bar]")

    c.child = 1
    # error: Property "child" defined in "Hep" is read-only
    # error: Incompatible types in assignment (expression has type "int", variable has type "Optional[Hep]")
I'm using mypy 0.701 with Python 3.7.3.
Post reply on HN