Re: [Python-ideas] PEP 484 (Type Hints) -- second draft
* It's a bit vague how local variables should be typed in the presence of multiple assignments. Consider: ``` if x > 0: root = sqrt(x) # type: int else: root = None ``` It seems that the type of `root` should be inferred as `Union[int, None]` (AKA `Optional[int]`), but there's no discussion about this type of inference in the PEP. Cases like this are the only way that the proposed blank `Undefined` value makes sense to me, using an example like: ``` root = Undefined # type: Optional[int] if x > 0: root = sqrt(x) # type: int else: root = None ``` * +1 for only allowing .pyi as the extension for stub files (and not also .py). Makes it very clear that they should only be interpreted as stubs. * -1 that `Optional[T1]` is unnecessary. It's conceptually more clean and directly corresponds conceptually to types like `int?` and `?int` from other languages. * +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere. -- David Foster http://dafoster.net/ P.S. Apologies if this doesn't add to the message chain correctly. There's no clearly correct way to reply to a digest email from Mailman.
On 3/23/2015 2:02 AM, David Foster wrote:
* It's a bit vague how local variables should be typed in the presence of multiple assignments. Consider:
``` if x > 0: root = sqrt(x) # type: int else: root = None ```
root = sqrt(x) if x > 0 or None # type: Union(int, None)
* +1 for only allowing .pyi as the extension for stub files (and not also .py). Makes it very clear that they should only be interpreted as stubs.
Agreed
* +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere.
cast x to T and is x instance of T, agreed. -- Terry Jan Reedy
On 23Mar2015 04:55, Terry Reedy
On 3/23/2015 2:02 AM, David Foster wrote:
* It's a bit vague how local variables should be typed in the presence of multiple assignments. Consider:
``` if x > 0: root = sqrt(x) # type: int else: root = None ```
root = sqrt(x) if x > 0 or None # type: Union(int, None)
Should people need to reshape their code for this?
In the example above it is small, though less readable already.
But it scales very badly; what of a 4 way if etc? It moves from a bit less
readable to a nightmare.
Cheers,
Cameron Simpson
On Mar 23, 2015, at 2:41 PM, Cameron Simpson
On 23Mar2015 04:55, Terry Reedy
wrote: On 3/23/2015 2:02 AM, David Foster wrote: * It's a bit vague how local variables should be typed in the presence of multiple assignments. Consider:
``` if x > 0: root = sqrt(x) # type: int else: root = None ```
root = sqrt(x) if x > 0 or None # type: Union(int, None)
Should people need to reshape their code for this? In the example above it is small, though less readable already.
If this is going to be the example we keep focusing on... Why would you have a custom int sqrt function whose return type is neither declared nor inferrable? I'd suspect that most variables that are initialized with a function call don't need a type hint, and if there are special cases where it comes up, they may be special enough that a real-life-plausible example is worth having rather than an example that's almost certainly unnecessary (or, in this case, an explicit type error, or code that type checks but only because you accidentally fooled the checker, because the last time I actually wrote, used, or wanted an int sqrt function it was in 6502 assembly...)
But it scales very badly; what of a 4 way if etc? It moves from a bit less readable to a nightmare.
Agreed. In fact, even when what you have is an expression in the first place, Python makes it easy (and relatively cheap) to break it up into a sequence of statements (e.g., assignments to temporary variables), and that's more often a good idea than the other way around, so typing had better work nicely for the statement version of things (and Jukka and Guido seem to have worked hard to make that true in general).
Cheers, Cameron Simpson
Do not cross the oncoming lanes of death that are California's highways. - SJ Mercury News _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, Mar 23, 2015 at 2:41 PM, Cameron Simpson
But it scales very badly; what of a 4 way if etc? It moves from a bit less readable to a nightmare.
in python one particular type or None is a very common idiom -- well worth special casing. Somethign that could take or return 4 different types is a far less usual case, we can be more verbose there. Note that Cython and Pyrex default to "or NOne", and you have to do func( int x not None) if you don't want to allow the None. That's because None as a not-set-yet value is so common. In this case, it's a mistake to make it the default, as Cython does not generate checks later for None, so it can get ugly if you don't specifically test for it in your code. None the less, it's very useful. -Chris -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov
On Mon, Mar 23, 2015 at 3:19 PM, Chris Barker
On Mon, Mar 23, 2015 at 2:41 PM, Cameron Simpson
wrote: But it scales very badly; what of a 4 way if etc? It moves from a bit less readable to a nightmare.
in python one particular type or None is a very common idiom -- well worth special casing. Somethign that could take or return 4 different types is a far less usual case, we can be more verbose there.
Note that Cython and Pyrex default to "or NOne", and you have to do
func( int x not None)
if you don't want to allow the None. That's because None as a not-set-yet value is so common. In this case, it's a mistake to make it the default, as Cython does not generate checks later for None, so it can get ugly if you don't specifically test for it in your code. None the less, it's very useful.
There's a lot of discussion in the typehinting tracker and also in the mypy tracker about what to do with "or None" types. mypy currently doesn't complain about this because it's deemed too noisy (Jukka regularly tries to convert various real-world code bases to type hints, and he learns a lot from these). That said, about the specific example used here, the best way to deal with this currently is to put an annotation comment on the first line, like this: if a < 0: r = None # type: Optional[float] else: r = sqrt(a) There's currently one place in the PEP where Optional[t] is automatically inferred: an argument that's annotated and has None for a default: def foo(x: int = None): ... The actual type of x here is Optional[int]. Note that the PEP doesn't prescribe that the type of a variable becomes the union of the type assigned to in in separate branches of a conditional. This is intentional -- except for a few special cases (like discussed here) giving a variable a different type in different branches (if it is used after the branches join) is more likely a bug than desired. Experimenting with this in mypy is difficult because it explicitly silences complaints about None (see above); if you want to understand what it does in general it's better to experiment with two distinct non-None types, e.g. float and str. Using this approach I found that when you use an if-statement, you should put a #type: comment on the assignment in the first branch to silence it; when you use an if-expression, there's no way to shut it up! (This is exactly the opposite of the assumption that was made earlier in this thread.) PS. About the cast() argument order, I've opened a tracker issue for this: https://github.com/ambv/typehinting/issues/63 -- --Guido van Rossum (python.org/~guido)
On Mon, Mar 23, 2015 at 7:59 PM, Greg
On 24/03/2015 11:35 a.m., Guido van Rossum wrote:
when you use an if-expression, there's no way to shut it up!
Maybe an if-expression should assume the union type, then?
This was flagged as a mypy bug, but the resolution is still up in the air: https://github.com/JukkaL/mypy/issues/622 -- --Guido van Rossum (python.org/~guido)
On 23Mar2015 15:19, Chris Barker - NOAA Federal
On Mon, Mar 23, 2015 at 2:41 PM, Cameron Simpson
wrote: But it scales very badly; what of a 4 way if etc? It moves from a bit less readable to a nightmare.
in python one particular type or None is a very common idiom -- well worth special casing. Somethign that could take or return 4 different types is a far less usual case, we can be more verbose there.
I did not mean 4 different types, I meant a four (or more) way decision which
gave a (probably) single type or None. If "a if b else c" construction rapidly
becomes ery hard to read/verify.
Cheers,
Cameron Simpson
On Sun, Mar 22, 2015 at 11:02 PM, David Foster
* It's a bit vague how local variables should be typed in the presence of multiple assignments. Consider:
``` if x > 0: root = sqrt(x) # type: int else: root = None ```
It seems that the type of `root` should be inferred as `Union[int, None]` (AKA `Optional[int]`), but there's no discussion about this type of inference in the PEP.
Cases like this are the only way that the proposed blank `Undefined` value makes sense to me, using an example like:
``` root = Undefined # type: Optional[int] if x > 0: root = sqrt(x) # type: int else: root = None ```
Right. Another use case for Undefined is class variables (serving as instance variable initialization/declaration). If you feel strongly that this needs to be mentioned in the PEP, can you send a PR to github.com/ambv/typehinting ?
* +1 for only allowing .pyi as the extension for stub files (and not also .py). Makes it very clear that they should only be interpreted as stubs.
Perhaps. Though there are also downsides.
* -1 that `Optional[T1]` is unnecessary. It's conceptually more clean and directly corresponds conceptually to types like `int?` and `?int` from other languages.
Indeed, Optional[t] is much clearer to the human reader than Union[t, None].
* +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int). -- --Guido van Rossum (python.org/~guido)
On Mon, Mar 23, 2015 at 2:55 PM, Guido van Rossum
On Sun, Mar 22, 2015 at 11:02 PM, David Foster
wrote: * It's a bit vague how local variables should be typed in the presence of multiple assignments. Consider:
``` if x > 0: root = sqrt(x) # type: int else: root = None ```
It seems that the type of `root` should be inferred as `Union[int, None]` (AKA `Optional[int]`), but there's no discussion about this type of inference in the PEP.
Cases like this are the only way that the proposed blank `Undefined` value makes sense to me, using an example like:
``` root = Undefined # type: Optional[int] if x > 0: root = sqrt(x) # type: int else: root = None ```
Right. Another use case for Undefined is class variables (serving as instance variable initialization/declaration).
If you feel strongly that this needs to be mentioned in the PEP, can you send a PR to github.com/ambv/typehinting ?
* +1 for only allowing .pyi as the extension for stub files (and not also .py). Makes it very clear that they should only be interpreted as stubs.
Perhaps. Though there are also downsides.
* -1 that `Optional[T1]` is unnecessary. It's conceptually more clean and directly corresponds conceptually to types like `int?` and `?int` from other languages.
Indeed, Optional[t] is much clearer to the human reader than Union[t, None].
* +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
I brought this up somewhere before, but I still think this would be great for casts: cast[T](x) Like C++'s named casts (static_cast<T>(x), dynamic_cast<T>(x), etc.). It gives the notion that T is a type, not some random variable, and that cast is NOT a normal function call.
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Ryan [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/
Yeah, I've considered that, but it feels too punctuation-heavy (as does the
C++ cast<T>(x)). Also, we need cast() to be a fast no-op; cast[T](x)
necessarily means calling two functions rather than one.
On Mon, Mar 23, 2015 at 1:13 PM, Ryan Gonzalez
On Mon, Mar 23, 2015 at 2:55 PM, Guido van Rossum
wrote: On Sun, Mar 22, 2015 at 11:02 PM, David Foster
wrote: * It's a bit vague how local variables should be typed in the presence of multiple assignments. Consider:
``` if x > 0: root = sqrt(x) # type: int else: root = None ```
It seems that the type of `root` should be inferred as `Union[int, None]` (AKA `Optional[int]`), but there's no discussion about this type of inference in the PEP.
Cases like this are the only way that the proposed blank `Undefined` value makes sense to me, using an example like:
``` root = Undefined # type: Optional[int] if x > 0: root = sqrt(x) # type: int else: root = None ```
Right. Another use case for Undefined is class variables (serving as instance variable initialization/declaration).
If you feel strongly that this needs to be mentioned in the PEP, can you send a PR to github.com/ambv/typehinting ?
* +1 for only allowing .pyi as the extension for stub files (and not also .py). Makes it very clear that they should only be interpreted as stubs.
Perhaps. Though there are also downsides.
* -1 that `Optional[T1]` is unnecessary. It's conceptually more clean and directly corresponds conceptually to types like `int?` and `?int` from other languages.
Indeed, Optional[t] is much clearer to the human reader than Union[t, None].
* +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
I brought this up somewhere before, but I still think this would be great for casts:
cast[T](x)
Like C++'s named casts (static_cast<T>(x), dynamic_cast<T>(x), etc.). It gives the notion that T is a type, not some random variable, and that cast is NOT a normal function call.
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Ryan [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/
-- --Guido van Rossum (python.org/~guido)
On 23 March 2015 at 19:55, Guido van Rossum
* +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
However, in SQL, which is the only language I know of with an with an explicitly named cast function (unless you count C++'s static_cast<T>(foo) which isn't normal function syntax), the syntax is CAST(foo AS type). So there's definitely a precedent (arguably stronger, depending on your background) for cast(x, T). Paul.
On Tue, Mar 24, 2015 at 8:05 AM, Paul Moore
On 23 March 2015 at 19:55, Guido van Rossum
wrote: * +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
However, in SQL, which is the only language I know of with an with an explicitly named cast function (unless you count C++'s static_cast<T>(foo) which isn't normal function syntax), the syntax is CAST(foo AS type). So there's definitely a precedent (arguably stronger, depending on your background) for cast(x, T).
I would *hope* that some stronger argument is used than "SQL puts it this way". SQL has so many bizarre oddities of both syntax and semantics that it's seldom a good guideline. In this case, the isinstance() parallel is a better argument than the SQL cast, but it's a small point and I could easily be tipped back to preferring cast(T, x). ChrisA
On 24Mar2015 10:56, Chris Angelico
On Tue, Mar 24, 2015 at 8:05 AM, Paul Moore
wrote: On 23 March 2015 at 19:55, Guido van Rossum
wrote: * +1 for the argument order cast(x, T). This is consistent with (x: T) elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
Well, in C you write (T)x, but I would expect that to be a syntactic requirement. Had it been framed as a function call I would not find cast(x, T) surprising at all.
However, in SQL, which is the only language I know of with an with an explicitly named cast function [...] the syntax is CAST(foo AS type). [...] stronger, depending on your background) for cast(x, T).
I would *hope* that some stronger argument is used than "SQL puts it this way".
Shrug. It is a consistency argument. And language like SQL and (gasp) COBOL strive in a sense for English prose friendliness.
In this case, the isinstance() parallel is a better argument than the SQL cast, but it's a small point and I could easily be tipped back to preferring cast(T, x).
I would not. Leaving aside object method calls (fp.write(blah)), usually the
thing acted upon or through is the first function agrument:
write(fd, data, size)
find(string, substring)
heapsort(mylist)
frob(widget, frobbificationness)
I also like the instance() argument.
For the record, I am +1 for cast(x, T).
Cheers,
Cameron Simpson
On Mar 23, 2015 1:03 PM, "Guido van Rossum"
On Sun, Mar 22, 2015 at 11:02 PM, David Foster
wrote:
* +1 for the argument order cast(x, T). This is consistent with (x: T)
elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
I don't have any strong opinion here, but I don't find the consistency argument convincing. In int(x), 'int' is the verb, and in English verbs come before undergoers ("I am inting the x"). In cast(...), though, cast is the verb, x remains the undergoer, and int is conceptualized as the destination (or something like that), and destinations go in prepositional clauses after the object. You'd say "I'm casting the x to int"; (cf "I'm throwing the ball to Sarah"). "I'm casting to int the x" is extremely weird. -n
On Mon, 23 Mar 2015 14:17:20 -0700
Nathaniel Smith
On Mar 23, 2015 1:03 PM, "Guido van Rossum"
wrote: On Sun, Mar 22, 2015 at 11:02 PM, David Foster
wrote:
* +1 for the argument order cast(x, T). This is consistent with (x: T)
elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
I don't have any strong opinion here, but I don't find the consistency argument convincing. In int(x), 'int' is the verb, and in English verbs come before undergoers ("I am inting the x"). In cast(...), though, cast is the verb, x remains the undergoer, and int is conceptualized as the destination (or something like that), and destinations go in prepositional clauses after the object. You'd say "I'm casting the x to int"; (cf "I'm throwing the ball to Sarah"). "I'm casting to int the x" is extremely weird.
I agree with cast(x, T). It also nicely mirrors isinstance(x, T). Regards Antoine.
For what it's worth, 'cast(x, T)' also reads far more naturally to me.
On Mon, Mar 23, 2015 at 3:08 PM, Antoine Pitrou
On Mon, 23 Mar 2015 14:17:20 -0700 Nathaniel Smith
wrote: On Mar 23, 2015 1:03 PM, "Guido van Rossum"
wrote: On Sun, Mar 22, 2015 at 11:02 PM, David Foster
wrote:
* +1 for the argument order cast(x, T). This is consistent with (x: T)
elsewhere.
I disagree on this. It goes against the argument order of casts in other languages, e.g. C, C++, Java, and even in Python -- you write int(x), not x(int).
I don't have any strong opinion here, but I don't find the consistency argument convincing. In int(x), 'int' is the verb, and in English verbs come before undergoers ("I am inting the x"). In cast(...), though, cast is the verb, x remains the undergoer, and int is conceptualized as the destination (or something like that), and destinations go in prepositional clauses after the object. You'd say "I'm casting the x to int"; (cf "I'm throwing the ball to Sarah"). "I'm casting to int the x" is extremely weird.
I agree with cast(x, T). It also nicely mirrors isinstance(x, T).
Regards
Antoine.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On 24/03/2015 11:08 a.m., Antoine Pitrou wrote:
I agree with cast(x, T). It also nicely mirrors isinstance(x, T).
I think that cast(T, x) is more readable when x is a long expression. It puts the type next to the thing using or being assigned the result, making it easier to visually verify that the types match up. -- Greg
participants (13)
-
Andrew Barnert
-
Antoine Pitrou
-
Cameron Simpson
-
Chris Angelico
-
Chris Barker
-
David Foster
-
David Mertz
-
Greg
-
Guido van Rossum
-
Nathaniel Smith
-
Paul Moore
-
Ryan Gonzalez
-
Terry Reedy