Proposal: Use mypy syntax for function annotations

[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.] Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online ( https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions: (a) Python should adopt mypy's syntax for function annotations (b) Python's use of mutabe containers by default is wrong (c) Python should adopt some kind of Abstract Data Types Proposals (b) and (c) don't feel particularly actionable (if you disagree please start a new thread, I'd be happy to discuss these further if there's interest) but proposal (a) feels right to me. So what is mypy? It is a static type checker for Python written by Jukka for his Ph.D. thesis. The basic idea is that you add type annotations to your program using some custom syntax, and when running your program using the mypy interpreter, type errors will be found during compilation (i.e., before the program starts running). The clever thing here is that the custom syntax is actually valid Python 3, using (mostly) function annotations: your annotated program will still run with the regular Python 3 interpreter. In the latter case there will be no type checking, and no runtime overhead, except to evaluate the function annotations (which are evaluated at function definition time but don't have any effect when the function is called). In fact, it is probably more useful to think of mypy as a heavy-duty linter than as a compiler or interpreter; leave the type checking to mypy, and the execution to Python. It is easy to integrate mypy into a continuous integration setup, for example. To read up on mypy's annotation syntax, please see the mypy-lang.org website. Here's just one complete example, to give a flavor: from typing import List, Dict def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result Note that the #type: comment is part of the mypy syntax; mypy uses comments to declare types in situations where no syntax is available -- although this particular line could also be written as follows: result = Dict[str, int]() Either way the entire function is syntactically valid Python 3, and a suitable implementation of typing.py (containing class definitions for List and Dict, for example) can be written to make the program run correctly. One is provided as part of the mypy project. I should add that many of mypy's syntactic choices aren't actually new. The basis of many of its ideas go back at least a decade: I blogged about this topic in 2004 (http://www.artima.com/weblogs/viewpost.jsp?thread=85551 -- see also the two followup posts linked from the top there). I'll emphasize once more that mypy's type checking happens in a separate pass: no type checking happens at run time (other than what the interpreter already does, like raising TypeError on expressions like 1+"1"). There's a lot to this proposal, but I think it's possible to get a PEP written, accepted and implemented in time for Python 3.5, if people are supportive. I'll go briefly over some of the action items. *(1) A change of direction for function annotations* PEP 3107 <http://legacy.python.org/dev/peps/pep-3107/>, which introduced function annotations, is intentional non-committal about how function annotations should be used. It lists a number of use cases, including but not limited to type checking. It also mentions some rejected proposals that would have standardized either a syntax for indicating types and/or a way for multiple frameworks to attach different annotations to the same function. AFAIK in practice there is little use of function annotations in mainstream code, and I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them. (We may have to have some backwards compatibility provision to avoid breaking code that currently uses annotations for some other purpose. Fortunately the only issue, at least initially, will be that when running mypy to type check such code it will produce complaints about the annotations; it will not affect how such code is executed by the Python interpreter. Nevertheless, it would be good to deprecate such alternative uses of annotations.) *(2) A specification for what to add to Python 3.5* There needs to be at least a rough consensus on the syntax for annotations, and the syntax must cover a large enough set of use cases to be useful. Mypy is still under development, and some of its features are still evolving (e.g. unions were only added a few weeks ago). It would be possible to argue endlessly about details of the notation, e.g. whether to use 'list' or 'List', what either of those means (is a duck-typed list-like type acceptable?) or how to declare and use type variables, and what to do with functions that have no annotations at all (mypy currently skips those completely). I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter. The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module. This module defines several dozen new classes (and a few decorators and other helpers) that can be used in expressing argument types. If you want to type-check your code you have to download and install mypy and run it separately. The curious thing here is that while standardizing a syntax for type annotations, we technically still won't be adopting standard rules for type checking. This is intentional. First of all, fully specifying all the type checking rules would make for a really long and boring PEP (a much better specification would probably be the mypy source code). Second, I think it's fine if the type checking algorithm evolves over time, or if variations emerge. The worst that can happen is that you consider your code correct but mypy disagrees; your code will still run. That said, I don't want to *completely* leave out any specification. I want the contents of the typing.py module to be specified in the PEP, so that it can be used with confidence. But whether mypy will complain about your particular form of duck typing doesn't have to be specified by the PEP. Perhaps as mypy evolves it will take options to tell it how to handle certain edge cases. Forks of mypy (or entirely different implementations of type checking based on the same annotation syntax) are also a possibility. Maybe in the distant future a version of Python will take a different stance, once we have more experience with how this works out in practice, but for Python 3.5 I want to restrict the scope of the upheaval. *Appendix -- Why Add Type Annotations?* The argument between proponents of static typing and dynamic typing has been going on for many decades. Neither side is all wrong or all right. Python has traditionally fallen in the camp of extremely dynamic typing, and this has worked well for most users, but there are definitely some areas where adding type annotations would help. - Editors (IDEs) can benefit from type annotations; they can call out obvious mistakes (like misspelled method names or inapplicable operations) and suggest possible method names. Anyone who has used IntelliJ or Xcode will recognize how powerful these features are, and type annotations will make such features more useful when editing Python source code. - Linters are an important tool for teams developing software. A linter doesn't replace a unittest, but can find certain types of errors better or quicker. The kind of type checking offered by mypy works much like a linter, and has similar benefits; but it can find problems that are beyond the capabilities of most linters. - Type annotations are useful for the human reader as well! Take the above word_count() example. How long would it have taken you to figure out the types of the argument and return value without annotations? Currently most people put the types in their docstrings; developing a standard notation for type annotations will reduce the amount of documentation that needs to be written, and running the type checker might find bugs in the documentation, too. Once a standard type annotation syntax is introduced, it should be simple to add support for this notation to documentation generators like Sphinx. - Refactoring. Bob's talk has a convincing example of how type annotations help in (manually) refactoring code. I also expect that certain automatic refactorings will benefit from type annotations -- imagine a tool like 2to3 (but used for some other transformation) augmented by type annotations, so it will know whether e.g. x.keys() is referring to the keys of a dictionary or not. - Optimizers. I believe this is actually the least important application, certainly initially. Optimizers like PyPy or Pyston <https://github.com/dropbox/pyston> wouldn't be able to fully trust the type annotations, and they are better off using their current strategy of optimizing code based on the types actually observed at run time. But it's certainly feasible to imagine a future optimizer also taking type annotations into account. -- --Guido "I need a new hobby" van Rossum (python.org/~guido)

On 08/13/2014 12:44 PM, Guido van Rossum wrote:
[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.]
+0 on the proposal as a whole. It is not something I'm likely to use, but I'm not opposed to it, so long as it stays optional.
Nevertheless, it would be good to deprecate such alternative uses of annotations.
-1 on deprecating alternative uses of annotations. -- ~Ethan~

On Wed, Aug 13, 2014 at 12:59 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 08/13/2014 12:44 PM, Guido van Rossum wrote:
[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.]
+0 on the proposal as a whole. It is not something I'm likely to use, but I'm not opposed to it, so long as it stays optional.
Nevertheless, it would be good to deprecate such alternative uses of
annotations.
-1 on deprecating alternative uses of annotations.
Do you have a favorite alternative annotation use that you actually use (or are likely to)? -- --Guido van Rossum (python.org/~guido)

On 08/13/2014 01:19 PM, Guido van Rossum wrote:
On Wed, Aug 13, 2014 at 12:59 PM, Ethan Furman wrote:
-1 on deprecating alternative uses of annotations.
Do you have a favorite alternative annotation use that you actually use (or are likely to)?
My script argument parser [1] uses annotations to figure out how to parse the cli parameters and cast them to appropriate values (copied the idea from one of Michele Simionato's projects... plac [2], I believe). I could store the info in some other structure besides 'annotations', but it's there and it fits the bill conceptually. Amusingly, it's a form of type info, but instead of saying what it has to already be, says what it will become. -- ~Ethan~ [1] https://pypi.python.org/pypi/scription (due for an overhaul now I've used it for awhile ;) [2] https://pypi.python.org/pypi/plac/0.9.1

On Wed, Aug 13, 2014 at 1:50 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 08/13/2014 01:19 PM, Guido van Rossum wrote:
On Wed, Aug 13, 2014 at 12:59 PM, Ethan Furman wrote:
-1 on deprecating alternative uses of annotations.
Do you have a favorite alternative annotation use that you actually use (or are likely to)?
My script argument parser [1] uses annotations to figure out how to parse the cli parameters and cast them to appropriate values (copied the idea from one of Michele Simionato's projects... plac [2], I believe).
I could store the info in some other structure besides 'annotations', but it's there and it fits the bill conceptually. Amusingly, it's a form of type info, but instead of saying what it has to already be, says what it will become.
I couldn't find any docs for scription (the tarball contains just the source code, not even an example), although I did find some for plac. I expect using type annotations to the source of scription.py might actually make it easier to grok what it does. :-) But really, I'm sure that in Python 3.5, scription and mypy can coexist. If the mypy idea takes off you might eventually be convinced to use a different convention. But you'd get plenty of warning.
[1] https://pypi.python.org/pypi/scription (due for an overhaul now I've used it for awhile ;) [2] https://pypi.python.org/pypi/plac/0.9.1
-- --Guido van Rossum (python.org/~guido)

2014-08-14, 0:19, Guido van Rossum <guido@python.org> wrote:
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online (https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations
+1. I'm a developer of the code analysis engine of PyCharm. I have discussed this idea with Jukka Lehtosalo and recently with Dave Halter, the author of Jedi code completion library. Standardized type annotations would be very useful for code analysis tools and IDEs such as PyCharm, Jedi and pylint. Type annotations would be especially great for third-party libraries. The idea is that most Python programmers don't have to write annotations in order to benefit from them. Annotated libraries are often enough for good code analysis. We (PyCharm) and Jukka have made some initial steps in this direction, including thoughts on semantics of annotations (https://github.com/pytypes/pytypes). Feedback is welcome. Here are slides from my talk about optional typing in Python, that show how Mypy types can be used in both static and dynamic type checking (http://blog.pirx.ru/media/files/2013/python-optional-typing/), Mypy-related part starts from slide 14. We are interested in getting type annotations standardized and we would like to help developing and testing type annotations proposals. -- Andrey Vlasovskikh Web: http://pirx.ru/

Wow. Awesome. I will make time to study what you have already done! On Wed, Aug 13, 2014 at 2:08 PM, Andrey Vlasovskikh < andrey.vlasovskikh@gmail.com> wrote:
2014-08-14, 0:19, Guido van Rossum <guido@python.org> wrote:
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online ( https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations
+1. I'm a developer of the code analysis engine of PyCharm. I have discussed this idea with Jukka Lehtosalo and recently with Dave Halter, the author of Jedi code completion library. Standardized type annotations would be very useful for code analysis tools and IDEs such as PyCharm, Jedi and pylint. Type annotations would be especially great for third-party libraries. The idea is that most Python programmers don't have to write annotations in order to benefit from them. Annotated libraries are often enough for good code analysis.
We (PyCharm) and Jukka have made some initial steps in this direction, including thoughts on semantics of annotations ( https://github.com/pytypes/pytypes). Feedback is welcome.
Here are slides from my talk about optional typing in Python, that show how Mypy types can be used in both static and dynamic type checking ( http://blog.pirx.ru/media/files/2013/python-optional-typing/), Mypy-related part starts from slide 14.
We are interested in getting type annotations standardized and we would like to help developing and testing type annotations proposals.
-- Andrey Vlasovskikh Web: http://pirx.ru/
-- --Guido van Rossum (python.org/~guido)

Here are slides from my talk about optional typing in Python, that show how Mypy types can be used in both static and dynamic type checking (http://blog.pirx.ru/media/files/2013/python-optional-typing/), I tried this on Windows 7in both Firefox and Internet Explorer and I cannot find any way to advance other than changing the page number on
On 8/13/2014 5:08 PM, Andrey Vlasovskikh wrote: the url bar. -- Terry Jan Reedy

Allow me to chime in. Am 13.08.2014 22:19, schrieb Guido van Rossum:
On Wed, Aug 13, 2014 at 12:59 PM, Ethan Furman <ethan@stoneleaf.us <mailto:ethan@stoneleaf.us>> wrote:
-1 on deprecating alternative uses of annotations.
Do you have a favorite alternative annotation use that you actually use (or are likely to)?
I would be very sad to see annotations being limited to convey type information. But I think I have a solution that will make all happy (see end of mail) I've programmed Java web application for many years now (hoping to finally switch to Python), and "method parameter annotations" as they are called in Java would be one thing I'd really miss, as they can be very useful. Let me give you two examples: 1. Annotations can be used to communicate additional restrictions on values that must be checked on run time Let's assume a simple Web service that is called via HTTP to register a user, and the underlying framework decodes the request and finally calls a simple controller function, it could look like this (Java code, @ signifies an annotation) public Response register(@Range(18,100) int age, @ValidEmail String email) { ... } The framework would check the range of the age parameter and the validity of the email and if there are validation errors, refusing the request with a suitable error message without ever calling our function. Even if we assume that mypy's type system will be incredibly complex and allowing to specify all kinds of restrictions on a type, it won't help because those checks have to be done at run time, and are not optional. Of course those checks could be hard coded into the function, but using annotation also provides a simple and immediate documentation about the allowed values, and avoids boilerplate (I do not have to write "if (emailNotvalie(email) throw ValidationError("Field email is not a valid email")" in every method that uses an email) 2. They can give meta information to a framework An admittedly contrieved example, let's expand our register function: public Response register( int age, @Inject @Scope("session") UserService userService, @Authenticated User user) .... Here I can tell the dependency injection framework that I want an instance of the UserService injected, but one instance that has session scope instead of the normal "singleton" scope. I also ask the framework to inject me the currently authenticated user object (let's assume if I'd write "@Authenticated String user" I could get the login name as string etc.) The flexibility annotations give in Java makes programming much less frustrating. It also took quite some time before annotations were widly used (they were only available starting in Java 5) and people started finding more uses for them. I think this will also be true for Python, it will take time before people find useful ways for them. Redefining them now to be "not much more" than static type information feels wrong to me. My proposed solution: If an annotation is a tuple, mypy will take a look at each item and do it's usual thing. If it doesn't recognise an item, it is skipped. Every framework that uses annotations should also iterate over entries of a tuple to find the ones it is interested in. This also allows more than one annotation at a time, and is completely backwards compatible (as far as Python itself is concerned) for example, my first example could be written as def register(age: (int, range(18,100)), email: (str, ValidEmail)) also, it will allow me to add annotations to existing "typed" functions, def foo(bar: int) -> int could become def foo(bar: (int, MyAnnotation)) -> (int, AnotherOfMyAnnotations) I'm not sure what should happen if two conflicting types are given, like (int, str); I think it should be treated as a union type (either int or str). -- Dennis

On Thu, Aug 14, 2014 at 2:28 PM, Dennis Brakhane <brakhane@googlemail.com> wrote:
public Response register(@Range(18,100) int age, @ValidEmail String email) { ... }
The framework would check the range of the age parameter and the validity of the email and if there are validation errors, refusing the request with a suitable error message without ever calling our function.
Couldn't you do that today in Python with a suitably sophisticated function decorator? The range/type checking would happen before the user's actual function is called. Skip

Am 14.08.2014 21:34, schrieb Skip Montanaro:
Couldn't you do that today in Python with a suitably sophisticated function decorator? The range/type checking would happen before the user's actual function is called.
I suppose so, but I'd have to repeat myself and it would look ugly, because I have to tell the decorator which parameter I'm talking about. Something like @do_range_check('age', 18, 100) @do_email_check('email') def register(age: int, email: str): looks not nearly as nice. Furthermore, my proposal allows multiple uses of annotations, without restricting them to a single use. If you only use mypy, you can keep using annotations as type declarations, when you use some other framework that uses annotations for a different thing, you can still use them, only once you want to use both *and* you have a method that needs both types of annotations are you forced to use the tuple notation.

Couldn't you do that today in Python with a suitably sophisticated function decorator? The range/type checking would happen before the user's actual function is called.
PyContracts does this; with (1) an @contract decorator, (2) Python 3 annotations, (3) with :type: and :rtype: docstrings ... http://www.reddit.com/r/Python/comments/2dh036/pythonideas_proposal_use_mypy... :
+1 for static typing for certain problems. [...] Static type checking at compile time (linting) looks really neat. [...] Do we need a separate approach for actual type assertions at runtime? Will that ever be in scope for mypy?

See responses inline On Aug 14, 2014 3:28 PM, "Dennis Brakhane" <brakhane@googlemail.com> wrote:
1. Annotations can be used to communicate additional restrictions on values that must be checked on run time
Let's assume a simple Web service that is called via HTTP to register a user, and the underlying framework decodes the request and finally calls a simple controller function, it could look like this
(Java code, @ signifies an annotation)
public Response register(@Range(18,100) int age, @ValidEmail String email) { ... }
The framework would check the range of the age parameter and the validity of the email and if there are validation errors, refusing the request with a suitable error message without ever calling our function.
This is exactly the kind of syntax I want to avoid. Python should not attempt to just blindly become like Java or any other language. Though Dennis was just illustrating his point (this was not a suggestion of an alternate syntax), I feel like Python is moving further and further towards code like this. Python programmers should not be forcing as much as possible into each line. Explicit is better than implicit.
Even if we assume that mypy's type system will be incredibly complex and allowing to specify all kinds of restrictions on a type, it won't help because those checks have to be done at run time, and are not optional.
This is a great point. Regardless of the nature of the annotation, we can't let this become a way out for lazy programmers. Having annotations is no excuse for poor error checking.
2. They can give meta information to a framework
An admittedly contrieved example, let's expand our register function:
public Response register( int age, @Inject @Scope("session") UserService userService, @Authenticated User user) ....
This is too much to put on one line in Python. We should be aiming to make code cleaner and promote proper error checking and documentation.
The flexibility annotations give in Java makes programming much less frustrating. It also took quite some time before annotations were widly used (they were only available starting in Java 5) and people started finding more uses for them. I think this will also be true for Python, it will take time before people find useful ways for them. Redefining them now to be "not much more" than static type information feels wrong to me.
I agree that annotations can be useful, but I don't think they should be used for type checking at this scale. Sunjay _______________________________________________
Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

I'm strongly opposed this, for a few reasons. First, I think that standardizing on a syntax, without a semantics is incredibly confusing, and I can't imagine how having *multiple* competing implementations would be a boon for anyone. This proposal seems to be built around the idea that we should have a syntax, and then people can write third party tools, but Python itself won't really do anything with them. Fundamentally, this seems like a very confusing approach. How we write a type, and what we do with that information are fundamentally connected. Can I cast a ``List[str]`` to a ``List[object]`` in any way? If yes, what happens when I go to put an ``int`` in it? There's no runtime checking, so the type system is unsound, on the other hand, disallowing this prevents many types of successes. Both solutions have merit, but the idea of some implementations of the type checker having covariance and some contravariance is fairly disturbing. Another concern I have is that analysis based on these types is making some pretty strong assumptions about static-ness of Python programs that aren't valid. While existing checkers like ``flake8`` also do this, their assumptions are basically constrained to the symbol table, while this is far deeper. For example, can I annotate somethign as ``six.text_type``? What about ``django.db.models.sql.Query`` (keep in mind that this class is redefined based on what database you're using (not actually true, but it used to be))? Python's type system isn't very good. It lacks many features of more powerful systems such as algebraic data types, interfaces, and parametric polymorphism. Despite this, it works pretty well because of Python's dynamic typing. I strongly believe that attempting to enforce the existing type system would be a real shame. Alex PS: You're right. None of this would provide *any* value for PyPy.

On Aug 13, 2014, at 4:29 PM, Alex Gaynor <alex.gaynor@gmail.com> wrote:
I'm strongly opposed this, for a few reasons.
First, I think that standardizing on a syntax, without a semantics is incredibly confusing, and I can't imagine how having *multiple* competing implementations would be a boon for anyone.
This proposal seems to be built around the idea that we should have a syntax, and then people can write third party tools, but Python itself won't really do anything with them.
Fundamentally, this seems like a very confusing approach. How we write a type, and what we do with that information are fundamentally connected. Can I cast a ``List[str]`` to a ``List[object]`` in any way? If yes, what happens when I go to put an ``int`` in it? There's no runtime checking, so the type system is unsound, on the other hand, disallowing this prevents many types of successes.
Both solutions have merit, but the idea of some implementations of the type checker having covariance and some contravariance is fairly disturbing.
Another concern I have is that analysis based on these types is making some pretty strong assumptions about static-ness of Python programs that aren't valid. While existing checkers like ``flake8`` also do this, their assumptions are basically constrained to the symbol table, while this is far deeper. For example, can I annotate somethign as ``six.text_type``? What about ``django.db.models.sql.Query`` (keep in mind that this class is redefined based on what database you're using (not actually true, but it used to be))?
Python's type system isn't very good. It lacks many features of more powerful systems such as algebraic data types, interfaces, and parametric polymorphism. Despite this, it works pretty well because of Python's dynamic typing. I strongly believe that attempting to enforce the existing type system would be a real shame.
Alex
PS: You're right. None of this would provide *any* value for PyPy.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
I agree with Alex that I think leaving the actual semantics of what these things mean up to a third party, which can possibly be swapped out by individual end users, is terribly confusing. I don’t think I agree though that this is a bad idea in general, I think that we should just add it for real and skip the indirection. IOW I'm not sure I see the benefit of defining the syntax but not the semantics when it seems this is already completely possible given the fact that mypy exists. The only real benefits I can see from doing it are that the stdlib can use it, and the ``import typing`` aspect. I don't believe that the stdlib benefits are great enough to get the possible confusion of multiple different implementations and I think that the typing import could easily be provided as a project on PyPI that people can depend on if they want to use this in their code. So my vote would be to add mypy semantics to the language itself. --- Donald Stufft PGP: 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA

On Wed, Aug 13, 2014 at 1:53 PM, Donald Stufft <donald@stufft.io> wrote:
I agree with Alex that I think leaving the actual semantics of what these things mean up to a third party, which can possibly be swapped out by individual end users, is terribly confusing. I don’t think I agree though that this is a bad idea in general, I think that we should just add it for real and skip the indirection.
Yeah, I probably overstated the option of alternative interpretations. I just don't want to have to write a PEP that specifies every little detail of mypy's type checking algorithm, and I don't think anyone would want to have to read such a PEP either. But maybe we can compromise on something that sketches broad strokes and leaves the details up to the team that maintains mypy (after all that tactic has worked pretty well for Python itself :-).
IOW I'm not sure I see the benefit of defining the syntax but not the semantics when it seems this is already completely possible given the fact that mypy exists.
The only real benefits I can see from doing it are that the stdlib can use it, and the ``import typing`` aspect. I don't believe that the stdlib benefits are great enough to get the possible confusion of multiple different implementations and I think that the typing import could easily be provided as a project on PyPI that people can depend on if they want to use this in their code.
So my vote would be to add mypy semantics to the language itself.
What exactly would that mean? I don't think the Python interpreter should reject programs that fail the type check -- in fact, separating the type check from run time is the most crucial point of my proposal. I'm fine to have a discussion on things like covariance vs. contravariance, or what form of duck typing are acceptable, etc. -- --Guido van Rossum (python.org/~guido)

On Aug 13, 2014, at 6:05 PM, Guido van Rossum <guido@python.org> wrote:
On Wed, Aug 13, 2014 at 1:53 PM, Donald Stufft <donald@stufft.io <mailto:donald@stufft.io>> wrote: I agree with Alex that I think leaving the actual semantics of what these things mean up to a third party, which can possibly be swapped out by individual end users, is terribly confusing. I don’t think I agree though that this is a bad idea in general, I think that we should just add it for real and skip the indirection.
Yeah, I probably overstated the option of alternative interpretations. I just don't want to have to write a PEP that specifies every little detail of mypy's type checking algorithm, and I don't think anyone would want to have to read such a PEP either. But maybe we can compromise on something that sketches broad strokes and leaves the details up to the team that maintains mypy (after all that tactic has worked pretty well for Python itself :-).
IOW I'm not sure I see the benefit of defining the syntax but not the semantics when it seems this is already completely possible given the fact that mypy exists.
The only real benefits I can see from doing it are that the stdlib can use it, and the ``import typing`` aspect. I don't believe that the stdlib benefits are great enough to get the possible confusion of multiple different implementations and I think that the typing import could easily be provided as a project on PyPI that people can depend on if they want to use this in their code.
So my vote would be to add mypy semantics to the language itself.
What exactly would that mean? I don't think the Python interpreter should reject programs that fail the type check -- in fact, separating the type check from run time is the most crucial point of my proposal.
I don’t know exactly :) Some ideas: 1) Raise a warning when the type check fails, but allow it happen. This would have the benefit of possibly catching bugs, but it's still opt in in the sense that you have to write the annotations for anything to happen. This would also enable people to turn on enforced type checking by raising the warning level to an exception. Even if this was off by default it would make it easy to enable it during test runs and also enable easier/better quickcheck like functionality. 2) Simply add a flag to the interpreter that turns on type checking. 3) Add a stdlib module that would run the program under type checking, like ``python -m typing myprog`` instead of ``python -m myprog``. Really I think a lot of the benefit is likely to come in the form of linting and during test runs. However if I have to run a seperate Python interpreter to actually do the run then I risk getting bad results through varying things like interpreter differences, language level differences, etc. Although I wouldn't complain if it meant that Python had actual type checking at the run time if a function had type annotations :)
I'm fine to have a discussion on things like covariance vs. contravariance, or what form of duck typing are acceptable, etc.
I’m not particularly knowledgable about the actual workings of a type system and covariance vs contravariance and the like. My main concern there is having a single reality. The meaning of something shouldn't change because I used a different interpreter/linter/whatever. Beyond that I don't know enough to have an opinion on the actual semantics. --- Donald Stufft PGP: 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA

On Wed, Aug 13, 2014 at 3:44 PM, Donald Stufft <donald@stufft.io> wrote:
On Aug 13, 2014, at 6:05 PM, Guido van Rossum <guido@python.org> wrote:
On Wed, Aug 13, 2014 at 1:53 PM, Donald Stufft <donald@stufft.io> wrote:
So my vote would be to add mypy semantics to the language itself.
What exactly would that mean? I don't think the Python interpreter should reject programs that fail the type check -- in fact, separating the type check from run time is the most crucial point of my proposal.
I don’t know exactly :)
Some ideas:
1) Raise a warning when the type check fails, but allow it happen. This would have the benefit of possibly catching bugs, but it's still opt in in the sense that you have to write the annotations for anything to happen. This would also enable people to turn on enforced type checking by raising the warning level to an exception.
I don't think that's going to happen. It would require the entire mypy implementation to be checked into the stdlib. It would also require all sorts of hacks in that implementation to deal with dynamic (or just delayed) imports. Mypy currently doesn't handle any of that -- it must be able to find all imported modules before it starts executing even one line of code.
Even if this was off by default it would make it easy to enable it during test runs and also enable easier/better quickcheck like functionality.
It would *have* to be off by default -- it's way too slow to be on by default (note that some people are already fretting out today about a 25 msec process start-up time).
2) Simply add a flag to the interpreter that turns on type checking.
3) Add a stdlib module that would run the program under type checking, like ``python -m typing myprog`` instead of ``python -m myprog``.
Really I think a lot of the benefit is likely to come in the form of linting and during test runs. However if I have to run a separate Python interpreter to actually do the run then I risk getting bad results through varying things like interpreter differences, language level differences, etc.
Yeah, but I just don't think it's realistic to do anything about that for 3.5 (or 3.6 for that matter). In a decade... Who knows! :-)
Although I wouldn't complain if it meant that Python had actual type checking at the run time if a function had type annotations :)
It's probably possibly to write a decorator that translates annotations into assertions that are invoked when a function is called. But in most cases it would be way too slow to turn on everywhere.
I'm fine to have a discussion on things like covariance vs. contravariance, or what form of duck typing are acceptable, etc.
I’m not particularly knowledgable about the actual workings of a type system and covariance vs contravariance and the like. My main concern there is having a single reality. The meaning of something shouldn't change because I used a different interpreter/linter/whatever. Beyond that I don't know enough to have an opinion on the actual semantics.
Yeah, I regret writing it so vaguely already. Having Alex Gaynor open with "I'm strongly opposed [to] this" is a great joy killer. :-) I just really don't want to have to redundantly write up a specification for all the details of mypy's type checking rules in PEP-worthy English. But I'm fine with discussing whether List[str] is a subclass or a superclass of List[object] and how to tell the difference. Still, different linters exist and I don't hear people complain about that. I would also be okay if PyCharm's interpretation of the finer points of the type checking syntax was subtly different from mypy's. In fact I would be surprised if they weren't sometimes in disagreement. Heck, PyPy doesn't give *every* Python program the same meaning as CPython, and that's a feature. :-) -- --Guido van Rossum (python.org/~guido)

On Aug 13, 2014, at 7:43 PM, Guido van Rossum <guido@python.org> wrote:
On Wed, Aug 13, 2014 at 3:44 PM, Donald Stufft <donald@stufft.io <mailto:donald@stufft.io>> wrote:
On Aug 13, 2014, at 6:05 PM, Guido van Rossum <guido@python.org <mailto:guido@python.org>> wrote:
On Wed, Aug 13, 2014 at 1:53 PM, Donald Stufft <donald@stufft.io <mailto:donald@stufft.io>> wrote:
So my vote would be to add mypy semantics to the language itself.
What exactly would that mean? I don't think the Python interpreter should reject programs that fail the type check -- in fact, separating the type check from run time is the most crucial point of my proposal.
I don’t know exactly :)
Some ideas:
1) Raise a warning when the type check fails, but allow it happen. This would have the benefit of possibly catching bugs, but it's still opt in in the sense that you have to write the annotations for anything to happen. This would also enable people to turn on enforced type checking by raising the warning level to an exception.
I don't think that's going to happen. It would require the entire mypy implementation to be checked into the stdlib. It would also require all sorts of hacks in that implementation to deal with dynamic (or just delayed) imports. Mypy currently doesn't handle any of that -- it must be able to find all imported modules before it starts executing even one line of code.
Even if this was off by default it would make it easy to enable it during test runs and also enable easier/better quickcheck like functionality.
It would *have* to be off by default -- it's way too slow to be on by default (note that some people are already fretting out today about a 25 msec process start-up time).
2) Simply add a flag to the interpreter that turns on type checking.
3) Add a stdlib module that would run the program under type checking, like ``python -m typing myprog`` instead of ``python -m myprog``.
Really I think a lot of the benefit is likely to come in the form of linting and during test runs. However if I have to run a separate Python interpreter to actually do the run then I risk getting bad results through varying things like interpreter differences, language level differences, etc.
Yeah, but I just don't think it's realistic to do anything about that for 3.5 (or 3.6 for that matter). In a decade... Who knows! :-)
Although I wouldn't complain if it meant that Python had actual type checking at the run time if a function had type annotations :)
It's probably possibly to write a decorator that translates annotations into assertions that are invoked when a function is called. But in most cases it would be way too slow to turn on everywhere.
I'm fine to have a discussion on things like covariance vs. contravariance, or what form of duck typing are acceptable, etc.
I’m not particularly knowledgable about the actual workings of a type system and covariance vs contravariance and the like. My main concern there is having a single reality. The meaning of something shouldn't change because I used a different interpreter/linter/whatever. Beyond that I don't know enough to have an opinion on the actual semantics.
Yeah, I regret writing it so vaguely already. Having Alex Gaynor open with "I'm strongly opposed [to] this" is a great joy killer. :-)
I just really don't want to have to redundantly write up a specification for all the details of mypy's type checking rules in PEP-worthy English. But I'm fine with discussing whether List[str] is a subclass or a superclass of List[object] and how to tell the difference.
Understood! And really the most important thing I'm worried about isn’t that there is some sort of code in the stdlib or in the interpreter just that there is an authoritative source of what stuff means.
Still, different linters exist and I don't hear people complain about that. I would also be okay if PyCharm's interpretation of the finer points of the type checking syntax was subtly different from mypy's. In fact I would be surprised if they weren't sometimes in disagreement. Heck, PyPy doesn't give *every* Python program the same meaning as CPython, and that's a feature. :-)
Depends on what is meant by "meaning" I suppose. Generally in those linters or PyPy itself if there is a different *meaningful* result (for instance if print was defaulting to sys.stderr) then CPython (incl docs) acts as the authoritative source of what ``print()`` means (in this case writing to sys.stdout). I'm also generally OK with deferring possible code/interpreter changes to add actual type checking until a later point in time. If there's a defined semantics to what those annotations mean than third parties can experiment and do things with it and those different things can be looked at adding/incorporating into Python proper in 3.6 (or 3.7, or whatever). Honestly I think that probably the things I was worried about is sufficiently allayed given that it appears I was reading more into the vaguness and the optionally different interpretations than what was meant and I don't want to keep harping on it :) As long as there's some single source of what List[str] or what have you means than I'm pretty OK with it all. --- Donald Stufft PGP: 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA

On Wed, Aug 13, 2014 at 1:29 PM, Alex Gaynor <alex.gaynor@gmail.com> wrote:
I'm strongly opposed this, for a few reasons.
First, I think that standardizing on a syntax, without a semantics is incredibly confusing, and I can't imagine how having *multiple* competing implementations would be a boon for anyone.
That part was probably overly vague in my original message. I actually do want to standardize on semantics, but I think the semantics will prove controversial (they already have :-) and I think it's better to standardize the syntax and *some* semantics first rather than having to wait another decade for the debate over the semantics to settle. I mostly want to leave the door open for mypy to become smarter. But it might make sense to have a "weaker" interpretation in some cases too (e.g. an IDE might use a weaker type system in order to avoid overwhelming users with warnings).
This proposal seems to be built around the idea that we should have a syntax, and then people can write third party tools, but Python itself won't really do anything with them.
Right.
Fundamentally, this seems like a very confusing approach. How we write a type, and what we do with that information are fundamentally connected. Can I cast a ``List[str]`` to a ``List[object]`` in any way? If yes, what happens when I go to put an ``int`` in it? There's no runtime checking, so the type system is unsound, on the other hand, disallowing this prevents many types of successes.
Mypy has a cast() operator that you can use to shut it up when you (think you) know the conversion is safe.
Both solutions have merit, but the idea of some implementations of the type checker having covariance and some contravariance is fairly disturbing.
Yeah, that wouldn't be good. ;-)
Another concern I have is that analysis based on these types is making some pretty strong assumptions about static-ness of Python programs that aren't valid. While existing checkers like ``flake8`` also do this, their assumptions are basically constrained to the symbol table, while this is far deeper. For example, can I annotate something as ``six.text_type``? What about ``django.db.models.sql.Query`` (keep in mind that this class is redefined based on what database you're using (not actually true, but it used to be))?
Time will have to tell. Stubs can help. I encourage you to try annotating a medium-sized module. It's likely that you'll find a few things: maybe a bug in mypy, maybe a missing mypy feature, maybe a bug in your code, maybe a shady coding practice in your code or a poorly documented function (I know I found several of each during my own experiments so far).
Python's type system isn't very good. It lacks many features of more powerful systems such as algebraic data types, interfaces, and parametric polymorphism. Despite this, it works pretty well because of Python's dynamic typing. I strongly believe that attempting to enforce the existing type system would be a real shame.
Mypy shines in those areas of Python programs that are mostly statically typed. There are many such areas in most large systems. There are usually also some areas where mypy's type system is inadequate. It's easy to shut it up for those cases (in fact, mypy is silent unless you use at least one annotation for a function). But that's the case with most type systems. Even Haskell sometimes calls out to C. -- --Guido van Rossum (python.org/~guido)

Am 13.08.2014 23:46, schrieb Guido van Rossum:
Mypy has a cast() operator that you can use to shut it up when you (think you) know the conversion is safe.
Does Mypy provide a way to "fix/monkeypatch" incorrect type declarations in function signatures? For example, by modifying __annotations__? My pet peeve of static languages is that programmers are often too fixated on their particular problem that they don't think about alternate uses for their code and make the type declarations uncessarily complex. For example, in Java nearly every method in Java that deals with character strings uses "String" as parameter type, while they should have used "CharSequence". Having to read an entire input stream and storing it in a String just to be able to use a method is not fun (String is final in Java) I'm worried that in Python we will have utility functions that declare they require a List[int], when in fact they actually only require a Sequence[int] or Sequence[Number]. While Mypy's cast is nice in that I won't have to wrap my Integer Tuple in list like object, having to cast it every time I use a particular broken utility method feels very ugly to me; and defining a wrapper function with the correct type information feels like unnecessary run time overhead for no gain. Don't get me wrong, I'm not entirely against some kind of type checking, but I fear that there must exist possible workarounds for badly written code.

+1 This is definitely something to consider. One of the many benefits of Python is that you can use objects with equivalent interfaces in functions that may not have expected that type while they were being written. One thing to note: if we try to make this syntax too verbose, it may lose all of its purpose all together. For example: one (bad) solution to support what I described above is to define some sort of convoluted syntax for specifying the exact interface a function supports. At this point, any addition to the language would do nothing but hinder it. Anything that verbose loses is too complex to be valuable. We have to be careful with this. If we do accept it (or any of the many alternatives suggested so far), then we should choose a few use cases and focus on solving them as best as possible. On Aug 14, 2014 4:06 PM, "Dennis Brakhane" <brakhane@googlemail.com> wrote:
Am 13.08.2014 23:46, schrieb Guido van Rossum:
Mypy has a cast() operator that you can use to shut it up when you (think you) know the conversion is safe.
Does Mypy provide a way to "fix/monkeypatch" incorrect type declarations in function signatures? For example, by modifying __annotations__?
My pet peeve of static languages is that programmers are often too fixated on their particular problem that they don't think about alternate uses for their code and make the type declarations uncessarily complex.
For example, in Java nearly every method in Java that deals with character strings uses "String" as parameter type, while they should have used "CharSequence". Having to read an entire input stream and storing it in a String just to be able to use a method is not fun (String is final in Java)
I'm worried that in Python we will have utility functions that declare they require a List[int], when in fact they actually only require a Sequence[int] or Sequence[Number].
While Mypy's cast is nice in that I won't have to wrap my Integer Tuple in list like object, having to cast it every time I use a particular broken utility method feels very ugly to me; and defining a wrapper function with the correct type information feels like unnecessary run time overhead for no gain.
Don't get me wrong, I'm not entirely against some kind of type checking, but I fear that there must exist possible workarounds for badly written code.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

Am 14.08.2014 22:16, schrieb Sunjay Varma:
One of the many benefits of Python is that you can use objects with equivalent interfaces in functions that may not have expected that type while they were being written. Exactly. As others have already noted, Guido's example is actually restricting code from calling it with a file object for no particular reason.
We have to be careful with this. If we do accept it (or any of the many alternatives suggested so far), then we should choose a few use cases and focus on solving them as best as possible. Yes, my fear is that we will either end up with a broken type system
Another thing is that it restricts the entries of the iterables to be str, while it actually only requires them to provide a split method. For example, and I'm aware that this is a contrieved case, let's assume word_count would actually be somehow extremely optimized code that I don't want to rewrite. I now want to count all prime factors in a list of integers. Without annotations I *could* do something like this ("Language for consenting adults"): class IntWrapper(int): def split(self): return primefactors(self) word_count(IntWrapper(i) for i in my_integer_list) I don't want to argue whether that's good code or not (it probably isn't), it would be possible to write such code and it will work correctly. For mypy to accept such code, the type declaration of word_count would have to be something like def word_count(input: Iterable[has("split() -> T")]) -> Dict[T, int] Which would require a very complex type system. If I understand MyPy correctly, the above would be possible, but it would have to look something like cast(Dict[int, int], word_count(cast(List[str], (IntWrapper(i) for i in my_integer_list))) One could argue that this ugly piece of code is the rightful punishment for abusing duck typing, but I'm not convinced you should be forced to make the code unreadable (and therefore refactor your code and reimplement word_count yourself). like Java (which provides more problems than it solves) or end up with a hugely complex type system and type hierachy like Scala (see http://www.scala-lang.org/api/2.11.2/#scala.collection.MapLike for a quick example what you end up with)

If we're looking for precedents in other languages, the Dialyzer project for Erlang is worth a look. It does completely optional static type checking without affecting the runtime at all. It was pretty widely adopted and it works well in my experience. I think it's a bit different from what's being proposed here (it does type inference as well, for instance), but it is a more or less successful example of adding optional static checks to a dynamically typed language. Based on my experiences with Dialyzer and the way I've used function annotations in Python to date, I'm really excited about this proposal. On Thu, Aug 14, 2014 at 3:11 PM, Dennis Brakhane <brakhane@googlemail.com> wrote:
Am 14.08.2014 22:16, schrieb Sunjay Varma:
One of the many benefits of Python is that you can use objects with equivalent interfaces in functions that may not have expected that type while they were being written. Exactly. As others have already noted, Guido's example is actually restricting code from calling it with a file object for no particular reason.
Another thing is that it restricts the entries of the iterables to be str, while it actually only requires them to provide a split method.
For example, and I'm aware that this is a contrieved case, let's assume word_count would actually be somehow extremely optimized code that I don't want to rewrite.
I now want to count all prime factors in a list of integers.
Without annotations I *could* do something like this ("Language for consenting adults"):
class IntWrapper(int): def split(self): return primefactors(self)
word_count(IntWrapper(i) for i in my_integer_list)
I don't want to argue whether that's good code or not (it probably isn't), it would be possible to write such code and it will work correctly.
For mypy to accept such code, the type declaration of word_count would have to be something like
def word_count(input: Iterable[has("split() -> T")]) -> Dict[T, int]
Which would require a very complex type system.
If I understand MyPy correctly, the above would be possible, but it would have to look something like
cast(Dict[int, int], word_count(cast(List[str], (IntWrapper(i) for i in my_integer_list)))
One could argue that this ugly piece of code is the rightful punishment for abusing duck typing, but I'm not convinced you should be forced to make the code unreadable (and therefore refactor your code and reimplement word_count yourself).
We have to be careful with this. If we do accept it (or any of the many alternatives suggested so far), then we should choose a few use cases and focus on solving them as best as possible. Yes, my fear is that we will either end up with a broken type system like Java (which provides more problems than it solves) or end up with a hugely complex type system and type hierachy like Scala (see http://www.scala-lang.org/api/2.11.2/#scala.collection.MapLike for a quick example what you end up with)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

Am 15.08.2014 00:35, schrieb Alex Hammel:
I think it's a bit different from what's being proposed here (it does type inference as well, for instance)
FWIW, David Halter is currently working on extending Jedi to include a linter that does type inference. For example, the current development version can do the following $ cat foo.py def foo(x): return x + "foo" foo(1) $ python -m jedi linter foo.py foo.py:2:13: E11 TypeError: unsupported operand type(s) for +: <CompiledObject: 1> and <CompiledObject: 'foo'>

On Fri, Aug 15, 2014 at 6:05 AM, Dennis Brakhane <brakhane@googlemail.com> wrote:
Does Mypy provide a way to "fix/monkeypatch" incorrect type declarations in function signatures? For example, by modifying __annotations__?
AIUI there's a separation of execution and type checking here - you *either* run under mypy to check types, *or* run under Python to execute the code. If that's the case, it should be possible to create a magic module that you import which overwrites a few functions with stubs. # spam.py (which you can't change) def func(x: int) -> int: return x * 3 + 5 # type_fixes.py in the regular PYTHONPATH """This is a stub; the mypy equivalent fixes some type annotations used in third-party libraries.""" # type_fixes.py in the mypy PYTHONPATH import spam def func(x: Number) -> Number: pass spam.func = func # in main_module.py import type_fixes import spam x = func(42.5) Monkey-patching entire functions shouldn't be a problem if they don't need bodies. ChrisA

On Thu Aug 14 2014 at 1:06:20 PM Dennis Brakhane <brakhane@googlemail.com> wrote:
Am 13.08.2014 23:46, schrieb Guido van Rossum:
Mypy has a cast() operator that you can use to shut it up when you (think you) know the conversion is safe.
Does Mypy provide a way to "fix/monkeypatch" incorrect type declarations in function signatures? For example, by modifying __annotations__?
My pet peeve of static languages is that programmers are often too fixated on their particular problem that they don't think about alternate uses for their code and make the type declarations uncessarily complex.
For example, in Java nearly every method in Java that deals with character strings uses "String" as parameter type, while they should have used "CharSequence". Having to read an entire input stream and storing it in a String just to be able to use a method is not fun (String is final in Java)
I'm worried that in Python we will have utility functions that declare they require a List[int], when in fact they actually only require a Sequence[int] or Sequence[Number].
This always happens, even in docstrings where people state types today. For example, I often correct people during code reviews to say sequence of instead of list of in docstrings. Nothing can really prevent that beyond documentation about annotations having good examples and promoting the use of concept or interface annotations over specific type annotations. But what's being proposed here isn't a strict static type check, it's an annotation. Sure, if someone misannotates their code and runs it through a "compile time" checker (aka superlint) such as mypy or something even more advanced, it will highlight problems where there may in fact be none. But the problem being highlighted there is that the annotation is wrong. Any system doing that level of code analysis will need a way to deal with loading corrected annotations for code that for some reason cannot be changed directly in the annotated files themselves (standard library, third party library from pypi, etc). But this is really no different than loading annotations for extension modules and the stdlib builtins. Those are already likely to be separate files declaring interface annotations only (there is a pile of these that we've generated via code analysis for builtins and parts of the stdlib in the pytypedecl repo, expect more of that to come from anyone working on this kind of thing). While Mypy's cast is nice in that I won't have to wrap my Integer Tuple
in list like object, having to cast it every time I use a particular broken utility method feels very ugly to me; and defining a wrapper function with the correct type information feels like unnecessary run time overhead for no gain.
Don't get me wrong, I'm not entirely against some kind of type checking, but I fear that there must exist possible workarounds for badly written code.
always. but this is more a task for all checker implementations. not something the syntax for annotations itself can prevent. -gps

On Thu, Aug 14, 2014 at 9:18 PM, Gregory P. Smith <greg@krypto.org> wrote:
(there is a pile of these that we've generated via code analysis for builtins and parts of the stdlib in the pytypedecl repo, expect more of that to come from anyone working on this kind of thing).
This is exciting news! It means that we should be able to convert pytypedecl stubs to mypy stubs and mypy will get a lot of stdlib stubs for free. I filed https://github.com/JukkaL/mypy/issues/382 for this. Hopefully you are releasing Python 3 stubs along these lines too? -- --Guido van Rossum (python.org/~guido)

On Thursday, August 14, 2014 9:20 PM, Gregory P. Smith <greg@krypto.org> wrote:
On Thu Aug 14 2014 at 1:06:20 PM Dennis Brakhane <brakhane@googlemail.com> wrote:
My pet peeve of static languages is that programmers are often too fixated on their particular problem that they don't think about alternate uses for their code and make the type declarations uncessarily complex.
For example, in Java nearly every method in Java that deals with character strings uses "String" as parameter type, while they should have used "CharSequence". Having to read an entire input stream and storing it in a String just to be able to use a method is not fun (String is final in Java)
I'm worried that in Python we will have utility functions that declare they require a List[int], when in fact they actually only require a Sequence[int] or Sequence[Number].
This always happens, even in docstrings where people state types today. For example, I often correct people during code reviews to say sequence of instead of list of in docstrings. Nothing can really prevent that
We can't _prevent_ it, but we certainly can _drastically reduce_ it, just by not providing List[int]. Again, I think 90% or more of the uses of List[int] in type annotations will be incorrect. Why go out of our way to make things more complicated just so people can make more mistakes? If anyone really does need a list of ints, implementing List should be pretty simple (in fact, it might even just be `class List(MutableSequence, list): pass`). Why bend over backwards to make it easier for people to make mistakes?

On Wednesday, August 13, 2014 1:30 PM, Alex Gaynor <alex.gaynor@gmail.com> wrote:
I'm strongly opposed this, for a few reasons.
[...]
Python's type system isn't very good. It lacks many features of more powerful systems such as algebraic data types, interfaces, and parametric polymorphism. Despite this, it works pretty well because of Python's dynamic typing. I strongly believe that attempting to enforce the existing type system would be a real shame.
This is my main concern, but I'd phrase it very differently. First, Python's type system _is_ powerful, but only because it's dynamic. Duck typing simulates parametric polymorphism perfectly, disjunction types as long as they don't include themselves recursively, algebraic data types in some but not all cases, etc. Simple (Java-style) generics, of the kind that Guido seems to be proposing, are not nearly as flexible. That's the problem. On the other hand, even though these types only cover a small portion of the space of Python's implicit type system, a lot of useful functions fall within that small portion. As long as you can just leave the rest of the program untyped, and there are no boundary problems, there's no real risk. On the third hand, what worries me is this:
Mypy has a cast() operator that you can use to shut it up when you (think you) know the conversion is safe.
Why do we need casts? You shouldn't be trying to enforce static typing in a part of the program whose static type isn't sound. Languages like Java and C++ have no choice; Python does, so why not take advantage of it? The standard JSON example seems appropriate here. What's the return type of json.loads? In Haskell, you write a pretty trivial JSONThing ADT, and you return a JSONThing that's an Object (which means its value maps String to JSONThing). In Python today, you return a dict, and use it exactly the same as in Haskell, except that you can't verify its soundness at compile time. In Java or C++, it's… what? The sound option is a special JSONThing that has separate getObjectMemberString and getArrayMemberString and getObjectMemberInt, which is incredibly painful to use. A plain old Dict[String, Object] looks simple, but it means you have to downcast all over the place to do anything, making it completely unsound, and still unpleasant. The official Java json.org library gives you a hybrid between the two that manages to be neither sound nor user-friendly. And of course there are libraries for many poor static languages (especially C++) that try to fake duck typing as far as possible for their JSON objects, which is of course nowhere near as far as Python gets for free.

On 08/14/2014 01:26 PM, Andrew Barnert wrote:
In Java or C++, it's… what? The sound option is a special JSONThing that has separate getObjectMemberString and getArrayMemberString and getObjectMemberInt, which is incredibly painful to use.
That's mainly because Java doesn't let you define your own types that use convenient syntax such as [] for indexing. Python doesn't have that problem, so a decent static type system for Python should let you define a JSONThing class that's fully type-safe while having a standard mapping interface. -- Greg

On Aug 13, 2014, at 18:44, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 08/14/2014 01:26 PM, Andrew Barnert wrote:
In Java or C++, it's… what? The sound option is a special JSONThing that has separate getObjectMemberString and getArrayMemberString and getObjectMemberInt, which is incredibly painful to use.
That's mainly because Java doesn't let you define your own types that use convenient syntax such as [] for indexing.
No it's not, or other languages like C++ (which has operator methods and overloading) wouldn't have the exact same problem, but they do. Look at JsonCpp, for example: https://github.com/open-source-parsers/jsoncpp/
Python doesn't have that problem, so a decent static type system for Python should let you define a JSONThing class that's fully type-safe while having a standard mapping interface.
How? If you go with a single JSONThing type that represents an object, array, number, bool, string, or null, then it can't have a standard mapping interface, because it also needs to have a standard sequence interface, and they conflict. Likewise for number vs. string. The only fully type-safe interface it can have is as_string, as_number, etc. methods (which of course can only check at runtime, so it's no better than using isinstance from Python, and you're forced to do it for every single access.) What if you go the other way and have separate JSONObject, JSONArray, etc. types? Then all of those problems go away; you can define an unambiguous __getitem__. But what is its return value? The only possibility is a union of all the various types mentioned above, and such a union type has no interface at all. It's only useful if people subvert the type safety by casting. (I guess you could argue that returning a union type makes your JSON library type safe, it's only every program that ever uses it for anything that's unsafe. But where does that get you?) The only usable type safe interface is separate get_string, get_number, etc. methods in place of __getitem__. Or you can merge the two together and have a single JSONThing that has both as methods and, for convenience, combined as_object+get, or even as_object+get+as_str. Also, look at the mutation interfaces for these libraries. They're only marginally tolerable because all variable have obligatory types that you can overload on, which wouldn't be the case in Python. The alternative is, of course, to come up with a way to avoid type safety. In Swift, you parse a JSON object into a Cocoa NSDictionary, which is a dynamically-typed heterogeneous collection just like a Python dict. There are C++ libraries with a bunch of types that effectively act like Python dict, list, float, str, etc. and try to magically cast when they come into contact with native types. That's the best solution anyone has to dealing with even a dead-simple algebraic data type like JSON in a static language whose type system isn't powerful enough: to try to fake being a duck typed language.

Andrew Barnert wrote:
If you go with a single JSONThing type that represents an object, array, number, bool, string, or null, then it can't have a standard mapping interface, because it also needs to have a standard sequence interface,
I didn't mean that. The most Pythonic way would probably be to have something like JSONThing = Union(JSONDict, JSONList, Int, Float, Str, Bool) JSONDict = Mapping[str, JSONThing] JSONList = Sequence[JSONThing] If you're concerned about type safety, at some point you need to introspect on what you've got to figure out what to do with it. This is inevitable, since a JSON object is inherently a dynamically-typed thing. This is true even in Haskell, you just don't notice it so much because you do it with pattern matching.
The only possibility is a union of all the various types mentioned above, and such a union type has no interface at all. It's only useful if people subvert the type safety by casting.
I don't know what mypy does with union types, but if I were designing a type system like this I would say that, e.g. if i is know to be of type Int and d of type JSONDict, then i = d['foo'] should be allowed, on the grounds that the return value *could* be an Int, with a run-time type check to make sure that it actually is. An implicit cast, in other words. As long as mypy is just a linter it's obviously not in a position to insert the runtime check, but it could be argued that it should allow the assignment anyway, since Python will end up raising a TypeError at some point if it's wrong. I've forgotten what the original point of all this was. If the point was that there's no benefit in trying to make JSON type-safe in Python, and you should just leave it all dynamically typed, maybe you're right. -- Greg

On Aug 13, 2014, at 23:48, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
I've forgotten what the original point of all this was. If the point was that there's no benefit in trying to make JSON type-safe in Python, and you should just leave it all dynamically typed, maybe you're right.
Well, that was my starting point, which I assumed people would take for granted, so I could use it to make my real point, which is that "just downcast it", which came up a few times in the first couple hours of this discussion, is a bad answer in general. If you start from the assumption that everything can and should be statically typed, but don't design a type system powerful enough to do that, you end up putting downcasts from void* or Object or whatever all over the place, and it's impossible to tell what parts of the program are actually safe, which defeats the entire purpose of static typing. If you start from the assumption that there will be some self-contained regions of your code that can be typed by your type system, and other parts just have to be dynamically typed and that's OK, you don't subvert the type system all over the place, and you know which parts of the code are or aren't known to be safe. Anyway, I think this is basically what you just said--you started explaining how we could effectively add C++ style implicit casts to hide the ugliness and unsafety of dealing with JSON, but then said that maybe it would be better to just leave the values dynamically typed. As long as the type checker can (not necessarily in the initial version, but at least reasonably plausibly) make it easy to tell which areas of your code have been "infected" by dynamic types, I think defaulting to "too hard, leave it dynamic" is the simplest, and probably right, thing to do. (Unless we actually want to design a sufficiently powerful type system, which I don't think we do.) There's a great paper on how this works in practice in an ML variant, but unfortunately googling any subset of the only keywords I can remember (ML type duck infect dynamic) just brings up papers about avian virology (even if I leave our "duck"). I think there's also a (much more recent and less technical) blog post by one of the Swift guys about a similar idea--making it easy to stay dynamic means you end up with cleanly walled off regions of safe and unsafe code. (Or you would, if Swift had a halfway-decent stdlib instead of forcing you to bridge to ObjC for almost anything, but that's not important here.)

On 13.08.2014 21:44, Guido van Rossum wrote:
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online (https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations (b) Python's use of mutabe containers by default is wrong (c) Python should adopt some kind of Abstract Data Types
I was at Bob's talk during EP14 and really liked the idea. A couple of colleagues and other attendees also said it's a good and useful proposal. I also like your proposal to standardize the type annotations first without a full integration of mypy. In general I'm +1 but I like to discuss two aspects: 1) I'm not keen with the naming of mypy's typing classes. The visual distinction between e.g. dict() and Dict() is too small and IMHO confusing for newcomers. How about an additional 'T' prefix to make clear that the objects are referring to typing objects? from typing import TList, TDict def word_count(input: TList[str]) -> TDict[str, int]: ... 2) PEP 3107 only specifies arguments and return values but not exceptions that can be raised by a function. Java has the "throws" syntax to list possible exceptions: public void readFile() throws IOException {} May I suggest that we also standardize a way to annotate the exceptions that can be raised by a function? It's a very useful piece of information and commonly requested information on the Python user mailing list. It doesn't have to be a new syntax element, a decorator in the typing module would suffice, too. For example: from typing import TList, TDict, raises @raises(RuntimeError, (ValueError, "is raised when input is empty")) def word_count(input: TList[str]) -> TDict[str, int]: ... Regards, Christian

On Wed, Aug 13, 2014 at 3:29 PM, Christian Heimes <christian@python.org> wrote:
On 13.08.2014 21:44, Guido van Rossum wrote:
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online (https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations (b) Python's use of mutabe containers by default is wrong (c) Python should adopt some kind of Abstract Data Types
I was at Bob's talk during EP14 and really liked the idea. A couple of colleagues and other attendees also said it's a good and useful proposal. I also like your proposal to standardize the type annotations first without a full integration of mypy.
In general I'm +1 but I like to discuss two aspects:
1) I'm not keen with the naming of mypy's typing classes. The visual distinction between e.g. dict() and Dict() is too small and IMHO confusing for newcomers. How about an additional 'T' prefix to make clear that the objects are referring to typing objects?
from typing import TList, TDict
def word_count(input: TList[str]) -> TDict[str, int]: ...
Eeewwwww. That's way too Pascal-ish.
2) PEP 3107 only specifies arguments and return values but not exceptions that can be raised by a function. Java has the "throws" syntax to list possible exceptions:
public void readFile() throws IOException {}
May I suggest that we also standardize a way to annotate the exceptions that can be raised by a function? It's a very useful piece of information and commonly requested information on the Python user mailing list. It doesn't have to be a new syntax element, a decorator in the typing module would suffice, too. For example:
from typing import TList, TDict, raises
@raises(RuntimeError, (ValueError, "is raised when input is empty")) def word_count(input: TList[str]) -> TDict[str, int]: ...
That was a disaster in C++. It's confusing, especially since Python uses exceptions more than most other languages do.
Regards, Christian
_______________________________________________ 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 If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."

Christian Heimes <christian@python.org> writes:
1) I'm not keen with the naming of mypy's typing classes. The visual distinction between e.g. dict() and Dict() is too small and IMHO confusing for newcomers. How about an additional 'T' prefix to make clear that the objects are referring to typing objects?
To this reader, ‘dict’ and ‘list’ *are* “typing objects” — they are objects that are types. Seeing code that referred to something else as “typing objects” would be an infitation to confusion, IMO. You could argue “that's because you don't know the special meaning of “typing object” being discussed here”. To which my response would be, for a proposal to add something else as meaningful Python syntax, the jargon is poorly chosen and needlessly confusing with established terms in Python. If there's going to be a distinction between the types (‘dict’, ‘list’, etc.) and something else, I'd prefer it to be based on a clearer terminology distinction. -- \ “Simplicity and elegance are unpopular because they require | `\ hard work and discipline to achieve and education to be | _o__) appreciated.” —Edsger W. Dijkstra | Ben Finney

On Wed, Aug 13, 2014 at 10:29:48PM +0200, Christian Heimes wrote:
1) I'm not keen with the naming of mypy's typing classes. The visual distinction between e.g. dict() and Dict() is too small and IMHO confusing for newcomers. How about an additional 'T' prefix to make clear that the objects are referring to typing objects?
from typing import TList, TDict
def word_count(input: TList[str]) -> TDict[str, int]: ...
Would it be possible, and desirable, to modify the built-in types so that we could re-use them in the type annotations? def word_count(input: list[str]) -> dict[str, int]: Since types are otherwise unlikely to be indexable like that, I think that might work.
2) PEP 3107 only specifies arguments and return values but not exceptions that can be raised by a function. Java has the "throws" syntax to list possible exceptions:
public void readFile() throws IOException {}
I understand that this is called a "checked exception" in Java. I also understand that they are hated and derided as useless or even counter-productive: http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/
May I suggest that we also standardize a way to annotate the exceptions that can be raised by a function? It's a very useful piece of information and commonly requested information on the Python user mailing list.
And as frequently explained on the python-list, it's almost impossible to answer. Or rather, the answer is usually no more specific than "raises Exception". There are very few guarantees you can reliably make about what exceptions *cannot* be raised by a function. To put it simply, given almost any operation in your function, say, x+1, there's no limit on what x.__add__ might raise. Even if you know x is a subclass of int, it could do anything in its __add__ method. Only if you know x is *exactly* a builtin int can you be confident that it won't raise (say) ImportError. Perhaps with a few years of experience, we might be able to extend this to exceptions without making the same mistakes as Java's checked exceptions, but I wouldn't rush into it. -- Steven

On Aug 14, 2014, at 12:02 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Would it be possible, and desirable, to modify the built-in types so that we could re-use them in the type annotations?
def word_count(input: list[str]) -> dict[str, int]:
Since types are otherwise unlikely to be indexable like that, I think that might work.
-1 on that idea. Actually, -1 on List, Dict and friends as well. Square brackets are for lookup (indexing, key-based, or slicing). Saying here that you’re “looking up” a subtype of list that holds strings is a far stretch. -- Best regards, Łukasz Langa WWW: http://lukasz.langa.pl/ Twitter: @llanga IRC: ambv on #python-dev

Steven D'Aprano <steve@pearwood.info> writes:
On Wed, Aug 13, 2014 at 10:29:48PM +0200, Christian Heimes wrote:
1) I'm not keen with the naming of mypy's typing classes. The visual distinction between e.g. dict() and Dict() is too small and IMHO confusing for newcomers. […]
Would it be possible, and desirable, to modify the built-in types so that we could re-use them in the type annotations?
That would address my concern. With that change, when the programmer who reads the code encounters mention of a type, it means precisely the same type object as it appears to be and not some special beast. -- \ “Science embraces facts and debates opinion; religion embraces | `\ opinion and debates the facts.” —Tom Heehler, _The Well-Spoken | _o__) Thesaurus_ | Ben Finney

Steven D'Aprano writes:
2) PEP 3107 only specifies arguments and return values but not exceptions that can be raised by a function. Java has the "throws" syntax to list possible exceptions:
public void readFile() throws IOException {}
I understand that this is called a "checked exception" in Java. I also understand that they are hated and derided
Sure, but that's because it's hard for a human to guess what might happen down in lower-level functions, let alone 3rd-party libraries and the runtime -- and your program fails if you guess wrong. Maybe the fact that type-checking is going to be optional mitigates that. I suspect it's not terrible useful, but another idea is to invert the sense, i.e. say what exceptions the function believes it handles, and therefore cannot be raised.
Perhaps with a few years of experience, we might be able to extend this to exceptions without making the same mistakes as Java's checked exceptions, but I wouldn't rush into it.
+1 to that!

On Aug 14, 2014, at 2:02 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Would it be possible, and desirable, to modify the built-in types so that we could re-use them in the type annotations?
def word_count(input: list[str]) -> dict[str, int]:
Since types are otherwise unlikely to be indexable like that, I think that might work.
Huge +1 from me. I know that extending types to do more might not be the first thing someone would want to do, but I think describing types well is exactly the right thing to extend type instances to do. If that were to be considered plausible, it would also make sense to my mind to implement the union operator (|) on types, so that we could use: int | None Which I think would be quite beautiful. (or would it need to be NoneType? Perhaps we can give some special consideration to the None singleton) It’s only tangentially related, but my (likely broken) work on a TypeSet metaclass might also be informative. It’s goal was to bring set operations to types, which has some overlap with the above discussion. https://github.com/ryanhiebert/typeset

On Thursday, August 14, 2014 12:03 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Wed, Aug 13, 2014 at 10:29:48PM +0200, Christian Heimes wrote:
1) I'm not keen with the naming of mypy's typing classes. The visual distinction between e.g. dict() and Dict() is too small and IMHO confusing for newcomers. How about an additional 'T' prefix to make clear that the objects are referring to typing objects?
from typing import TList, TDict
def word_count(input: TList[str]) -> TDict[str, int]: ...
Would it be possible, and desirable, to modify the built-in types so that we could re-use them in the type annotations?
def word_count(input: list[str]) -> dict[str, int]:
Since types are otherwise unlikely to be indexable like that, I think that might work.
I strongly agree with the basic sentiment here, but if you take it a little bit farther, it makes things a lot simpler. Most of the code in typing.py is a duplication of the ABCs in the collections.abc module (and io and maybe others). I understand that MyPy couldn't monkeypatch that code into the stdlib, so it had to fork the contents, but if this is going into the stdlib, can't we just modify collections/abc.py instead? Why not have the type information on collections.abc.Sequence be introspectable at runtime? More importantly, why not have the interface defined by collections.abc.Sequence be _exactly_ the same as the one checked by the static type checker, instead of just very similar? This would also make it easier for some libraries to document their types. For example, dbm.* could inherit from or register with MutableMapping[bytes, bytes] instead of just MutableMapping, and then it wouldn't have to explain in the docstring that the keys and values have to be bytes. Once you move the ABC typing to the actual ABCs, the only problem left is the built-in (concrete) collections. But I still don't understand why people even _want_ those to be checked. Does anyone have a good example of a function that needs to restrict its arguments to list[str] instead of Sequence[str]? Guido gave us a _terrible_ example: a function whose most obvious use was on text files, but he added a static type check that prevents it being used that way. And I think that will be the case the vast majority of the time. When I see documentation in a PyPI library or a colleague's code that says a function takes a list, it's almost always a lie; the function in fact takes any iterable (or occasionally any sequence or mutable sequence). (In fact, every exception I can think of is written in C, so it couldn't be easily annotated anyway.) It seems like we're making things a lot harder for ourselves, just for a handful of types that people are almost always going to use wrong. (As a side note, generic ABCs might even be the answer to the AnyStr problem: str implements String[str], bytes implements String[bytes], bytearray implements MutableString[bytes], and classes like PyObjC's NSString can now document that they implement String[str] or String[bytes] as appropriate.) Anyway, these leaves typing.py as nothing but functions (Union, Optional, etc.) on types defined elsewhere, in the rest of the stdlib.

On Thu, Aug 14, 2014 at 8:51 PM, Andrew Barnert < abarnert@yahoo.com.dmarc.invalid> wrote:
Most of the code in typing.py is a duplication of the ABCs in the collections.abc module (and io and maybe others). I understand that MyPy couldn't monkeypatch that code into the stdlib, so it had to fork the contents, but if this is going into the stdlib, can't we just modify collections/abc.py instead? Why not have the type information on collections.abc.Sequence be introspectable at runtime? More importantly, why not have the interface defined by collections.abc.Sequence be _exactly_ the same as the one checked by the static type checker, instead of just very similar?
This would also make it easier for some libraries to document their types. For example, dbm.* could inherit from or register with MutableMapping[bytes, bytes] instead of just MutableMapping, and then it wouldn't have to explain in the docstring that the keys and values have to be bytes.
Once you move the ABC typing to the actual ABCs, the only problem left is the built-in (concrete) collections. But I still don't understand why people even _want_ those to be checked. Does anyone have a good example of a function that needs to restrict its arguments to list[str] instead of Sequence[str]? Guido gave us a _terrible_ example: a function whose most obvious use was on text files, but he added a static type check that prevents it being used that way. And I think that will be the case the vast majority of the time. When I see documentation in a PyPI library or a colleague's code that says a function takes a list, it's almost always a lie; the function in fact takes any iterable (or occasionally any sequence or mutable sequence). (In fact, every exception I can think of is written in C, so it couldn't be easily annotated anyway.) It seems like we're making things a lot harder for ourselves, just for a handful of types that people are almost always going to use wrong.
(As a side note, generic ABCs might even be the answer to the AnyStr problem: str implements String[str], bytes implements String[bytes], bytearray implements MutableString[bytes], and classes like PyObjC's NSString can now document that they implement String[str] or String[bytes] as appropriate.)
Anyway, these leaves typing.py as nothing but functions (Union, Optional, etc.) on types defined elsewhere, in the rest of the stdlib.
I think I already responded to a similar proposal. I think modifying the existing ABCs is fine for Python 3.5 and beyond, but I think I'd like to keep aliasing in the typing module to make it easier to use annotations in earlier Python versions (using a typing.py installed from PyPI). I feel similar about using a|b as a concise way to spell Union[a, n], and int|None would be quite decent as a way to spell optional int. But again, these could only be used in code meant exclusively for Python 3.5 and later. -- --Guido van Rossum (python.org/~guido)

On Thursday, August 14, 2014 9:15 PM, Guido van Rossum <guido@python.org> wrote:
On Thu, Aug 14, 2014 at 8:51 PM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
Most of the code in typing.py is a duplication of the ABCs in the collections.abc module (and io and maybe others). I understand that MyPy couldn't monkeypatch that code into the stdlib, so it had to fork the contents, but if this is going into the stdlib, can't we just modify collections/abc.py instead? Why not have the type information on collections.abc.Sequence be introspectable at runtime? More importantly, why not have the interface defined by collections.abc.Sequence be _exactly_ the same as the one checked by the static type checker, instead of just very similar?
[snip]
I think I already responded to a similar proposal. I think modifying the existing ABCs is fine for Python 3.5 and beyond, but I think I'd like to keep aliasing in the typing module to make it easier to use annotations in earlier Python versions (using a typing.py installed from PyPI).
That sounds like a great idea. My worry wasn't the extra few KB of code sitting on my computer, it was the extra set of not-quite-the-same parallel concepts that are in typing.py today. If it instead has exactly the same classes as collections.abc, io, etc., everything's great.
I feel similar about using a|b as a concise way to spell Union[a, n], and int|None would be quite decent as a way to spell optional int.
Swift makes you sound like a 12-year-old girl? who makes every phrase? sound like a question? by writing `int?` everywhere? Haskell maybe makes you sound wishy-washy and maybe a little uncertain by having you say `Maybe int`. Python lets you declare, `int, or None` like Patrick Henry. Sounds good.

It was written:
a static type check that prevents it being used that way.
Can we please refrain from using the word "prevent" in this thread? I suggest instead a static type check that generates [scads of|tons of|way too many] [spurious|bogus|annoying] warnings when used that way. which is IMO a genuine concern but also probably has technological fixes like the one Andrew proposes (automatically promoting many container types to the ABC actually needed). In some sense I'm not really bothered by this (I'd be willing to omit all the bracketed phrases :-). If an upstream author writes def somefilter(l_of_s: List[str]) -> List[int]: pass I think it's reasonable to assume that Ms. U. S. Author didn't think about the implications of tuples or dictionary views or iterators or whatever. I personally think a warning here is a *good* thing: if Ms. Author didn't intend those usages, why should she have to check them? If somebody else does the checking and believes the function is properly prepared for those usages, they should prepare an RFE (*not* a bug report! -- Ms. Author will have to recheck that the code works as claimed in the more general context, and perhaps fix bugs to ensure that it does work to *her* standards for code *she* distributes).

On Thursday, August 14, 2014 10:46 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
I suggest instead
a static type check that generates [scads of|tons of|way too many] [spurious|bogus|annoying] warnings when used that way.
which is IMO a genuine concern but also probably has technological fixes like the one Andrew proposes (automatically promoting many container types to the ABC actually needed).
In some sense I'm not really bothered by this (I'd be willing to omit all the bracketed phrases :-). If an upstream author writes
def somefilter(l_of_s: List[str]) -> List[int]: pass
I think it's reasonable to assume that Ms. U. S. Author didn't think about the implications of tuples or dictionary views or iterators or whatever. I personally think a warning here is a *good* thing: if Ms. Author didn't intend those usages, why should she have to check them?
You're forgetting that linting is not the only purpose of static type annotations in this proposal. If you write what should be perfectly valid code and it spits out scads of warnings when linted, yes, you can quickly figure out that it's a bug in the library and report it upstream. But if, say, your IDE doesn't suggest a function that it could have, you may never notice the problem, and just find the library to be less fun to use than you expected.

Andrew Barnert writes:
On Thursday, August 14, 2014 10:46 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
I think it's reasonable to assume that Ms. U. S. Author didn't think about the implications of tuples or dictionary views or iterators or whatever. I personally think a warning here is a *good* thing: if Ms. Author didn't intend those usages, why should she have to check them?
You're forgetting that linting is not the only purpose of static type annotations in this proposal.
Hardly.
But if, say, your IDE doesn't suggest a function that it could have, you may never notice the problem, and just find the library to be less fun to use than you expected.
But who decides it *should* be perfectly valid code? *Somebody* has to check that, and if code is used naively in a case where it shouldn't be (cf the "why doesn't sum() handle iter_of_str" thread!), the user is screwed again. Have you never written code that was perfectly sound for your purpose, but could be used in other contexts according to ducktyping? Are you *sure* that it is valid in those other contexts? (Think sum vs. math.fsum -- accuracy matters.) Of course it's a matter of balance, and *maybe* it tips more in the direction of "default to the most general type where the code will run without crashing or raising". I'm just saying it's not obvious to me that it's such a bad thing that one-off piece of junk libraries may not be as re-abusable if you use a type checker as they are when you don't.

Am 15.08.2014 11:11, schrieb Stephen J. Turnbull:
Have you never written code that was perfectly sound for your purpose, but could be used in other contexts according to ducktyping? Are you *sure* that it is valid in those other contexts? (Think sum vs. math.fsum -- accuracy matters.)
Well, if someone uses my frobnicate method that I intended to be only usable by lists, but he uses it with generators, it's his responsibility to write a unit test for his code (which he should do anyway). Therefore he's responsible for guaranteeing that his code continues to work, and a linter should not tell him "you can't do that, the author hasn't thought of that". At least I should be able to silence the warning *once* and for all. Furthermore, I want my IDE to suggest me the frobnicate method even if I'm working with generators, so there should be a way for me to tell the linter and IDE "frobnicate takes an iterable, ignore the silly original annotation" Unit testing your own code is the only way you can "guarantee" that your code runs correctly. After all, even if you use my frobnicate method only with lists, you will still have to write a test, maybe I introduce a subtle bug in the new version of my library.

On Fri, Aug 15, 2014 at 12:02:21PM +0200, Dennis Brakhane wrote:
Unit testing your own code is the only way you can "guarantee" that your code runs correctly.
That is actually backwards. Unit testing cannot guarantee the code is correct. If you have 1000 tests, all that you have proved is that the code works for those 1000 cases. It tells you nothing about all the uncounted billions of other cases you haven't written tests for. Given some set of tests which pass, you can be confident that your code gets at least those things correct (assuming the tests are themselves correct -- you write tests for your tests, don't you? *wink*). But that's all. As Dijkstra said, “Program testing can be used to show the presence of bugs, but never to show their absence.” To put it another way, there could be a million bugs hiding in all the corners of your code that you don't test, and you can never test *everything*. Static typing is complementary to unit testing. Static typing can eliminate a lot of the unit tests that you otherwise would have to write. For example, you might have unit tests that try to demonstrate that given arguments of type float, the function will return a float; given Fraction arguments, the function returns a Fraction; and so on. A static type checker can *prove* that[1] instead of merely testing it with a few dozen examples and hoping the result generalises to all the billions of other floats and Fractions not tested. Static typing is actually a form of automated correctness testing. [1] Assuming you trust that the checker itself is bug free. -- Steven

Hello, First, as a disclaimer, I am currently working on Numba for Continuum Analytics. Numba has its own type inference system which it applies to functions decorated with the @jit decorator. Due to Numba's objectives, the type inference system is heavily geared towards numerical computing, but it is conceptually (and a bit concretely) able to represent more generic information, such as "an enumerate() over an iterator of a complex128 numpy array". There are two sides to type inference: 1) first the (optional) annotations (I'm saying "optional" because in the most basic usage, a JIT compiler is normally able to defer compilation until the first function or method call, and to deduce input types from that) 2) second the inference engine properly, which walks the code (in whatever form the tool's developer has chosen: bytecode, AST, IR) and deduces types for any intermediate values Now only #1 is implied by this PEP proposal, but it also sounds like we should take into account the desired properties of #2 (for example, being able to express "an iterator of three-tuples" can be important for a JIT compiler - or not, perhaps, depending on the JIT compiler :-)). What #2 wants to do will differ depending on the use case: e.g. a code checker may need less type granularity than a JIT compiler. Therefore, regardless of mypy's typesystem's completeness and granularity, one requirement is for it to be easily extensible. By extensible I mean not only being able to define new type descriptions, but being able to do so for existing third-party libraries you don't want to modify. I'm saying that because I'm looking at http://mypy-lang.org/tutorial.html#genericclasses , and it's not clear from this example whether the typing code has to be interwoven with the collection's implementation, or can be written as a separate code module entirely (*). Ideally both should probably be possible (in the same vein as being able to subclass an ABC, or register an existing class with it). This also includes being to type-declare functions and types from C extension modules. In Numba, this would be typically required to write typing descriptions for Numpy arrays and functions; but also to derive descriptions for fixed-width integers, single-precision floats, etc. (this also means some form of subclassing for type descriptions themselves). (*) (actually, I'm a bit worried when I see that "List[int]()" instantiates an actual list; calling a type description class should give you a parametered type description, not an object; the [] notation is in general not powerful enough if you want several type parameters, possibly keyword-only) At some point, it will be even better if the typing system is powerful enough to remember properties of the *values* (for example not only "a string", but "a one-character string, or even "one of the 'Y', 'M', 'D' strings"). Think about type-checking / type-infering calls to the struct module. I may come back with more comments once I've read the mypy docs and/or code in detail. Regards Antoine.

On Wed, Aug 13, 2014 at 3:14 PM, Guido van Rossum <guido@python.org> wrote:
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter.
I'll comment later on the core subject. For now, I think this deserves some thought: Function annotations are not available in Python 2.7, so promoting widespread use of annotations in 3.5 would be promoting code that is compatible only with 3.x, when the current situation is that much effort is being spent on writing code that works on both 2.7 and 3.4 (most libraries?). Independently of its core merits, this proposal should fail unless annotations are added to Python 2.8. Cheers, -- Juancarlo *Añez*

On Wed, Aug 13, 2014 at 3:21 PM, Juancarlo Añez <apalala@gmail.com> wrote:
On Wed, Aug 13, 2014 at 3:14 PM, Guido van Rossum <guido@python.org> wrote:
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter.
I'll comment later on the core subject.
For now, I think this deserves some thought:
Function annotations are not available in Python 2.7, so promoting widespread use of annotations in 3.5 would be promoting code that is compatible only with 3.x, when the current situation is that much effort is being spent on writing code that works on both 2.7 and 3.4 (most libraries?).
Independently of its core merits, this proposal should fail unless annotations are added to Python 2.8.
Actually, mypy already has a solution. There's a codec ( https://github.com/JukkaL/mypy/tree/master/mypy/codec) that you can use which transforms Python-2-with-annotations into vanilla Python 2. It's not an ideal solution, but it can work in cases where you absolutely have to have state of the art Python 3.5 type checking *and* backwards compatibility with Python 2. -- --Guido van Rossum (python.org/~guido)

On Wed, Aug 13, 2014 at 6:41 PM, Guido van Rossum <guido@python.org> wrote:
Actually, mypy already has a solution. There's a codec ( https://github.com/JukkaL/mypy/tree/master/mypy/codec) that you can use which transforms Python-2-with-annotations into vanilla Python 2. It's not an ideal solution, but it can work in cases where you absolutely have to have state of the art Python 3.5 type checking *and* backwards compatibility with Python 2.
It can't be a solution because it's a hack... Cheers, -- Juancarlo *Añez*

On Wed, Aug 13, 2014 at 05:51:40PM -0430, Juancarlo Añez wrote:
Function annotations are not available in Python 2.7, so promoting widespread use of annotations in 3.5 would be promoting code that is compatible only with 3.x,
Yes. You say that as if it were a bad thing. It is not. Python 3 is here to stay and we should be promoting Python 3 only code. There is absolutely no need to apologise for that fact. If people are happy with Python the way it is in 2.7, or 1.5 for that matter, that's great, they can stay on it for ever, but all new features are aimed at 3.x and not 2.x or 1.x.
when the current situation is that much effort is being spent on writing code that works on both 2.7 and 3.4 (most libraries?).
There's no reason why all new code should be aimed at 2.x and 3.x. But even for code which is, the nice thing about this proposal is that it's optional, so you can run your type-check using mypy under Python 3.x and still get the benefit of it when running under 2.x.
Independently of its core merits, this proposal should fail unless annotations are added to Python 2.8.
There will be no Python 2.8, and no Python 2.9 either. New features go into 3.x. -- Steven

On Thu, Aug 14, 2014 at 1:15 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Yes. You say that as if it were a bad thing. It is not. Python 3 is here to stay and we should be promoting Python 3 only code. There is absolutely no need to apologise for that fact. If people are happy with Python the way it is in 2.7, or 1.5 for that matter, that's great, they can stay on it for ever, but all new features are aimed at 3.x and not 2.x or 1.x.
That's reasonable..., in theory. Reality is that most of the people most supportive of the migration towards Python 3 are currently writing code that is compatible with both 3.x and 2.[67]. You don't leave your people behind (not without a lifeboat). Since its decided there will not be a 2.8, the right thing to do is to delay decisions about static typing (or restrictions on annotations) till 4.0. It would be "a bad thing" to break or deprecate existing 3.x code with 3.5. Cheers, -- Juancarlo *Añez*

Le 14/08/2014 22:12, Juancarlo Añez a écrit :
On Thu, Aug 14, 2014 at 1:15 PM, Steven D'Aprano <steve@pearwood.info <mailto:steve@pearwood.info>> wrote:
Yes. You say that as if it were a bad thing. It is not. Python 3 is here to stay and we should be promoting Python 3 only code. There is absolutely no need to apologise for that fact. If people are happy with Python the way it is in 2.7, or 1.5 for that matter, that's great, they can stay on it for ever, but all new features are aimed at 3.x and not 2.x or 1.x.
That's reasonable..., in theory.
Reality is that most of the people most supportive of the migration towards Python 3 are currently writing code that is compatible with both 3.x and 2.[67].
You don't leave your people behind (not without a lifeboat).
Since its decided there will not be a 2.8, the right thing to do is to delay decisions about static typing (or restrictions on annotations) till 4.0. It would be "a bad thing" to break or deprecate existing 3.x code with 3.5.
It breaks nothing since it's optional. What you say is true for every new feature in python: using a feature present in on version of python prevents the code to be compatible with previous versions. I mostly write code compatible with 2.7 and 3.x, I won't use annotations the same way I won't use asyncio or yield from...
Cheers, -- Juancarlo *Añez*
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

Juancarlo Añez writes:
Reality is that most of the people most supportive of the migration towards Python 3 are currently writing code that is compatible with both 3.x and 2.[67].
And under Guido's proposal, they can continue to do so. They just have to live without the benefits of function annotations in that code, same as they already do. Or they can write a 3to2 tool to strip the annotations. It's just that the benefits to using Python 3 vs. 2/3-compatible syntax may take a quantum leap upward. I don't see a real problem here. All that Guido is proposing to do is to rationalize development effort on something people are already doing by standardizing a good-enough approach, on an opt-in basis. Sure, there are people who want something more coercive, but Python developers aren't going to stand for that, and Guido himself is the first line of defense. He's already said that's not going to happen, not now (and IIUC probably never).

On Thu, Aug 14, 2014 at 10:09 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
All that Guido is proposing to do is to rationalize development effort on something people are already doing by standardizing a good-enough approach, on an opt-in basis. Sure, there are people who want something more coercive, but Python developers aren't going to stand for that, and Guido himself is the first line of defense. He's already said that's not going to happen, not now (and IIUC probably never).
The change has been decreed, so it will be. It's all right, because (as Guido suggests in the decree) we can now take the discussion to how to make it good for all involved. The Python community will likely succeed at that. It is a tautology that only experience will reveal the truth, at least so in our endeavour. Signing off this thread, yours truly, -- Juancarlo *Añez*

On Thu Aug 14 2014 at 8:02:14 PM Juancarlo Añez <apalala@gmail.com> wrote:
On Thu, Aug 14, 2014 at 10:09 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
All that Guido is proposing to do is to rationalize development effort on something people are already doing by standardizing a good-enough approach, on an opt-in basis. Sure, there are people who want something more coercive, but Python developers aren't going to stand for that, and Guido himself is the first line of defense. He's already said that's not going to happen, not now (and IIUC probably never).
The change has been decreed, so it will be.
Where? Please link to that in the mailing list archives if so.
It's all right, because (as Guido suggests in the decree) we can now take the discussion to how to make it good for all involved. The Python community will likely succeed at that.
It is a tautology that only experience will reveal the truth, at least so in our endeavour.
Signing off this thread, yours truly,
-- Juancarlo *Añez* _______________________________________________ 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 Wed, Aug 13, 2014 at 9:44 PM, Guido van Rossum <guido@python.org> wrote:
[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.]
This is a very interesting idea. I played a bit with function annotations ( https://github.com/ceronman/typeannotations) and I gave a talk about them at EuroPython 2013. Certainly static type analysis is probably the best use case. The curious thing here is that while standardizing a syntax for type
annotations, we technically still won't be adopting standard rules for type checking. This is intentional. First of all, fully specifying all the type checking rules would make for a really long and boring PEP (a much better specification would probably be the mypy source code). Second, I think it's fine if the type checking algorithm evolves over time, or if variations emerge. The worst that can happen is that you consider your code correct but mypy disagrees; your code will still run.
That said, I don't want to *completely* leave out any specification. I want the contents of the typing.py module to be specified in the PEP, so that it can be used with confidence. But whether mypy will complain about your particular form of duck typing doesn't have to be specified by the PEP. Perhaps as mypy evolves it will take options to tell it how to handle certain edge cases. Forks of mypy (or entirely different implementations of type checking based on the same annotation syntax) are also a possibility. Maybe in the distant future a version of Python will take a different stance, once we have more experience with how this works out in practice, but for Python 3.5 I want to restrict the scope of the upheaval.
The type checking algorithm might evolve over the time, but by including typing.py in the stdlib, the syntax for annotations would be almost frozen and that will be a limitation. In other projects such as TypeScript ( http://www.typescriptlang.org/), that the syntax usually evolves alongside the algorithms. Is the syntax specifyed in typing.py mature enough to put it in the stdlib and expect users to start annotating their projects without worrying too much about future changes? Is there enough feedback from users using mypy in their projects? I think that rushing typing.py into 3.5 is not a good idea. However, It'd be nice to add some notes in PEP8, encourage it's use as an external library, let some projects and tools (e.g. PyCharm) use it. It's not that bad if mypy lives 100% outside the Python distribution for a while. Just like TypeScript to JavaScript. After getting some user base, part of it (typing.py) could be moved to the stdlib. Manuel.

(1) A change of direction for function annotations
PEP 3107, which introduced function annotations, is intentional non-committal about how function annotations should be used. It lists a number of use cases, including but not limited to type checking. It also mentions some rejected proposals that would have standardized either a syntax for indicating types and/or a way for multiple frameworks to attach different annotations to the same function. AFAIK in practice there is
On Aug 13, 2014 9:45 PM, "Guido van Rossum" <guido@python.org> wrote: little use of function annotations in mainstream code, and I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them.
(We may have to have some backwards compatibility provision to avoid
breaking code that currently uses annotations for some other purpose. Fortunately the only issue, at least initially, will be that when running mypy to type check such code it will produce complaints about the annotations; it will not affect how such code is executed by the Python interpreter. Nevertheless, it would be good to deprecate such alternative uses of annotations.) I watched the original talk and read your proposal. I think type annotations could very very useful in certain contexts. However, I still don't get this bit. Why would allowing type annotations automatically imply that no other annotations would be possible? Couldn't we formalize what would be considered a type annotation while still allowing annotations that don't fit this criteria to be used for other things?

On Wed, Aug 13, 2014 at 3:28 PM, Todd <toddrjen@gmail.com> wrote:
However, I still don't get this bit. Why would allowing type annotations automatically imply that no other annotations would be possible? Couldn't we formalize what would be considered a type annotation while still allowing annotations that don't fit this criteria to be used for other things?
We certainly *could* do that. However, I haven't seen sufficient other uses of annotations. If there is only one use for annotations (going forward), annotations would be unambiguous. If we allow different types of annotations, there would have to be a way to tell whether a particular annotation is intended as a type annotation or not. Currently mypy ignores all modules that don't import typing.py (using any form of import statement), and we could continue this convention. But it would mean that something like this would still require the typing import in order to be checked by mypy: import typing def gcd(int a, int b) -> int: <tralala> The (necessary) import would be flagged as unused by every linter in the world... :-( -- --Guido van Rossum (python.org/~guido)

On 14 August 2014 00:30, Guido van Rossum <guido@python.org> wrote:
We certainly *could* do that. However, I haven't seen sufficient other uses of annotations. If there is only one use for annotations (going forward), annotations would be unambiguous. If we allow different types of annotations, there would have to be a way to tell whether a particular annotation is intended as a type annotation or not. Currently mypy ignores all modules that don't import typing.py (using any form of import statement), and we could continue this convention. But it would mean that something like this would still require the typing import in order to be checked by mypy:
import typing
def gcd(int a, int b) -> int: <tralala>
Sorry, I'm slowly going through this thread, so my apologies if this has been covered later, but it seems to me that projects (and in particular libraries) that want to target 2.7 as well as 3.x will be forced to avoid this feature. And avoid any dependencies that use it. Specifically, the annotation syntax is purely Python 3.x, so without some form of translation or import hook, 2.7 won't accept it. And adding a dependency (even if it's only a very lightweight typing.py plus a hook installed somewhere/somehow) for something that only has value as part of a developer-level type check doesn't seem like a good solution. So, I'd like someone to explain (maybe by pointing me to relevant mypy docs, I haven't investigated them yet) how I should modify my existing code that supports 2.7 and 3.x so that it uses the new functionality *without* extra runtime dependencies that only deliver build/test-time benefits (that's the problem I always had with setuptools/pkg_resources, and I'd not like to see it repeated here). Paul

On Wed Aug 20 2014 at 9:29:34 AM Paul Moore <p.f.moore@gmail.com> wrote:
On 14 August 2014 00:30, Guido van Rossum <guido@python.org> wrote:
We certainly *could* do that. However, I haven't seen sufficient other uses of annotations. If there is only one use for annotations (going forward), annotations would be unambiguous. If we allow different types of annotations, there would have to be a way to tell whether a particular annotation is intended as a type annotation or not. Currently mypy ignores all modules that don't import typing.py (using any form of import statement), and we could continue this convention. But it would mean that something like this would still require the typing import in order to be checked by mypy:
import typing
def gcd(int a, int b) -> int: <tralala>
Sorry, I'm slowly going through this thread, so my apologies if this has been covered later, but it seems to me that projects (and in particular libraries) that want to target 2.7 as well as 3.x will be forced to avoid this feature. And avoid any dependencies that use it.
Specifically, the annotation syntax is purely Python 3.x, so without some form of translation or import hook, 2.7 won't accept it. And adding a dependency (even if it's only a very lightweight typing.py plus a hook installed somewhere/somehow) for something that only has value as part of a developer-level type check doesn't seem like a good solution.
So, I'd like someone to explain (maybe by pointing me to relevant mypy docs, I haven't investigated them yet) how I should modify my existing code that supports 2.7 and 3.x so that it uses the new functionality *without* extra runtime dependencies that only deliver build/test-time benefits (that's the problem I always had with setuptools/pkg_resources, and I'd not like to see it repeated here).
I suspect the answer is "you don't". Just like everything else that is syntactically exclusive to Python 3, it's a perk you get with Python 3-only code that you simply can't get if you want to maintain backwards-compatibility.

On Aug 20, 2014, at 10:11 AM, Brett Cannon <brett@python.org> wrote:
On Wed Aug 20 2014 at 9:29:34 AM Paul Moore <p.f.moore@gmail.com <mailto:p.f.moore@gmail.com>> wrote: On 14 August 2014 00:30, Guido van Rossum <guido@python.org <mailto:guido@python.org>> wrote:
We certainly *could* do that. However, I haven't seen sufficient other uses of annotations. If there is only one use for annotations (going forward), annotations would be unambiguous. If we allow different types of annotations, there would have to be a way to tell whether a particular annotation is intended as a type annotation or not. Currently mypy ignores all modules that don't import typing.py (using any form of import statement), and we could continue this convention. But it would mean that something like this would still require the typing import in order to be checked by mypy:
import typing
def gcd(int a, int b) -> int: <tralala>
Sorry, I'm slowly going through this thread, so my apologies if this has been covered later, but it seems to me that projects (and in particular libraries) that want to target 2.7 as well as 3.x will be forced to avoid this feature. And avoid any dependencies that use it.
Specifically, the annotation syntax is purely Python 3.x, so without some form of translation or import hook, 2.7 won't accept it. And adding a dependency (even if it's only a very lightweight typing.py plus a hook installed somewhere/somehow) for something that only has value as part of a developer-level type check doesn't seem like a good solution.
So, I'd like someone to explain (maybe by pointing me to relevant mypy docs, I haven't investigated them yet) how I should modify my existing code that supports 2.7 and 3.x so that it uses the new functionality *without* extra runtime dependencies that only deliver build/test-time benefits (that's the problem I always had with setuptools/pkg_resources, and I'd not like to see it repeated here).
I suspect the answer is "you don't". Just like everything else that is syntactically exclusive to Python 3, it's a perk you get with Python 3-only code that you simply can't get if you want to maintain backwards-compatibility.
mypy does have a codec that will ignore annotations on 2.x. But other than that the answer is you don’t. --- Donald Stufft PGP: 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA

On Aug 20, 2014, at 7:25, Donald Stufft <donald@stufft.io> wrote:
On Aug 20, 2014, at 10:11 AM, Brett Cannon <brett@python.org> wrote:
On Wed Aug 20 2014 at 9:29:34 AM Paul Moore <p.f.moore@gmail.com> wrote:
On 14 August 2014 00:30, Guido van Rossum <guido@python.org> wrote:
We certainly *could* do that. However, I haven't seen sufficient other uses of annotations. If there is only one use for annotations (going forward), annotations would be unambiguous. If we allow different types of annotations, there would have to be a way to tell whether a particular annotation is intended as a type annotation or not. Currently mypy ignores all modules that don't import typing.py (using any form of import statement), and we could continue this convention. But it would mean that something like this would still require the typing import in order to be checked by mypy:
import typing
def gcd(int a, int b) -> int: <tralala>
Sorry, I'm slowly going through this thread, so my apologies if this has been covered later, but it seems to me that projects (and in particular libraries) that want to target 2.7 as well as 3.x will be forced to avoid this feature. And avoid any dependencies that use it.
Specifically, the annotation syntax is purely Python 3.x, so without some form of translation or import hook, 2.7 won't accept it. And adding a dependency (even if it's only a very lightweight typing.py plus a hook installed somewhere/somehow) for something that only has value as part of a developer-level type check doesn't seem like a good solution.
So, I'd like someone to explain (maybe by pointing me to relevant mypy docs, I haven't investigated them yet) how I should modify my existing code that supports 2.7 and 3.x so that it uses the new functionality *without* extra runtime dependencies that only deliver build/test-time benefits (that's the problem I always had with setuptools/pkg_resources, and I'd not like to see it repeated here).
I suspect the answer is "you don't". Just like everything else that is syntactically exclusive to Python 3, it's a perk you get with Python 3-only code that you simply can't get if you want to maintain backwards-compatibility.
This seems like much more of an issue for libraries than for applications. It's not a big deal for an app to require Python 3.5, but most libraries out there are supporting, e.g., 2.7/3.3+, and I think that will continue for quite some time. You want your library to be useful to people whose apps are in 2.x and who don't yet have a compelling reason to port them, unless you're confident that your library in itself is compelling enough to be worth the effort and risk.
mypy does have a codec that will ignore annotations on 2.x. But other than that the answer is you don’t.
If the issue here is not wanting to create a new runtime dependency, that should be pretty easy to solve by just applying the codec at build time for 2.x installs. If the codec isn't easy enough to use from setup.py, that shouldn't be too hard to fix. And maybe the codec (or equivalent functionality) could be added to setuptools, so it doesn't add an extra build-time dependency to most projects. I realize that at first glance this sounds similar to saying "just run 2to3 or 3to2 at install time", which turned out to be unrealistic for many libraries. But the difference is that this is a very simple, specific transformation that should have no semantic impact on your runtime code, so I don't think it's naive to expect that it would be simple and reliable as a fully automated transformation.

On 8/20/2014 10:55 AM, Andrew Barnert wrote:
This seems like much more of an issue for libraries than for applications. It's not a big deal for an app to require Python 3.5, but most libraries out there are supporting, e.g., 2.7/3.3+, and I think that will continue for quite some time. You want your library to be useful to people whose apps are in 2.x and who don't yet have a compelling reason to port them, unless you're confident that your library in itself is compelling enough to be worth the effort and risk.
If the annotation for a library are in a separate skeleton file (which would only run on 3.5+), then users of the library (including the library itself), would not see the annotations unless they run on 3.5+ and look for the annotation file. -- Terry Jan Reedy

Terry Reedy writes:
If the annotation for a library are in a separate skeleton file
You mean stub file, right? (Stub is the term used so far in this thread.) To develop Terry's idea a bit more explicitly, ISTM that during early development, you use Python 3 with annotations in file. Then when you need to test Python 2, you use a tool (as yet to be developed) which generates a Python file that has had all annotations stripped, and a stub file[1]. Throw away the stub file, test the Python file, and return to development. For distribution (to *both* Python 3 and Python 2 users) you ignore the original annotated code and distribute the generated Python file and the stubs file. Probably there would be a tool for combining the Python and stub files back into an annotated Python file. The two tools (stripper and annotator) can be very accurate (although the annotator may not produce precisely the style desired by users, the tools probably can be designed so that the re-annotated file accurately reflects the style used by the authors). Is the implied workflow really so burdensome? Especially to downstream? Note that Terry works on IDLE, so it seems likely that at least one Python IDE would support this workflow very well. :-) Footnotes: [1] Yes, I know, the stub file is actually Python, too. You know what I mean.

(My apologies in advance that I have not been able to read all the emails on this set of threads.) Mypy’s approach to parameterizing types using slice notation/__getitem__ is a rather neat hack, given how it does not require any new notation, matches rather similar syntax in languages like Scala and Haskell, and can be readily added to existing types, whether builtins like list, collections like deque, or ABC collection types like Iterator andSequence. Adding such parameterization support to builtin/stdlib types is a compelling reason for including in 3.5. An enthusiastic +1! Such support is also a very good reason for Jython 3.x to target 3.5. However, let's not restrict Python’s support for static typing, or related schemes like gradual typing, to just what Mypy can do now. This should be about a preferred syntax, especially since there’s other active work, some of which I’m involved in like Reticulated ( http://wphomes.soic.indiana.edu/jsiek/), which has the possibility of helping with such issues as better Java integration in Jython, such as with the Clamp project (https://github.com/jimbaker/clamped). Instead, I like what one message stated - that such type expressions in function annotations can be arbitrarily complicated expressions. We can have simple parameterization, but also disjunctive types, and more. In certain simple cases, linters will be able to meaningfully work with such type expressions without having to evaluate the defining module (if not actually run it); in other cases, they won’t. But this is just what linters cope with now. We see something similar in Clamp, since we need to evaluate class definitions ahead-of-time to generate the bytecode for corresponding Java proxy classes - otherwise, we would not have a jar that Java/JVM code could use for linkage. Using type variables is another aspect that can be addressed in this proposed syntax, but it is readily doable without further modification. Consider a type expression like Sequence[T] - the only thing that we need to do is assign the type variable T in the scope of the annotation usage. I’m pretty certain we can do some pretty nice things with such type variable assignments to describe type bounds, variance, and what not, for those so inclined and type checkers that can handle. Again, this keeps things nicely open without undue restrictions. One last thing: let’s not add List, TList, etc. Just use list and other existing types to generate new types. I think this is the main reason why 3.5 support is needed. Being able to use types as objects in this fashion is a wonderful pleasure of working with Python as of new style classes (so I will assume now that we are talking about Python 3.5!). The same goes for putting such support in docstrings (although static type checkers could potentially attempt to cross validate) and in comments. Using stub files in contrast is a great idea, and something that can also be standardized on. Thanks again for a fantastic, if somewhat overwhelming conversation. - Jim On Wed, Aug 20, 2014 at 7:42 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
Terry Reedy writes:
If the annotation for a library are in a separate skeleton file
You mean stub file, right? (Stub is the term used so far in this thread.)
To develop Terry's idea a bit more explicitly, ISTM that during early development, you use Python 3 with annotations in file. Then when you need to test Python 2, you use a tool (as yet to be developed) which generates a Python file that has had all annotations stripped, and a stub file[1]. Throw away the stub file, test the Python file, and return to development.
For distribution (to *both* Python 3 and Python 2 users) you ignore the original annotated code and distribute the generated Python file and the stubs file. Probably there would be a tool for combining the Python and stub files back into an annotated Python file.
The two tools (stripper and annotator) can be very accurate (although the annotator may not produce precisely the style desired by users, the tools probably can be designed so that the re-annotated file accurately reflects the style used by the authors).
Is the implied workflow really so burdensome? Especially to downstream?
Note that Terry works on IDLE, so it seems likely that at least one Python IDE would support this workflow very well. :-)
Footnotes: [1] Yes, I know, the stub file is actually Python, too. You know what I mean.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- - Jim jim.baker@{colorado.edu|python.org|rackspace.com|zyasoft.com} twitter.com/jimbaker github.com/jimbaker bitbucket.com/jimbaker

Le 20/08/2014 23:28, Jim Baker a écrit :
Mypy’s approach to parameterizing types using slice notation/|__getitem__| is a rather neat hack, given how it does not require any new notation, matches rather similar syntax in languages like Scala and Haskell, and can be readily added to existing types, whether builtins like |list|, collections like |deque|, or ABC collection types like |Iterator| and|Sequence|. Adding such parameterization support to builtin/stdlib types is a compelling reason for including in 3.5. An enthusiastic +1!
I'm -1 on using __getitem__ for parametering types. It is indeed a hack and its limitations will bite us harshly when wanting to refine the type descriptions (for example to describe a constraint on a collection's size).
One last thing: let’s not add |List|, |TList|, etc. Just use |list| and other existing types to generate new types.
I'm -1 on that, for exactly the same reason as above. Regards Antoine.

On 21 August 2014 23:23, Antoine Pitrou <antoine@python.org> wrote:
Le 20/08/2014 23:28, Jim Baker a écrit :
Mypy’s approach to parameterizing types using slice notation/|__getitem__| is a rather neat hack, given how it does not
require any new notation, matches rather similar syntax in languages like Scala and Haskell, and can be readily added to existing types, whether builtins like |list|, collections like |deque|, or ABC collection types like |Iterator| and|Sequence|. Adding such
parameterization support to builtin/stdlib types is a compelling reason for including in 3.5. An enthusiastic +1!
I'm -1 on using __getitem__ for parametering types. It is indeed a hack and its limitations will bite us harshly when wanting to refine the type descriptions (for example to describe a constraint on a collection's size).
Given: Sequence[Number] Sequence[Number|None] There's still at least three options for extending the syntax even further: Sequence[Number](EXPR) Sequence[Number:EXPR] Sequence[Number,EXPR] You'd probably want to use strings at that point, for the same reasons PyContracts does. For example: Number,"N>0" # Positive number Sequence[Number,"N>0"] # Sequence of positive numbers Sequence[Number],"N>0" # Non-empty sequence I doubt we'll ever get to that point, though. Type hinting and runtime assertions aren't the same thing.
One last thing: let’s not add |List|, |TList|, etc. Just use |list| and
other existing types to generate new types.
I'm -1 on that, for exactly the same reason as above.
I'm also -1, on the grounds that concrete types and ABCs are different, and I think type hinting should be heavily biased towards ABCs. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Fri, Aug 22, 2014 at 2:15 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
I'm also -1, on the grounds that concrete types and ABCs are different, and I think type hinting should be heavily biased towards ABCs.
We keep seeing this line of argument coming up, and while I've only been skimming, I haven't seen anyone comment that this applies to parameters but not so much to return values. A function might take a Sequence and iterate over it, and then return a concrete integer. Or it might take a file-like object and a string, and yield a series of strings. Or it might take some set of parameters, and return None unconditionally. There's nothing wrong with being concrete about return values. ChrisA

On 22 August 2014 02:21, Chris Angelico <rosuav@gmail.com> wrote:
On Fri, Aug 22, 2014 at 2:15 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
I'm also -1, on the grounds that concrete types and ABCs are different, and I think type hinting should be heavily biased towards ABCs.
We keep seeing this line of argument coming up, and while I've only been skimming, I haven't seen anyone comment that this applies to parameters but not so much to return values. A function might take a Sequence and iterate over it, and then return a concrete integer. Or it might take a file-like object and a string, and yield a series of strings. Or it might take some set of parameters, and return None unconditionally. There's nothing wrong with being concrete about return values.
For standalone functions, I agree. For methods and generics, I'd still be somewhat wary of encouraging it. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Le 21/08/2014 12:15, Nick Coghlan a écrit :
Given:
Sequence[Number] Sequence[Number|None]
There's still at least three options for extending the syntax even further:
Sequence[Number](EXPR) Sequence[Number:EXPR] Sequence[Number,EXPR]
You'd probably want to use strings at that point, for the same reasons PyContracts does. For example:
Number,"N>0" # Positive number Sequence[Number,"N>0"] # Sequence of positive numbers Sequence[Number],"N>0" # Non-empty sequence
All of those are horrible and un-Pythonic, though. Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I doubt we'll ever get to that point, though. Type hinting and runtime assertions aren't the same thing.
It's less about defining all those use cases up front and more about not painting ourselves in a corner, though. Regards Antoine.

On 22 Aug 2014 03:27, "Antoine Pitrou" <antoine@python.org> wrote:
Le 21/08/2014 12:15, Nick Coghlan a écrit :
Given:
Sequence[Number] Sequence[Number|None]
There's still at least three options for extending the syntax even
further:
Sequence[Number](EXPR) Sequence[Number:EXPR] Sequence[Number,EXPR]
You'd probably want to use strings at that point, for the same reasons PyContracts does. For example:
Number,"N>0" # Positive number Sequence[Number,"N>0"] # Sequence of positive numbers Sequence[Number],"N>0" # Non-empty sequence
All of those are horrible and un-Pythonic, though. Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I don't think it's possible to have "Pythonic" design by contract (PyContracts makes a laudable effort, but I think its decorator based syntax is cleaner than its annotation based one) Decorators aren't going anywhere, so I'm comfortable with the idea of complexity constraints on the type hint notation - anything that doesn't fit into the type hint syntax can still go in a decorator, and get a handy decorator name for readers to look up in the docs or on Google. Cheers, Nick.

Le 21/08/2014 20:40, Nick Coghlan a écrit :
On 22 Aug 2014 03:27, "Antoine Pitrou" <antoine@python.org <mailto:antoine@python.org>> wrote:
Le 21/08/2014 12:15, Nick Coghlan a écrit :
Given:
Sequence[Number] Sequence[Number|None]
There's still at least three options for extending the syntax even
further:
Sequence[Number](EXPR) Sequence[Number:EXPR] Sequence[Number,EXPR]
You'd probably want to use strings at that point, for the same reasons PyContracts does. For example:
Number,"N>0" # Positive number Sequence[Number,"N>0"] # Sequence of positive numbers Sequence[Number],"N>0" # Non-empty sequence
All of those are horrible and un-Pythonic, though. Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I don't think it's possible to have "Pythonic" design by contract
We're not talking about design by contract, we're talking about type annotations. "A list of X integers" isn't exactly an extremely complicated notion. We shouldn't need weird convoluted syntaxes to express it. Regards Antoine.

On 08/21/2014 06:24 PM, Antoine Pitrou wrote:
Le 21/08/2014 20:40, Nick Coghlan a écrit :
On 22 Aug 2014 03:27, "Antoine Pitrou" <antoine@python.org <mailto:antoine@python.org>> wrote:
Le 21/08/2014 12:15, Nick Coghlan a écrit :
Given:
Sequence[Number] Sequence[Number|None]
There's still at least three options for extending the syntax even
further:
Sequence[Number](EXPR) Sequence[Number:EXPR] Sequence[Number,EXPR]
You'd probably want to use strings at that point, for the same reasons PyContracts does. For example:
Number,"N>0" # Positive number Sequence[Number,"N>0"] # Sequence of positive numbers Sequence[Number],"N>0" # Non-empty sequence
All of those are horrible and un-Pythonic, though. Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I don't think it's possible to have "Pythonic" design by contract
We're not talking about design by contract, we're talking about type annotations. "A list of X integers" isn't exactly an extremely complicated notion. We shouldn't need weird convoluted syntaxes to express it.
I don't know for whom I am arguing and against whom I am arguing, I just want to remind us that a type can be quite a bit more complicated than int or str: class Counting(int): def __new__(cls, value): if value < 1: raise ValueError('Counting can only be positive') ... -- ~Ethan~

First, sorry for my (probably) bad english :) I think that everyone who wants to learn about types, type system, what is static type, what is not, why function annotations (or some other, but I prefer it) refering to types is being proposed, should learn a little bit (you won't regret!) 1) This article (What to know before debating type systems) has been sent before in this thread (I think) but is a must read: http://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/ 2) Bob's talk in Europython last month "What can python learn from haskell" at least until minute 20, that talks about this things. http://pyvideo.org/video/3046/what-can-python-learn-from-haskell 3) If you love maths, some book of Benjamin C. Pierce (about type systems, of course!), this three are so good (but hard). Ordered from low to high level. - Type Systems for programming languages. - Types for programming languages. - Advanced topics in Types and programming languages. 4) If want to learn more, search in google, there are so much books/articles about types, and if you can learn about haskell types (or Idris if you are crazy), better ;) As last word, I want to give my opinion about the proposal. I like the proposal, and I would love to see it in Python 3.x, I don't think that function notations can have a better purpose. Of course, what is being proposed won't be needed to run a python program, and you will still can write your function notations whatever you want, but have a standard proposal will help a lot to build tools that read the function notations (like IDEs, linters, static type checkers, ...) About the syntax, I love the mypy's syntax, and remember me a lot to other languages like scala. def primes_to_n(n: Int) => List[Int] def primes_to_n(n: int) -> List[int] This two function definitions are a function that get an int and return a list of integers. First definition is Scala, second one is Python with mypy's syntax. I like much more the Haskell syntax, but is another world and I think that python shouldn't have a syntax like that: primes_to_n :: Int -> [Int] About other syntax that I had seen in this thread: - decorators: I really don't like that one, is compatible with python 2, ok, but looks so bad (to me) - docs: That's not bad at all, and can still exist with the typing syntax to declare the types, but we have function annotations where the types are much close to the definition, and that's definitively good. - PyContracts: That's not bad at all, but just for check types in runtime, much things (the main diferences with mypy) can't be done in static, for example, how you know if this will work *before* execute it: @contract def f(x: 'list[3>](str)') -> 'list(str)' f(input().split(" ")) That's a function that take a list of three or more strings, and is called with the user input. You can't know if that list will be over three elements if you don't run it and get the input, and even those, you can't say that will be true all the times you execute it. That's just my opinion :) Cheers! -- Miguel García Lafuente - Rock Neurotiko Do it, the devil is in the details. The quieter you are, the more you are able to hear. Happy Coding. Code with Passion, Decode with Patience. If we make consistent effort, based on proper education, we can change the world. El contenido de este e-mail es privado, no se permite la revelacion del contenido de este e-mail a gente ajena a él.

On 8/22/2014 3:47 AM, Rock Neurotiko wrote:
About the syntax, I love the mypy's syntax, and remember me a lot to other languages like scala.
def primes_to_n(n: Int) => List[Int]
def primes_to_n(n: int) -> List[int]
The Python version need ':' at the end, after the return type. I don't think you are the first to omit it. -- Terry Jan Reedy

Yes, and the Scala version need a " = {}", I was talking just about how the syntax of the types and annotations are, I omitted the ':' because it was irrelevant. 2014-08-23 1:36 GMT+02:00 Terry Reedy <tjreedy@udel.edu>:
On 8/22/2014 3:47 AM, Rock Neurotiko wrote:
About the syntax, I love the mypy's syntax, and remember me a lot to
other languages like scala.
def primes_to_n(n: Int) => List[Int]
def primes_to_n(n: int) -> List[int]
The Python version need ':' at the end, after the return type. I don't think you are the first to omit it.
-- Terry Jan Reedy
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Miguel García Lafuente - Rock Neurotiko Do it, the devil is in the details. The quieter you are, the more you are able to hear. Happy Coding. Code with Passion, Decode with Patience. If we make consistent effort, based on proper education, we can change the world. El contenido de este e-mail es privado, no se permite la revelacion del contenido de este e-mail a gente ajena a él.

On 22.08.2014 03:24, Antoine Pitrou wrote:
Le 21/08/2014 20:40, Nick Coghlan a écrit :
On 22 Aug 2014 03:27, "Antoine Pitrou" <antoine@python.org <mailto:antoine@python.org>> wrote:
Le 21/08/2014 12:15, Nick Coghlan a écrit :
Given:
Sequence[Number] Sequence[Number|None]
There's still at least three options for extending the syntax even
further:
Sequence[Number](EXPR) Sequence[Number:EXPR] Sequence[Number,EXPR]
You'd probably want to use strings at that point, for the same reasons PyContracts does. For example:
Number,"N>0" # Positive number Sequence[Number,"N>0"] # Sequence of positive numbers Sequence[Number],"N>0" # Non-empty sequence
All of those are horrible and un-Pythonic, though. Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I don't think it's possible to have "Pythonic" design by contract
We're not talking about design by contract, we're talking about type annotations. "A list of X integers" isn't exactly an extremely complicated notion. We shouldn't need weird convoluted syntaxes to express it.
I agree with Antoine. Those EXPRs should not be needed for type annotations. The above is mixing type information with runtime information. All a linter needs to know is that the result of operation1(type1) is of type2, i.e. the information about certain features of those types is implicit in the type name and the information about whether types can be adapted to each other. If the linter then finds that the result of operation1() is being used as input for operation2(), which only supports type1 arguments, and it doesn't have information about how to adapt type2 to type1, it comes back with an error message. It's all abstract. Trying to conflate arbitrary type properties into the syntax is not needed - after all, type annotations are not meant for runtime checks. Regarding the whole idea of inline type annotations, I'm at most +0 on those. They add more complexity to the language and make it less readable for little added value. Making the type annotations external is much better in this respect and I'm sure IDEs could even help with this by providing nice interfaces for adding the type information. They could semi- automate the process and make suggestions based on the functions used inside the functions/method body. Now, since these tools would have to read and write the annotations programmatically, the syntax does not have to be nicely readable or Pythonic. It could just as well be a YAML/JSON/XML file. But hey, I don't want to spoil all the fun in defining a little language for type annotations, so please do continue to have fun defining Python .pyh header files ;-) Cheers, -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Aug 22 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-09-19: PyCon UK 2014, Coventry, UK ... 28 days to go 2014-09-30: Python Meeting Duesseldorf ... 39 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On Thu, Aug 21, 2014 at 09:23:39AM -0400, Antoine Pitrou wrote:
I'm -1 on using __getitem__ for parametering types. It is indeed a hack and its limitations will bite us harshly when wanting to refine the type descriptions (for example to describe a constraint on a collection's size).
One last thing: let’s not add |List|, |TList|, etc. Just use |list| and other existing types to generate new types.
I'm -1 on that, for exactly the same reason as above.
It's not 100% clear to me whether you're -1 on re-using |list| or -1 or adding |List|. I'm going to assume that you mean -1 on re-using list (see below). In a later message, Antoine wrote:
Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I wouldn't say it is alien. abc[xyz] is clearly Python syntax. Nor would I necessarily say it is inferior, although it is more limited. Deliberately so, I think. How complex will the static type declarations need to be for us to want to accept arbitrary keyword arguments, for example? I think limiting the syntax possible is a good thing, it will discourage people from trying to declare arbitrarily complex information in the type notations. But another reason for disliking call syntax for this is that it gets confusing: def function(param:Sequence(Spam, Eggs))->Mapping(Cheese, Toast): suffers compared to this: def function(param:Sequence[Spam, Eggs])->Mapping[Cheese, Toast]: In the second case, the type annotations stand out more easily because they use square brackets instead of round. As far as the question of list versus List, I don't believe we can use built-ins if we follow Antoine's suggestion of call syntax. def function(param:list(int))->list(str): for example, intends to declare param is a list of ints. But list(x) already has a meaning, and if x is an int, it raises TypeError. param:List(int) at least can work, but now consider int. Perhaps we might like to specify a range of ints, from 0 to 9 (say). The obvious syntax would be: param:List(int(0, 10)) # inclusive lower bound, exclusive upper bound but again that already has meaning. If we want the flexibility of providing arguments to scalar types like int, I believe we need to use subscripting syntax: param:List[int[0, 10]] -1 on re-using call syntax: it is more confusing to read inside a parameter list, too flexible, and clashes with existing use calling types. +1 on subscripting syntax: it is more easily distinguishable from calling, just flexible enough, and doesn't clash with existing functionality. -- Steven

Le 22/08/2014 21:36, Steven D'Aprano a écrit :
It's not 100% clear to me whether you're -1 on re-using |list| or -1 or adding |List|. I'm going to assume that you mean -1 on re-using list (see below).
Indeed.
In a later message, Antoine wrote:
Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I wouldn't say it is alien. abc[xyz] is clearly Python syntax.
But it is completely uncommon for anything else than subscripting and indexing containers. Python isn't known for reusing operators for completely unrelated things, e.g. it doesn't overload ">>" for I/O (thank goodness).
How complex will the static type declarations need to be for us to want to accept arbitrary keyword arguments, for example? I think limiting the syntax possible is a good thing, it will discourage people from trying to declare arbitrarily complex information in the type notations.
I think this is an incredibly weak argument, and the proof is that it hasn't been brought for anything else in the stdlib. Why the typing module should be any different and use a deliberately repressive parameter-passing syntax is beyond me - even though it's an advanced, optional, feature and will be put in the hands of consenting adults.
But another reason for disliking call syntax for this is that it gets confusing:
def function(param:Sequence(Spam, Eggs))->Mapping(Cheese, Toast):
suffers compared to this:
def function(param:Sequence[Spam, Eggs])->Mapping[Cheese, Toast]:
If you find it confusing (which may simply be a matter of habit, I don't know), you can simply alias the types: spam_eggs_sequence = Sequence(Spam, Eggs) cheese_toast_mapping = Mapping(Cheese, Toast) def function(param: spam_eggs_sequence) -> cheese_toast_mapping: ... Regardless of readability, I would expect people to create such aliases anyway, the same way people tend to create "typedefs" in other languages, because it helps condense and clarify which actual types are in use in a module.
As far as the question of list versus List, I don't believe we can use built-ins if we follow Antoine's suggestion of call syntax.
def function(param:list(int))->list(str):
for example, intends to declare param is a list of ints. But list(x) already has a meaning, and if x is an int, it raises TypeError.
And indeed this is exactly why I said I was against using built-ins for this (or the existing ABCs) :-)) Really, for it to be powerful enough, the type description system has to use its own set of classes. It can't try to hack away existing builtins and ABCs in the hope of expressing different things with them, or it *will* hit a wall some day (and, IMO, sooner rather than later). Regards Antoine.

On Fri, Aug 22, 2014 at 10:31:18PM -0400, Antoine Pitrou wrote:
Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I wouldn't say it is alien. abc[xyz] is clearly Python syntax.
But it is completely uncommon for anything else than subscripting and indexing containers.
And function call syntax is completely uncommon for anything else than calling functions (including types and methods). One way or the other, we're adding a new use, type declarations.
Python isn't known for reusing operators for completely unrelated things, e.g. it doesn't overload ">>" for I/O (thank goodness).
I see you've forgotten Python 2's print >>sys.stderr, x, y, z :-)
How complex will the static type declarations need to be for us to want to accept arbitrary keyword arguments, for example? I think limiting the syntax possible is a good thing, it will discourage people from trying to declare arbitrarily complex information in the type notations.
I think this is an incredibly weak argument, and the proof is that it hasn't been brought for anything else in the stdlib.
We frequently reject syntax because it will be (allegedly) confusing or hard to read. E.g. try...except expressions, anonymous code blocks, multiline lambda. Especially multiline lambda. Even something which I think is a no-brainer, allowing the with statement to use parentheses around the context managers: with (this_manager() as m1, that_manager() as m2): ... was criticised by Nick on the basis that if you can't fit the context managers all on one line, you ought to refactor your code. I'm sure if you check the rejected PEPs and Python-Ideas archives, you'll find many examples of my argument applied to the standard library. I don't think it's unPythonic to argue that type hinting syntax should by default use an annotation style which is not arbitrarily complex. The annotations themselves will remain arbitrary Python expressions, so if you really need a complex type declaration, you can create a helper and call it with whatever signature you like.[1]
Why the typing module should be any different and use a deliberately repressive parameter-passing syntax is beyond me - even though it's an advanced, optional, feature and will be put in the hands of consenting adults.
I can't speak for the author of mypy, Jukka Lehtosalo, but for me, I think the answer is that type declarations make a *mini-language*, not full-blown Python. It is unclear to me just how powerful the type language will be, but surely we don't expect it to evaluate arbitrarily complex Python expressions at compile time? Do we? I find it unlikely that we expect a static linter to make sense of this: def bizarre(param:int if random.random() > 0.5 else str)->float: except to say it returns a float and takes who-knows-what as argument. An extreme case, I grant, but I expect that there are going to be limits to what can be checked at compile time based on static analysis. I admit that I don't fully understand all the implications of static versus runtime type checking, but it doesn't seem unreasonable to expect static declarations to be considerably simpler than what can be expressed at runtime. Hence, a simpler mini-language is appropriate.
Really, for it to be powerful enough, the type description system has to use its own set of classes. It can't try to hack away existing builtins and ABCs in the hope of expressing different things with them, or it *will* hit a wall some day (and, IMO, sooner rather than later).
How powerful is "powerful enough"? Powerful enough for what? You've probably heard of the famous "the compiler found my infinite loop", where the ML type checker was able to detect that code would never terminate. I find that remarkable, and even more astonishing that, according to some, solving the halting problem is "nothing special" for type systems.[2] But many languages make do with less powerful type systems, and tools such as IDEs and editors surely don't need something that powerful. So: - how powerful do we expect the type system to be? - and how much of that power needs to be expressed using function annotations? The second question is critical, because there are alternatives to function annotations: decorators, docstring annotations, and external stub files. [1] Although if you do so, it is not clear to me how much the static checker will be able to use it. [2] http://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/ -- Steven

On Fri, Aug 22, 2014 at 10:13 PM, Steven D'Aprano <steve@pearwood.info> wrote:
You've probably heard of the famous "the compiler found my infinite loop", where the ML type checker was able to detect that code would never terminate. I find that remarkable, and even more astonishing that, according to some, solving the halting problem is "nothing special" for type systems.[2] But many languages make do with less powerful type systems, and tools such as IDEs and editors surely don't need something that powerful. --snip-- [2] http://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/
You seem to be mischaracterizing ML's type system as something magically powerful. The ML type checker can be implemented in a few dozen lines of Python code, and resolved efficiently. ML does type inference by looking at the constraints on arguments inside the body of a function. Infinite recursion often accidentally results in you omitting constraints that you didn't meant to. For example, "def foo(): return foo()" will be straight up marked as not returning at all, because no additional constraints are placed on the return value, which can only mean that it recurses infinitely. Many infinite loops will not be marked, though. It's a type system, not magic. (The ML type system is actually substantially simpler than anything one is likely to come up with to describe Python, because ML didn't have an object system or structural subtyping.) As for the comment that this is "nothing special", taken literally, they said something wrong -- solving the halting problem for Turing complete languages is impossible, static types or no. The only way static types can help is by allowing you to define a (sub-)language that is not Turing-complete. I think they were alluding to the fact that, counter to many peoples' intuition, creating a type system that allows you to detect some of the code that halts isn't a matter of creating a powerful type system. If you add even the simplest type system to the lambda calculus, poof, now every well-typed expression can only be "executed" finitely many steps! As for whether IDEs need to make something complicated enough that it can positively identify code that does or doesn't halt, I'd leave that to the IDE folks. C and Java tools do this sort of thing all the time, for things like detecting dead code. Why shouldn't Python tools? (This isn't a feature of the type system so much as control flow analysis under a given type system, so you're right that it doesn't belong in the types themselves.) -- Devin

Le 23/08/2014 01:13, Steven D'Aprano a écrit :
On Fri, Aug 22, 2014 at 10:31:18PM -0400, Antoine Pitrou wrote:
Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I wouldn't say it is alien. abc[xyz] is clearly Python syntax.
But it is completely uncommon for anything else than subscripting and indexing containers.
And function call syntax is completely uncommon for anything else than calling functions (including types and methods). One way or the other, we're adding a new use, type declarations.
It's not a new use. A type class is a class, and calling it is just instantiating that class. There's nothing new here. If you think that's a bit "meta", it's no different than e.g. higher-order functions.
Python isn't known for reusing operators for completely unrelated things, e.g. it doesn't overload ">>" for I/O (thank goodness).
I see you've forgotten Python 2's
print >>sys.stderr, x, y, z
:-)
I always forget about it, but the fact that we finally made print() a function in Python 3 actually supports my point: we had a special case, and we had to remove it - breaking compatibility in the process - because it was impeding on extensibility (adding keyword-only arguments) and flexibility (swapping out print() with another function). "The brackets looks better" is a misguided argument, the same way the idea that "print" was cuter as a non-parenthetical statement (and that we wouldn't need the power of a regular function call; eventually we *did* need it) was a misguided argument.
The annotations themselves will remain arbitrary Python expressions, so if you really need a complex type declaration, you can create a helper and call it with whatever signature you like.[1]
I don't think you understand what this discussion is about. It isn't only about annotations (they are only the channel used to convey the information), it is about standardizing a typing system in the stdlib - and, therefore, accross the Python community. I want the *default* typing system to be extensible (not by me, but by the community, because use cases for that *will* arise). I would want the same thing even if the proposal was to use something else than annotation to convey type descriptions.
I can't speak for the author of mypy, Jukka Lehtosalo, but for me, I think the answer is that type declarations make a *mini-language*, not full-blown Python.
No, the answer is that mypy wants to support runtime instantiations of its type descriptions. It wants to allow you to write e.g. "List[Int]()" to instantiate a regular Python list. While that's one of mypy's design points, though (because mypy ultimately seems to aim at becoming a separate Python dialect), it's not desired for regular Python.
It is unclear to me just how powerful the type language will be, but surely we don't expect it to evaluate arbitrarily complex Python expressions at compile time? Do we?
Why wouldn't it? Ideally, "compile time" is just the import of the module. (yes, some libraries are ill-behaved on import; they break pydoc and the like; I'd say it's their problem)
I find it unlikely that we expect a static linter to make sense of this:
def bizarre(param:int if random.random() > 0.5 else str)->float:
Yeah... so what, exactly? Do you also criticize Python because it allows you to write absurd code? Does it make Python "too powerful"?
So:
- how powerful do we expect the type system to be?
This is a bad question to ask. That's like asking "how powerful does a function or decorator need to be?" The entire point of devising new language or library tools is to enable use cases that we *don't know about yet*. I'm not the only one pointing this out. Other people have also come with examples where they'd like to make the type system more powerful. And that's right now. The future will see more interesting propositions, the same way decorators grew to a mechanism powering very diverse and sophisticated use cases. Saying "I don't want to make this functionality powerful because I can't think of powerful use cases" is the mentality that leads to things such as PHP (closures? functions as objects? "why would I need that to write Web pages?").
- and how much of that power needs to be expressed using function annotations?
The second question is critical, because there are alternatives to function annotations: decorators, docstring annotations, and external stub files.
Why would those other channels use a type description syntax different from the one used in function annotations? That would be crazy. You seem to think that a type system should simply be some kind of textual description. It's not. It's a set of objects (or classes) with some behaviour attached to them; that's why it uses Python syntax. Because it *is* Python code. Regards Antoine.

On Sat, Aug 23, 2014 at 09:44:02AM -0400, Antoine Pitrou wrote:
Le 23/08/2014 01:13, Steven D'Aprano a écrit :
On Fri, Aug 22, 2014 at 10:31:18PM -0400, Antoine Pitrou wrote:
Python has one of the most powerful and user-friendly function call syntaxes around, why reinvent something clearly inferior and alien?
I wouldn't say it is alien. abc[xyz] is clearly Python syntax.
But it is completely uncommon for anything else than subscripting and indexing containers.
And function call syntax is completely uncommon for anything else than calling functions (including types and methods). One way or the other, we're adding a new use, type declarations.
It's not a new use. A type class is a class, and calling it is just instantiating that class. There's nothing new here. If you think that's a bit "meta", it's no different than e.g. higher-order functions.
There's no instantiation during *static* analysis, because the code hasn't run yet. I don't think it's productive to argue about what's new and what isn't. I think it is more important to discuss what appears to me to be a problem with your suggestion that annotations use call syntax rather than subscript syntax. Here's an annotation: param:Sequence[int] or as you prefer: param:Sequence(int) There's two problems with the later, as I see it. Of course, during static analysis, we can use any syntax we like, but Python annotations are also evaluated at runtime. (1) Sequence is an ABC, and like all abstract classes, you're not supposed to be able to instantiate it: py> Sequence() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class Sequence with abstract methods __getitem__, __len__ For your proposal to be accepted, that would have to change. I think that is a problem. At the very least, it's a backwards-incompatibility, even within Python 3.x. (2) Even if we allow instantiating Sequence, and returning a type (rather than an instance of Sequence), I don't think that Sequence should accept arguments that concrete subclasses would accept: py> list(int) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'type' object is not iterable So we would have to allow list() to accept type arguments, and return a type, as well as the usual iterable arguments. That strikes me as messy, ugly and confusing. I would much prefer keeping call syntax on a type to instantiate the type, so that list(obj) either fails or returns a list. list[int] can return anything needed, it doesn't have to be an instance of list.
"The brackets looks better" is a misguided argument, the same way the idea that "print" was cuter as a non-parenthetical statement (and that we wouldn't need the power of a regular function call; eventually we *did* need it) was a misguided argument.
You're entitled to your opinion, but in my opinion, trying to avoid confusing, hard to read syntax is never misguided. Overloading call syntax to do two things (instantiate types, and type annotations), and placing such calls in the function parameter list which already has parens, risks creating hard to read, ugly code. I think that using [] helps the annotations stand out, and I think that allowing the full function call syntax complete with keyword-only arguments inside type annotations is a case of YAGNI as well as confusing and hard to read. That's my opinion; yours may differ. In any case, I think this is a minor point, and would rather not get into arguments about aesthetics. Ultimately, Guido will decide which one he prefers the look of.
The annotations themselves will remain arbitrary Python expressions, so if you really need a complex type declaration, you can create a helper and call it with whatever signature you like.[1]
I don't think you understand what this discussion is about. It isn't only about annotations (they are only the channel used to convey the information), it is about standardizing a typing system in the stdlib - and, therefore, accross the Python community.
I think you've got that exactly backwards. Guido's original post seemed clear to me that this proposal was *not* about adding a typing system to CPython, at least not now, but *only* about standardizing on the syntax used. A couple of quotes from his original post: [GvR] The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module. [...] The curious thing here is that while standardizing a syntax for type annotations, we technically still won't be adopting standard rules for type checking. This is intentional.
It is unclear to me just how powerful the type language will be, but surely we don't expect it to evaluate arbitrarily complex Python expressions at compile time? Do we?
Why wouldn't it? Ideally, "compile time" is just the import of the module. (yes, some libraries are ill-behaved on import; they break pydoc and the like; I'd say it's their problem)
When I say compile-time, I mean when the module is compiled to byte code, before it is executed or imported.
- how powerful do we expect the type system to be?
This is a bad question to ask. That's like asking "how powerful does a function or decorator need to be?" The entire point of devising new language or library tools is to enable use cases that we *don't know about yet*.
Decorators are an excellent example. Have you noticed how limited the syntax for decorators are? Probably not, because you can do a lot with decorators even though the syntax is deliberately limited. But limited it is. For instance, you can't use decorator syntax with an index lookup: py> @decorators[0] File "<stdin>", line 1 @decorators[0] ^ SyntaxError: invalid syntax or more than one call: py> @decorate(x)(y) File "<stdin>", line 1 @decorate(x)(y) ^ SyntaxError: invalid syntax (Both examples in 3.3; 3.4 may differ.) Does this limit the power of decorators? No, of course not. The sorts of things decorators can do is only lightly constrained by the restrictions on syntax, and I believe that the same will apply to type annotations. [...]
- and how much of that power needs to be expressed using function annotations?
The second question is critical, because there are alternatives to function annotations: decorators, docstring annotations, and external stub files.
Why would those other channels use a type description syntax different from the one used in function annotations? That would be crazy.
Because function annotations are limited to a single expression, while the others are not. You're talking about me wanting to limit the power of the static type system, but I'm not really. I'm just wanting to limit how much clutter ends up inside the function parameter list. Not every arbitrarily complex type description needs to be written as a single expression inside the parameter list. Static analysis tools can make use of: * function annotations; * class definitions outside of the function (e.g. ABCs); * decorators; * docstring annotations; * stub files; * type inference on variables; * even inline comments (as mypy does) Guido's proposal is only about the first, and possibly by implication the second. The rest aren't going away, nor are they being standardised yet. There's plenty of opportunity for future tools to develop.
You seem to think that a type system should simply be some kind of textual description. It's not. It's a set of objects (or classes) with some behaviour attached to them; that's why it uses Python syntax. Because it *is* Python code.
That may be true about *runtime* typing, but it is not true for *static* typing since that occurs before the Python code runs. You're absolutely correct that there are other uses for type annotations apart from static type checking, and if you go back over my previous posts you'll see I've made a number of references to dynamic runtime behaviour. Ethan even (politely) told me off for forgetting the static part of the proposal, when he thought I was emphasising the runtime implications too much. For you now to think I've forgotten the runtime implications is ironic :-) -- Steven

Le 23/08/2014 13:25, Steven D'Aprano a écrit :
It's not a new use. A type class is a class, and calling it is just instantiating that class. There's nothing new here. If you think that's a bit "meta", it's no different than e.g. higher-order functions.
There's no instantiation during *static* analysis, because the code hasn't run yet.
In your idea of "static analysis", it hasn't. Because you think it should involve some kind of separate syntax analysis tool that has nothing to do with regular Python. But Python is powerful enough to let you do that using normal introspection of modules. And it's *exactly* how we are exposing function annotations (and also docstrings, etc.): using runtime-accessible introspection information which is gathered by importing modules and therefore actually *executing* toplevel module code. Not merely compiling it. The rest of your message I'm not motivated to respond to, sorry. Regards Antoine.

On 08/23/2014 04:00 PM, Antoine Pitrou wrote:
Le 23/08/2014 13:25, Steven D'Aprano a écrit :
It's not a new use. A type class is a class, and calling it is just instantiating that class. There's nothing new here. If you think that's a bit "meta", it's no different than e.g. higher-order functions.
There's no instantiation during *static* analysis, because the code hasn't run yet.
In your idea of "static analysis", it hasn't. Because you think it should involve some kind of separate syntax analysis tool that has nothing to do with regular Python. But Python is powerful enough to let you do that using normal introspection of modules.
And it's *exactly* how we are exposing function annotations (and also docstrings, etc.): using runtime-accessible introspection information which is gathered by importing modules and therefore actually *executing* toplevel module code. Not merely compiling it.
Is this workable? If I have a nested class in a function: def spam(): class fribble: def __init__(self, x:int): blahblah I would expect MyPy to be able to retrieve and use the annotations from fribble without actually calling spam. If static checking is only available at the top level I think a lot of power/usefulness is gone. -- ~Ethan~

Le 24/08/2014 19:59, Ethan Furman a écrit :
Is this workable? If I have a nested class in a function:
def spam(): class fribble: def __init__(self, x:int): blahblah
I would expect MyPy to be able to retrieve and use the annotations from fribble without actually calling spam.
I don't know. But that's not really the point, because you are still able to do, say: my_type = some_invocation(...) def spam(): class fribble: def __init__(self, x: my_type): blahblah ... as far as the type checker uses module imports, that is. Of course, /after/ importing the module it can also walk the bytecode defined in that module. Regards Antoine.

On Sun, Aug 24, 2014 at 04:59:02PM -0700, Ethan Furman wrote:
Is this workable? If I have a nested class in a function:
def spam(): class fribble: def __init__(self, x:int): blahblah
I would expect MyPy to be able to retrieve and use the annotations from fribble without actually calling spam. If static checking is only available at the top level I think a lot of power/usefulness is gone.
That's entirely up to Mypy or whatever linter, compiler, editor or other tool being used, and has nothing to do with this proposal, which is only to standardize on the syntax. I would expect different static tools would have different levels of ability to deal with dynamically created classes like that. -- Steven

On Sat, Aug 23, 2014 at 07:00:54PM -0400, Antoine Pitrou wrote:
Le 23/08/2014 13:25, Steven D'Aprano a écrit :
It's not a new use. A type class is a class, and calling it is just instantiating that class. There's nothing new here. If you think that's a bit "meta", it's no different than e.g. higher-order functions.
There's no instantiation during *static* analysis, because the code hasn't run yet.
In your idea of "static analysis", it hasn't. Because you think it should involve some kind of separate syntax analysis tool that has nothing to do with regular Python.
That's not "my" idea of static analysis, that is the standard definition of "static" as happening at compile-time. If it happens at runtime, it's not static.
But Python is powerful enough to let you do that using normal introspection of modules.
This proposal isn't just about the Python interpreter, its also about static tools like linters, IDEs and editors.
And it's *exactly* how we are exposing function annotations (and also docstrings, etc.): using runtime-accessible introspection information which is gathered by importing modules and therefore actually *executing* toplevel module code. Not merely compiling it.
Correct, the annotations will be available at runtime as well as compile-time. But tools like linters and editors will rely on static analysis, not dynamic run-time checks, and that's Guido's intention. Here's his initial post: https://mail.python.org/pipermail/python-ideas/2014-August/028618.html -- Steven

Le 24/08/2014 21:30, Steven D'Aprano a écrit :
In your idea of "static analysis", it hasn't. Because you think it should involve some kind of separate syntax analysis tool that has nothing to do with regular Python.
That's not "my" idea of static analysis, that is the standard definition of "static" as happening at compile-time. If it happens at runtime, it's not static
No, that's the standard definition of "static" in a certain category of languages such as C. Python has "static methods" and they don't happen at compile-time: "staticmethod" is a regular callable which is invoked at runtime. And there are other precedents, such as RPython which is also "statically" typed but... using code loaded from imported modules (in other words, I'm not inventing anything new here).
This proposal isn't just about the Python interpreter, its also about static tools like linters, IDEs and editors.
Those so-called "static" tools are free to invoke the interpreter and use Python's *runtime* introspection facilities. And I'm sure some of them do. Really, it's amusing that I tried several times to explain you that "static" analysis didn't need to happen in an entirely separate evaluation context, and you still don't get it. You speak like some C programmer who has newly discovered Python and doesn't realize the magnitude of the differences between the two languages. Therefore I think the word "static" should be banned from this entire discussion, otherwise folks like you will keep associating irrelevant concepts with it. Regards Antoine.

On Mon, Aug 25, 2014 at 12:11 PM, Antoine Pitrou <antoine@python.org> wrote:
No, that's the standard definition of "static" in a certain category of languages such as C. Python has "static methods" and they don't happen at compile-time: "staticmethod" is a regular callable which is invoked at runtime.
"Static method" is a quite different meaning of static - they always execute at run-time. https://en.wikipedia.org/wiki/Method_(computer_programming)#Static_methods ChrisA

Le 24/08/2014 22:16, Chris Angelico a écrit :
On Mon, Aug 25, 2014 at 12:11 PM, Antoine Pitrou <antoine@python.org> wrote:
No, that's the standard definition of "static" in a certain category of languages such as C. Python has "static methods" and they don't happen at compile-time: "staticmethod" is a regular callable which is invoked at runtime.
"Static method" is a quite different meaning of static - they always execute at run-time.
Oh, really, do they? You missed the entire point. Python's static methods not only execute at runtime, they are *defined* at runtime. The compiler doesn't know that something will be a "static method", a regular function, a "class method"... or whatever. The meaning of "static" is entirely different from C's or Java's, where the information about static methods is (and has to be) known at compile time. Of course, this isn't limited to static methods. The same can be said about docstrings, which use separate analysis methods in languages such as C++ or Java (e.g. doxygen, javadoc...), but regular runtime introspection capabilities in Python. Regards Antoine.

This argument over the word "static" is getting ridiculous. The meaning of "static analysis" is perfectly well understood (just Google it) and using Python run-time introspection for the code being analyzed is exactly what you *don't* want in this case, for a variety of reasons. (And that includes doing the analysis as part of the byte-code compilation that may happen at import/load time. Once any part of the program is executing it's simply too late for the kind of analysis we're interested in.) People who are talking about what should happen if a decorator changes __annotations__ are likewise missing the point. (This doesn't mean that decorators aren't allowed to affect the signature. But the static analyzer has to use its static powers to figure out what the decorator does, or it has to be told, e.g. via a stub file.) Note that I am okay with being somewhat skittish about the term "static typing", given Python's long struggle for legitimacy in a world where professors everywhere used to equate static typing and strong typing and tell their impressionable students that it was the Right Way. We should probably avoid "static typing" and use "type hinting" instead (following TypeScript's lead I believe). But the process of looking for violations against the hinted types before the code run is still called static analysis. On Sun, Aug 24, 2014 at 7:11 PM, Antoine Pitrou <antoine@python.org> wrote:
Le 24/08/2014 21:30, Steven D'Aprano a écrit :
In your idea of "static analysis", it hasn't. Because you think it should involve some kind of separate syntax analysis tool that has nothing to do with regular Python.
That's not "my" idea of static analysis, that is the standard definition of "static" as happening at compile-time. If it happens at runtime, it's not static
No, that's the standard definition of "static" in a certain category of languages such as C. Python has "static methods" and they don't happen at compile-time: "staticmethod" is a regular callable which is invoked at runtime.
And there are other precedents, such as RPython which is also "statically" typed but... using code loaded from imported modules (in other words, I'm not inventing anything new here).
This proposal isn't just about the Python interpreter, its also about
static tools like linters, IDEs and editors.
Those so-called "static" tools are free to invoke the interpreter and use Python's *runtime* introspection facilities. And I'm sure some of them do.
Really, it's amusing that I tried several times to explain you that "static" analysis didn't need to happen in an entirely separate evaluation context, and you still don't get it. You speak like some C programmer who has newly discovered Python and doesn't realize the magnitude of the differences between the two languages.
Therefore I think the word "static" should be banned from this entire discussion, otherwise folks like you will keep associating irrelevant concepts with it.
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/
-- --Guido van Rossum (python.org/~guido)

Le 24/08/2014 22:30, Guido van Rossum a écrit :
People who are talking about what should happen if a decorator changes __annotations__ are likewise missing the point.
But you shouldn't use annotations for something that refuses to use runtime introspection abilities. There's no reason to waste annotations if the need can be fullfilled by separate description files in a DSL that doesn't even need to be Python code (but cutely looks like so). And it's not surprising that some people may be missing the point when a feature pretends to use a runtime facility but interprets it from a separate channel which eschews any code execution. Your proposal seems determined by the fact that mypy has much grander ambitions (and therefore requires its insertion into regular Python), but it doesn't make use of that power at all, worse, it forbids others to use it. Regards Antoine.

Le 24/08/2014 22:51, Antoine Pitrou a écrit :
Le 24/08/2014 22:30, Guido van Rossum a écrit :
People who are talking about what should happen if a decorator changes __annotations__ are likewise missing the point.
But you shouldn't use annotations for something that refuses to use runtime introspection abilities. There's no reason to waste annotations if the need can be fullfilled by separate description files in a DSL that doesn't even need to be Python code (but cutely looks like so).
(*) or a docstring-embedded DSL, as others acutely remarked. Regards Antoine.

On Sun, Aug 24, 2014 at 7:51 PM, Antoine Pitrou <antoine@python.org> wrote:
Le 24/08/2014 22:30, Guido van Rossum a écrit :
People who are talking about what should happen if a decorator changes
__annotations__ are likewise missing the point.
But you shouldn't use annotations for something that refuses to use runtime introspection abilities. There's no reason to waste annotations if the need can be fullfilled by separate description files in a DSL that doesn't even need to be Python code (but cutely looks like so).
It doesn't feel like a waste to me -- having it syntactically be part of the source code to me is a strong advantage over stubs or special comments or even special syntax in docstrings. As I have said several times now, it fulfills exactly the original (pre-PEP 3107) goal I had in mind for them.
And it's not surprising that some people may be missing the point when a feature pretends to use a runtime facility but interprets it from a separate channel which eschews any code execution.
There is no pretense here. It is simply useful to make the type annotations *also* available at run time.
Your proposal seems determined by the fact that mypy has much grander ambitions (and therefore requires its insertion into regular Python), but it doesn't make use of that power at all, worse, it forbids others to use it.
This remark about mypy's ambitions sounds delusional and paranoid. I wouldn't care about mypy at all if it wasn't right in line with my intentions and desires for Python. And while its author *originally* intended to create a whole new language (since that is what academics do :-), he gracefully changed his design (over a year ago) when I suggested that it might be more useful if it fit neatly into Python 3 annotations. Finally, I would actually be okay if we found a way to let type hints and other annotations coexist -- I just prefer type hints to be the default use, since I see them as much more generally useful than all other uses combined. -- --Guido van Rossum (python.org/~guido)

On 8/24/2014 11:12 PM, Guido van Rossum wrote:
Finally, I would actually be okay if we found a way to let type hints and other annotations coexist -- I just prefer type hints to be the default use, since I see them as much more generally useful than all other uses combined.
I believe co-existence is possible, but the details will depend on the form type hints take. First, other annotations should be easily distinguished from type hints. Second, other annotations that would interfere with the runtime use of __annotations__ should be pulled out by a decorator and possibly moved to another attribute specific to the decorator (such as '_deco_name'). -- Terry Jan Reedy

On 25 August 2014 07:18, Terry Reedy <tjreedy@udel.edu> wrote:
On 8/24/2014 11:12 PM, Guido van Rossum wrote:
Finally, I would actually be okay if we found a way to let type hints and other annotations coexist -- I just prefer type hints to be the default use, since I see them as much more generally useful than all other uses combined.
I believe co-existence is possible, but the details will depend on the form type hints take. First, other annotations should be easily distinguished from type hints. Second, other annotations that would interfere with the runtime use of __annotations__ should be pulled out by a decorator and possibly moved to another attribute specific to the decorator (such as '_deco_name').
… or we could have a decorator for type hints, and ascribe no new meaning at all to __annotations__. Or assume __annotations__ contains type hints iff all the annotations present are instances of typing.*. That might be better anyway, since a decorator could then add (or augment) type information without assigning to __annotations__ (which would be weird). Even if conveying type information is the most useful use of annotations, there's no reason it can't be explicit (and consistent with other uses of annotations). Ed Kellett

On Aug 25, 2014, at 6:20 AM, Ed Kellett <edk141@gmail.com> wrote:
On 25 August 2014 07:18, Terry Reedy <tjreedy@udel.edu> wrote:
On 8/24/2014 11:12 PM, Guido van Rossum wrote:
Finally, I would actually be okay if we found a way to let type hints and other annotations coexist -- I just prefer type hints to be the default use, since I see them as much more generally useful than all other uses combined.
I believe co-existence is possible, but the details will depend on the form type hints take. First, other annotations should be easily distinguished from type hints. Second, other annotations that would interfere with the runtime use of __annotations__ should be pulled out by a decorator and possibly moved to another attribute specific to the decorator (such as '_deco_name').
… or we could have a decorator for type hints, and ascribe no new meaning at all to __annotations__. Or assume __annotations__ contains type hints iff all the annotations present are instances of typing.*. That might be better anyway, since a decorator could then add (or augment) type information without assigning to __annotations__ (which would be weird).
Even if conveying type information is the most useful use of annotations, there's no reason it can't be explicit (and consistent with other uses of annotations).
I'm going to beat on my drum some more, but if annotations are dictionaries, where the keys are objects known to be associated with the type checker, then we don't have to guess, we'll know. E.g.: """ # type_checker.py class type_checker(object): # decorator magic TYPE_CHECKER = type_checker() """ """ # Your file from type_checker import TYPE_CHECKER @TYPE_CHECKER(a, int) def foo(a): pass """ which is morally equivalent to: """ from type_checker import TYPE_CHECKER def foo(a: {TYPE_CHECKER: int}): pass """ Chris Angelico pointed this trick out to me, and I think it's a good one. Assuming the type checker has enough brains to check and ensure that TYPE_CHECKER is never reassigned, it is guaranteed by the runtime system that TYPE_CHECKER is unique, which means that it can do the moral equivalent of 'TYPE_CHECKER in foo.__annotations__['a']'. That gives a VERY simple method of knowing what the annotations are being used for. Thanks, Cem Karan

On Mon, Aug 25, 2014 at 8:57 PM, Cem Karan <cfkaran2@gmail.com> wrote:
from type_checker import TYPE_CHECKER
def foo(a: {TYPE_CHECKER: int}): pass
I still don't think this offers much benefit over def foo(a: int): pass It's a lot wordier and the flexibility will almost never be needed. So is that multiplexing really worth it? ChrisA

On Aug 25, 2014, at 7:03 AM, Chris Angelico <rosuav@gmail.com> wrote:
On Mon, Aug 25, 2014 at 8:57 PM, Cem Karan <cfkaran2@gmail.com> wrote:
from type_checker import TYPE_CHECKER
def foo(a: {TYPE_CHECKER: int}): pass
I still don't think this offers much benefit over
def foo(a: int): pass
It's a lot wordier and the flexibility will almost never be needed. So is that multiplexing really worth it?
I'm just trying to preserve annotations as they currently are; that is, without a clearly defined meaning. Right now, I see annotations as a great place to store both documentation and type information, but without some kind of multiplexing standard you can't have both at the same time. I also suspect that if some kind of standard is in place that allows multiplexing, then other uses will be invented to take advantage of it. The other alternative is to define more attributes that functions are supposed to have, but that feels like a hack. It feels cleaner to me to store information about the arguments and return values with the arguments and return values directly, rather than somewhere else. I know that with decorators that isn't a problem, but it doesn't feel as clean to me. Thanks, Cem Karan

On Mon, Aug 25, 2014 at 7:12 PM, Cem Karan <cfkaran2@gmail.com> wrote:
On Aug 25, 2014, at 7:03 AM, Chris Angelico <rosuav@gmail.com> wrote:
On Mon, Aug 25, 2014 at 8:57 PM, Cem Karan <cfkaran2@gmail.com> wrote:
from type_checker import TYPE_CHECKER
def foo(a: {TYPE_CHECKER: int}): pass
I still don't think this offers much benefit over
def foo(a: int): pass
It's a lot wordier and the flexibility will almost never be needed. So is that multiplexing really worth it?
I'm just trying to preserve annotations as they currently are; that is, without a clearly defined meaning. Right now, I see annotations as a great place to store both documentation and type information, but without some kind of multiplexing standard you can't have both at the same time. I also suspect that if some kind of standard is in place that allows multiplexing, then other uses will be invented to take advantage of it.
The other alternative is to define more attributes that functions are supposed to have, but that feels like a hack. It feels cleaner to me to store information about the arguments and return values with the arguments and return values directly, rather than somewhere else. I know that with decorators that isn't a problem, but it doesn't feel as clean to me.
I appreciate your efforts, but in my mind this is a lost cause -- whatever notation you use to put multiple annotations on a single argument is just too noisy to bear, and I don't think the need is strong enough. Let's focus instead on coming up with a way to indicate to which category the annotations for a given function belong. As I've said before, I think a simple decorator should suffice. In fact, let me propose a straw-man so we have something to shoot down. The only thing we need is a way to tell the static type checker (which for the rest of this message I'm just going to abbreviate as "mypy") "these are not the annotations you are looking for." The simplest thing that could possibly work would be a single designated decorator. Let's say we import it from typing.py: from typing import no_type_checks @no_type_checks def speeder(short: 'r2d2', tall: 'c3po') -> 'luke': print('These are not the droids you are looking for') (Note that mypy uses string literals as forward class references, so without the decorator mypy would probably issue some confused complaints.) However, I think we need to be slightly more flexible. Most likely a function that uses annotations in some pre-mypy way is already decorated by a decorator that interprets the annotations. It would be annoying to have to double-decorate such functions, so I think there should be a way to decorate such decorators. Assuming such a decorator is implemented as a function, we can just decorate the decorator, as follows: from typing import no_type_checks @no_type_checks def clone_trooper(func): print('Looking for', func.__annotations__, 'in', func.__name__) func.searched = True return func @clone_trooper def speeder(short: 'r2d2', tall: 'c3po') -> 'luke': print('These are not the droids you are looking for') This is probably going to put off some people for its lack of purity -- how does mypy know that the @no_type_checks flag rubs off on anything decorated with it? In fact, how would it know that in the first example speeder() isn't a decorator? I personally think decorators are rare enough that no confusion will happen in practice, but I'll concede that perhaps it would be better to use two different decorators. Then @no_type_checks could be a simple no-op decorator which is itself decorated with e.g. @no_type_checks_decorator; both defined by typing.py. PS. In case someone asks, mypy is smart enough to follow imports, so @clone_trooper doesn't need to be defined in the same file where it is used -- I just did that to simplify the example. -- --Guido van Rossum (python.org/~guido)

Guido van Rossum <guido@...> writes:
from typing import no_type_checks
@no_type_checks def speeder(short: 'r2d2', tall: 'c3po') -> 'luke': print('These are not the droids you are looking for')
@no_type_checks def clone_trooper(func): print('Looking for', func.__annotations__, 'in', func.__name__) func.searched = True return func
@clone_trooper def speeder(short: 'r2d2', tall: 'c3po') -> 'luke': print('These are not the droids you are looking for')
Can I propose a slight modification -- the designated decorator takes another decorator, in the decoration line: @non_typing_annotation(None) def speeder(short: 'r2d2', tall: 'c3po') -> 'luke': print('These are not the droids you are looking for') @non_typing_annotation(clone_trooper) def speeder(short: 'r2d2', tall: 'c3po') -> 'luke': print('These are not the droids you are looking for') This way the library defining `clone_trooper` doesn't have to change to support the typing improvements, and the reader always knows whether the annotations will be treated as type hints without needing to consult another libraries' source. Also, I'd like to suggest that mypy should use the import of 'typing' as an indicator that the module's annotations are in fact type hints. -Tony

On Mon, Aug 25, 2014 at 3:20 AM, Ed Kellett <edk141@gmail.com> wrote:
On 25 August 2014 07:18, Terry Reedy <tjreedy@udel.edu> wrote:
On 8/24/2014 11:12 PM, Guido van Rossum wrote:
Finally, I would actually be okay if we found a way to let type hints and other annotations coexist -- I just prefer type hints to be the default use, since I see them as much more generally useful than all other uses combined.
I believe co-existence is possible, but the details will depend on the
form
type hints take. First, other annotations should be easily distinguished from type hints. Second, other annotations that would interfere with the runtime use of __annotations__ should be pulled out by a decorator and possibly moved to another attribute specific to the decorator (such as '_deco_name').
… or we could have a decorator for type hints, and ascribe no new meaning at all to __annotations__. Or assume __annotations__ contains type hints iff all the annotations present are instances of typing.*. That might be better anyway, since a decorator could then add (or augment) type information without assigning to __annotations__ (which would be weird).
Even if conveying type information is the most useful use of annotations, there's no reason it can't be explicit (and consistent with other uses of annotations).
All that sounds fine, but you still have to have a way to convey all that information to the type checker. Remember, the type checker cannot (or doesn't want to) execute the code and it can only see annotations in their original syntactic form. (But it can indeed be told about certain decorators.) -- --Guido van Rossum (python.org/~guido)

On 25 Aug 2014 16:29, "Guido van Rossum" <guido@python.org> wrote:
… or we could have a decorator for type hints, and ascribe no new meaning at all to __annotations__. Or assume __annotations__ contains type hints iff all the annotations present are instances of typing.*. That might be better anyway, since a decorator could then add (or augment) type information without assigning to __annotations__ (which would be weird).
Even if conveying type information is the most useful use of annotations, there's no reason it can't be explicit (and consistent with other uses of annotations).
All that sounds fine, but you still have to have a way to convey all that information to the type checker. Remember, the type checker cannot (or doesn't want to) execute the code and it can only see annotations in their original syntactic form. (But it can indeed be told about certain decorators.)
That's reasonable - I was imagining the decorator for type hints wouldn't do anything (apart from possibly marking the function as having type hints), it would just be there to tell the static checker "this function should be type-checked ". Ed Kellett

On Sun, Aug 24, 2014 at 11:18 PM, Terry Reedy <tjreedy@udel.edu> wrote:
On 8/24/2014 11:12 PM, Guido van Rossum wrote:
Finally, I would actually be okay if we found a way to let type hints
and other annotations coexist -- I just prefer type hints to be the default use, since I see them as much more generally useful than all other uses combined.
I believe co-existence is possible, but the details will depend on the form type hints take. First, other annotations should be easily distinguished from type hints. Second, other annotations that would interfere with the runtime use of __annotations__ should be pulled out by a decorator and possibly moved to another attribute specific to the decorator (such as '_deco_name').
What exactly do you mean by "other annotations that would interfere with the runtime use of __annotations__"? I can only assume that you are thinking of a situation where you are introspecting some function/method of unknown origin and you are trying to see if it has any annotations, in which case you are going to use them for a locally-defined use (e.g. generate an HTML form -- contrived example). It sounds as if you are worried about being passed a function that in the past would not have any annotations (so you would just generate a default form based on the argument names) but which now has been annotated by a zealous programmer with type hints. And you are worried that those type hints will confuse (perhaps crash) the form-generation code. I think initially we will just tell the user "don't use type annotations for form-handling functions", and also "don't run the type checker on that code". The next stage would probably be to create a decorator (say, @form_handler) and request that all form handlers are thus decorated. The decorator wouldn't need to do anything -- we could just teach the type checker that @form_handler means that argument annotations mean something that's unrelated to type checking, and we would still have to tell the user "don't use type annotations for @form_handler functions", but at least they could run the type checker on files that contain form handlers (the handlers themselves just wouldn't be type-checked). It should be easy enough to teach the type checker about such decorators; all you need is some kind of marker on the decorator function (perhaps itself a decorator :-). Eventually users might encourage the form library's maintainers to move the form info elsewhere (e.g. in the decorator arguments) and then the marker on the decorator can be taken off. But the initial stage seems a totally fine solution for 3.5, and it doesn't require anyone to do anything (it just requires some people to refrain from doing some new things -- which is much easier, because people are naturally aware that new things can cause new problems :-). -- --Guido van Rossum (python.org/~guido)

On 8/25/2014 11:12 AM, Guido van Rossum wrote:
On Sun, Aug 24, 2014 at 11:18 PM, Terry Reedy
I believe co-existence is possible, but the details will depend on the form type hints take. First, other annotations should be easily distinguished from type hints. Second, other annotations that would interfere with the runtime use of __annotations__ should be pulled out by a decorator and possibly moved to another attribute specific to the decorator (such as '_deco_name').
What exactly do you mean by "other annotations that would interfere with the runtime use of __annotations__"?
I am referring to the current stdlib runtime use of .__annotations__ directly by inspect.getfullargspec and now inspect.signature and indirectly by Idle calltips. def f(n:[int, 'random non-type info'])->(int, 'more non-type info'): pass import inspect as ip print(ip.formatargspec(*ip.getfullargspec(f))) print(str(ip.signature(f))) (n: [<class 'int'>, 'random non-type info']) -> (<class 'int'>, 'more non-type info') (n:[<class 'int'>, 'random non-type info']) -> (<class 'int'>, 'more non-type info') (Idle currently displays the first, will change to the second. The display difference is more pronounced (now, at least) for C functions with .__text_signature__ from Argument Clinic.) To me, 'argument specification' and especially 'signature' imply arguments names and types, but not other stuff. Certainly, name and type is all that is wanted for a tip on how to write a call. Extra stuff would typically be noise in this context -- especially for beginners.
I can only assume that you are thinking of a situation where you are introspecting some function/method of unknown origin and you are trying to see if it has any annotations, in which case you are going to use them for a locally-defined use (e.g. generate an HTML form -- contrived example).
It sounds as if you are worried about being passed a function that in the past would not have any annotations (so you would just generate a default form based on the argument names) but which now has been annotated by a zealous programmer with type hints. And you are worried that those type hints will confuse (perhaps crash) the form-generation code.
I am worried about annotations other than type hints. Compact type hints, especially for type-opaque parameter names, should improve the usefulness of call tips. Extra noise detracts. If type hints were standardized so as to be easily recognizable, .signature could be given an option to ignore anything else. -- Terry Jan Reedy

On Mon, Aug 25, 2014 at 4:36 PM, Terry Reedy <tjreedy@udel.edu> wrote:
On 8/25/2014 11:12 AM, Guido van Rossum wrote:
On Sun, Aug 24, 2014 at 11:18 PM, Terry Reedy
I believe co-existence is possible, but the details will depend on
the form type hints take. First, other annotations should be easily distinguished from type hints. Second, other annotations that would interfere with the runtime use of __annotations__ should be pulled out by a decorator and possibly moved to another attribute specific to the decorator (such as '_deco_name').
What exactly do you mean by "other annotations that would interfere with the runtime use of __annotations__"?
I am referring to the current stdlib runtime use of .__annotations__ directly by inspect.getfullargspec and now inspect.signature and indirectly by Idle calltips.
def f(n:[int, 'random non-type info'])->(int, 'more non-type info'): pass import inspect as ip print(ip.formatargspec(*ip.getfullargspec(f))) print(str(ip.signature(f)))
(n: [<class 'int'>, 'random non-type info']) -> (<class 'int'>, 'more non-type info') (n:[<class 'int'>, 'random non-type info']) -> (<class 'int'>, 'more non-type info')
(Idle currently displays the first, will change to the second. The display difference is more pronounced (now, at least) for C functions with .__text_signature__ from Argument Clinic.)
To me, 'argument specification' and especially 'signature' imply arguments names and types, but not other stuff. Certainly, name and type is all that is wanted for a tip on how to write a call. Extra stuff would typically be noise in this context -- especially for beginners.
I see two possible concerns here -- can you clarify? (1) If a function is annotated with multiple *categories* of annotations (e.g. type hints and html form specifiers) you want to change inspect to only return the type hints. (2) If a function is annotated for a non-type-hinting category (e.g. only html form specifiers) you want to change inspect to return nothing. I hadn't thought of either of these; I was only concerned about making sure that a static type checker wouldn't be unduly confused by non-type-hinting annotations. I assume __annotations__ should always return the full expression found in the syntactic position of the annotation (though decorators can change this).
I can only assume that you are thinking of a situation where you are
introspecting some function/method of unknown origin and you are trying to see if it has any annotations, in which case you are going to use them for a locally-defined use (e.g. generate an HTML form -- contrived example).
It sounds as if you are worried about being passed a function that in the past would not have any annotations (so you would just generate a default form based on the argument names) but which now has been annotated by a zealous programmer with type hints. And you are worried that those type hints will confuse (perhaps crash) the form-generation code.
I am worried about annotations other than type hints. Compact type hints, especially for type-opaque parameter names, should improve the usefulness of call tips. Extra noise detracts.
If type hints were standardized so as to be easily recognizable, .signature could be given an option to ignore anything else.
So I think you are actually not excited about any mechanism to keep other uses of annotations alive, and you would just as well only have them used for type hints? (That's fine with me, but there is the backward compatibility issue, and perhaps legitimate cool alternate uses of annotations. We still have the choice to allow only one category of annotations per function.) -- --Guido van Rossum (python.org/~guido)

On Mon, Aug 25, 2014 at 07:36:53PM -0400, Terry Reedy wrote:
I am referring to the current stdlib runtime use of .__annotations__ directly by inspect.getfullargspec and now inspect.signature and indirectly by Idle calltips.
def f(n:[int, 'random non-type info'])->(int, 'more non-type info'): pass import inspect as ip print(ip.formatargspec(*ip.getfullargspec(f))) print(str(ip.signature(f)))
(n: [<class 'int'>, 'random non-type info']) -> (<class 'int'>, 'more non-type info') (n:[<class 'int'>, 'random non-type info']) -> (<class 'int'>, 'more non-type info')
(Idle currently displays the first, will change to the second. The display difference is more pronounced (now, at least) for C functions with .__text_signature__ from Argument Clinic.)
To me, 'argument specification' and especially 'signature' imply arguments names and types, but not other stuff.
I don't think that's a useful defintion for Python. There is no hard definition of "function signature" in computer science, since different languages provide different information in the function declaration. But here's a definition from the C++ world which seems to capture the gist of it to me: A function signature consists of the function prototype. What it tells you is the general information about a function, its name, parameters, what scope it is in, and other miscellaneous information. http://www.cs.unm.edu/~storm/C++/ProgrammingTerms/FunctionSignatures.html To put it another way, it's anything in the function declaration apart from the actual body of the function. In the case of Python, that includes annotations, which currently have no official semantics, but may include type hints, or documentation, or anything else.
Certainly, name and type is all that is wanted for a tip on how to write a call. Extra stuff would typically be noise in this context -- especially for beginners.
I think that it is particularly for beginners that "extra stuff" in the form of documentation could be useful. Don't think of "random non-type info", as you state above, think of NON-random information about the parameter. The most obvious example is documentation about what the parameter represents. Here's a hypothetical example of what I might have done had annotations not been prohibited in the standard library: print(inspect.signature(statistics.pvariance)) => (data, mu:"pre-calculated mean of the data, if already known"=None) So I think that there's no good reason to decide, ahead of time, that the information in an annotation is "noise" in a tool-tip. I think the tool-tip should show whatevr information the author of the function thinks is important enough to go in the annotation. (However, the tool-tip might choose to format it a bit more nicely than it currently does. But that's a separate discussion.)
I can only assume that you are thinking of a situation where you are introspecting some function/method of unknown origin and you are trying to see if it has any annotations, in which case you are going to use them for a locally-defined use (e.g. generate an HTML form -- contrived example).
It sounds as if you are worried about being passed a function that in the past would not have any annotations (so you would just generate a default form based on the argument names) but which now has been annotated by a zealous programmer with type hints. And you are worried that those type hints will confuse (perhaps crash) the form-generation code.
I am worried about annotations other than type hints. Compact type hints, especially for type-opaque parameter names, should improve the usefulness of call tips. Extra noise detracts.
Certainly "noise" detracts, but you have no reason to assume that annotations can only be two things: type hints, or noise. In fact, I would argue that for a beginner, type hints will often be noise. Do you expect beginners to understand ABCs? If not, what are they to make of something like this? flatten(it:Iterable[Any])->Sequence[Any] -- Steven

Le 24/08/2014 23:12, Guido van Rossum a écrit :
As I have said several times now, it fulfills exactly the original (pre-PEP 3107) goal I had in mind for them.
But were you envisioning back then that said annotations would be looked up without the regular execution environment? PEP 3107 doesn't say anything about that (or rather, it says that annotations can be looked up on the function, but function objects only exist at run-time). Actually, the bytecode isn't very practical to work with to extract annotations, it seems:
def g(): ... def f(a: "foo") -> "bar": pass ... dis.dis(g) 2 0 LOAD_CONST 1 ('foo') 3 LOAD_CONST 2 ('bar') 6 LOAD_CONST 3 (('a', 'return')) 9 LOAD_CONST 4 (<code object f at 0x7f17339e2580, file "<stdin>", line 2>) 12 LOAD_CONST 5 ('g.<locals>.f') 15 EXTENDED_ARG 3 18 MAKE_FUNCTION 196608 21 STORE_FAST 0 (f) 24 LOAD_CONST 0 (None) 27 RETURN_VALUE
... so I suppose people would want to run an AST pass instead? (and then evaluate the "annotations" parts of the AST by hand... uh)
Your proposal seems determined by the fact that mypy has much grander ambitions (and therefore requires its insertion into regular Python), but it doesn't make use of that power at all, worse, it forbids others to use it.
This remark about mypy's ambitions sounds delusional and paranoid.
It is entirely uncritical about mypy. It's fine to have other Python (or Python-like) implementations, even with deliberately different semantics. Such experimentations actually make the community much livelier than, say, PHP's or Ruby's. I don't know at which point mypy changed goals (if it has), but there are still signs in the website of the goal of building a separate runtime (not necessarily a separate syntax), e.g. """Also some language features that are evaluated at runtime in Python may happen during compilation in mypy when using the native semantics. For example, mypy base classes may be bound during compilation (or program loading, before evaluation), unlike Python.""" Also the fact that mypy supports constructs such as "List[int]()". This is a set of design constraints that you're not bound to. Regards Antoine.

On Mon, Aug 25, 2014 at 6:57 AM, Antoine Pitrou <antoine@python.org> wrote:
Le 24/08/2014 23:12, Guido van Rossum a écrit :
As I have said several times now, it fulfills exactly the original (pre-PEP 3107) goal I had in mind for them.
But were you envisioning back then that said annotations would be looked up without the regular execution environment?
PEP 3107 doesn't say anything about that (or rather, it says that annotations can be looked up on the function, but function objects only exist at run-time). Actually, the bytecode isn't very practical to work with to extract annotations, it seems:
def g(): ... def f(a: "foo") -> "bar": pass ... dis.dis(g) 2 0 LOAD_CONST 1 ('foo') 3 LOAD_CONST 2 ('bar') 6 LOAD_CONST 3 (('a', 'return')) 9 LOAD_CONST 4 (<code object f at 0x7f17339e2580, file "<stdin>", line 2>) 12 LOAD_CONST 5 ('g.<locals>.f') 15 EXTENDED_ARG 3 18 MAKE_FUNCTION 196608 21 STORE_FAST 0 (f) 24 LOAD_CONST 0 (None) 27 RETURN_VALUE
... so I suppose people would want to run an AST pass instead? (and then evaluate the "annotations" parts of the AST by hand... uh)
Actually mypy uses an entirely different parser. This is mostly for historical reasons: it started out as a compiler/checker/runtime for a different, somewhat-Python-like statically-typed language. But I assume that static checkers often have no choice in the matter and must write their own parser -- e.g. PyCharm is itself written in Java and its parser must work even if the code is not syntactically correct. Another reason to use a different parser is that mypy needs to see the comments so it can look for magic comments starting with "#type:". This is typical for linters. In general a type checker should not attempt to cope with clever things (decorators or otherwise) that modify __annotations__; it should take the expression tree of the annotation at face value. (It should of course know about certain decorators like @property and other Python ideosyncracies like self-passing for methods, and I think there should be a way to teach it about new decorators. But executing Python code should not be part of that.)
Your proposal seems determined by the fact that mypy has much grander ambitions (and therefore requires its insertion into regular Python), but it doesn't make use of that power at all, worse, it forbids others to use it.
This remark about mypy's ambitions sounds delusional and paranoid.
It is entirely uncritical about mypy. It's fine to have other Python (or Python-like) implementations, even with deliberately different semantics. Such experimentations actually make the community much livelier than, say, PHP's or Ruby's.
OK, thanks for clarifying that. I took "grand ambitions" as a (sarcastically) pejorative term, but you're right that it doesn't need to be read like that.
I don't know at which point mypy changed goals (if it has), but there are still signs in the website of the goal of building a separate runtime (not necessarily a separate syntax), e.g.
"""Also some language features that are evaluated at runtime in Python may happen during compilation in mypy when using the native semantics. For example, mypy base classes may be bound during compilation (or program loading, before evaluation), unlike Python."""
I have talked to Jukka about this, and he is definitely on board with reducing mypy's functionality to that of a linter; I think there's even an issue in mypy's tracker about removing the ability to execute the code (currently there's a flag you must pass to prevent it from trying to run the code). Updating the website is an ongoing low-priority project (you can probably send him pull requests for it).
Also the fact that mypy supports constructs such as "List[int]()". This is a set of design constraints that you're not bound to.
Yeah, I'm not crazy about that syntax (I think nobody is). It mostly exists for the common use case where you have a function that is declared to return e.g. a list of ints and you want to start off with an empty list; the current mypy implementation complains about such code. Example: def squares(xs: Sequence[float]) -> List[float]: sqs = [] for x in xs: sqs.append(x**2) return sqs mypy complains about the unannotated initialization of sqs. The two ways to currently address this are sqs = [] # type: List[float] or sqs = List[float]() Neither is very attractive; mypy should just infer the type of sqs. Nevertheless, I don't want to use call syntax to set parameters for generic types, since a generic type still "feels" sufficiently like a class that calling it is easily confused with instantiation -- even though ABCs are typically not instantiable. (There's no hard rule for that though -- it is merely the result of typical ABCs having at least one abstract method.) -- --Guido van Rossum (python.org/~guido)

On Mon, Aug 25, 2014 at 10:26 AM, Guido van Rossum <guido@python.org> wrote:
On Mon, Aug 25, 2014 at 6:57 AM, Antoine Pitrou <antoine@python.org> wrote:
PEP 3107 doesn't say anything about that (or rather, it says that annotations can be looked up on the function, but function objects only exist at run-time). Actually, the bytecode isn't very practical to work with to extract annotations,
I don't know at which point mypy changed goals (if it has), but there are
still signs in the website of the goal of building a separate runtime (not necessarily a separate syntax), e.g.
"""Also some language features that are evaluated at runtime in Python may happen during compilation in mypy when using the native semantics. For example, mypy base classes may be bound during compilation (or program loading, before evaluation), unlike Python."""
I have talked to Jukka about this, and he is definitely on board with reducing mypy's functionality to that of a linter; I think there's even an issue in mypy's tracker about removing the ability to execute the code (currently there's a flag you must pass to prevent it from trying to run the code). Updating the website is an ongoing low-priority project (you can probably send him pull requests for it).
Yeah, the website wasn't very up-to-date. The project goals have shifted a lot in the last 18 months. I just updated the tutorial and the FAQ to not talk about the native semantics.
mypy complains about the unannotated initialization of sqs. The two ways to currently address this are
sqs = [] # type: List[float]
or
sqs = List[float]()
Neither is very attractive; mypy should just infer the type of sqs.
Agreed, but this is very difficult to do in all possible contexts. For example, __init__ methods often use an empty list as an attribute initializer, and here it's more tricky to infer the item type, as we'd have to look into other methods to figure out the item type.
Nevertheless, I don't want to use call syntax to set parameters for generic types, since a generic type still "feels" sufficiently like a class that calling it is easily confused with instantiation -- even though ABCs are typically not instantiable. (There's no hard rule for that though -- it is merely the result of typical ABCs having at least one abstract method.)
Generic types don't need to be abstract, and for those the call syntax would be ambiguous (and confusing). Jukka

Given / assuming that most annotated functions will only concern themselves with one use of annotations (either typing, web forms, cli parameters), maybe inspect could be extended to have an annotation-filtering function. Given a namespace(could be any python object), its protocol regarding annotations for each parameter could be: * If the annotation is a mapping(c.abc.Mapping): assume the namespace object is a key of that mapping. If the key is not present, return None for that annotation. * If the annotation is not a mapping, it is always returned whole. The implications would be: * A stdlib-approved way of namespacing annotations would exist. Annotations for different introspectors can coexist. * inspect.signature's purpose remains unchanged and still tells the whole truth * Most annotated functions, typing-related or not are unchanged. (except those that consist of a mapping ☹) In Python: import inspect import collections.abc def filter_annotation(annotation, namespace): if isinstance(annotation, collections.abc.Mapping): return annotation.get(namespace, inspect.Signature.empty) return annotation def filter_annotations(signature, namespace): params = [ p.replace(annotation=filter_annotation(p.annotation, namespace)) for p in signature.parameters.values() ] ret = filter_annotation(signature.return_annotation, namespace) return signature.replace(parameters=params, return_annotation=ret) >>> def classic(param:'annot'): pass ... >>> cliparser = object() >>> typing = object() >>> def namespaced(*, param:{typing:int, cliparser:'p'}): pass ... >>> s_classic = inspect.signature(classic) >>> s_namespaced = inspect.signature(namespaced) >>> print(filter_annotations(s_classic, cliparser)) (param:'annot') >>> print(filter_annotations(s_classic, typing)) (param:'annot') >>> print(filter_annotations(s_namespaced, cliparser)) (*, param:'p') >>> print(filter_annotations(s_namespaced, typing)) (*, param:int) On 26 August 2014 07:47, Jukka Lehtosalo <jlehtosalo@gmail.com> wrote:
On Mon, Aug 25, 2014 at 10:26 AM, Guido van Rossum <guido@python.org> wrote:
On Mon, Aug 25, 2014 at 6:57 AM, Antoine Pitrou <antoine@python.org> wrote:
PEP 3107 doesn't say anything about that (or rather, it says that annotations can be looked up on the function, but function objects only exist at run-time). Actually, the bytecode isn't very practical to work with to extract annotations,
I don't know at which point mypy changed goals (if it has), but there are
still signs in the website of the goal of building a separate runtime (not necessarily a separate syntax), e.g.
"""Also some language features that are evaluated at runtime in Python may happen during compilation in mypy when using the native semantics. For example, mypy base classes may be bound during compilation (or program loading, before evaluation), unlike Python."""
I have talked to Jukka about this, and he is definitely on board with reducing mypy's functionality to that of a linter; I think there's even an issue in mypy's tracker about removing the ability to execute the code (currently there's a flag you must pass to prevent it from trying to run the code). Updating the website is an ongoing low-priority project (you can probably send him pull requests for it).
Yeah, the website wasn't very up-to-date. The project goals have shifted a lot in the last 18 months. I just updated the tutorial and the FAQ to not talk about the native semantics.
mypy complains about the unannotated initialization of sqs. The two ways to currently address this are
sqs = [] # type: List[float]
or
sqs = List[float]()
Neither is very attractive; mypy should just infer the type of sqs.
Agreed, but this is very difficult to do in all possible contexts. For example, __init__ methods often use an empty list as an attribute initializer, and here it's more tricky to infer the item type, as we'd have to look into other methods to figure out the item type.
Nevertheless, I don't want to use call syntax to set parameters for generic types, since a generic type still "feels" sufficiently like a class that calling it is easily confused with instantiation -- even though ABCs are typically not instantiable. (There's no hard rule for that though -- it is merely the result of typical ABCs having at least one abstract method.)
Generic types don't need to be abstract, and for those the call syntax would be ambiguous (and confusing).
Jukka
_______________________________________________ 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 Sun, Aug 24, 2014 at 10:00 PM, Guido van Rossum <guido@python.org> wrote:
We should probably avoid "static typing" and use "type hinting" instead (following TypeScript's lead I believe). But the process of looking for violations against the hinted types before the code run is still called static analysis.
+1 -- Juancarlo *Añez*

On Aug 22, 2014, at 22:13, Steven D'Aprano <steve@pearwood.info> wrote:
I can't speak for the author of mypy, Jukka Lehtosalo, but for me, I think the answer is that type declarations make a *mini-language*, not full-blown Python. It is unclear to me just how powerful the type language will be, but surely we don't expect it to evaluate arbitrarily complex Python expressions at compile time? Do we?
I find it unlikely that we expect a static linter to make sense of this:
def bizarre(param:int if random.random() > 0.5 else str)->float:
except to say it returns a float and takes who-knows-what as argument. An extreme case, I grant, but I expect that there are going to be limits to what can be checked at compile time based on static analysis. I admit that I don't fully understand all the implications of static versus runtime type checking, but it doesn't seem unreasonable to expect static declarations to be considerably simpler than what can be expressed at runtime. Hence, a simpler mini-language is appropriate.
I agree. Trying to make the declarative type system more powerful is possible, but probably not a good idea. Look at C++98's template system. It's a complete compile-time language for defining types declaratively (in terms of other types, integer literals, and pointer literals), except that it's missing the ability to recursively decompose recursive types (like a compile-time cons). While that sounds nice in theory, in practice it's painful to use, and completely different from programming in C++. That's what happens if you take a declarative type system and try to expand it until it's fully general: you end up with a type system that looks like ML instead of your language. C++14 lets you use almost the entire runtime language for computing at compile time (no goto or try, some limits on inheritance) with constexpr values and functions. You can always use the declarative language, but you can also write complex stuff imperatively, in a way that looks like C++. If we really need something as powerful as ML or Haskell for our compile-time type system, the latter would be the way to do it. And if this obviously takes too much effort to be worth doing, that means we probably don't actually need the former either. So, trying to extend MyPy's declarative system to be fully general probably isn't worth doing.
Really, for it to be powerful enough, the type description system has to use its own set of classes. It can't try to hack away existing builtins and ABCs in the hope of expressing different things with them, or it *will* hit a wall some day (and, IMO, sooner rather than later).
How powerful is "powerful enough"? Powerful enough for what?
You've probably heard of the famous "the compiler found my infinite loop", where the ML type checker was able to detect that code would never terminate. I find that remarkable, and even more astonishing that, according to some, solving the halting problem is "nothing special" for type systems.[2] But many languages make do with less powerful type systems, and tools such as IDEs and editors surely don't need something that powerful. So:
- how powerful do we expect the type system to be?
- and how much of that power needs to be expressed using function annotations?
The second question is critical, because there are alternatives to function annotations: decorators, docstring annotations, and external stub files.
[1] Although if you do so, it is not clear to me how much the static checker will be able to use it.
[2] http://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/
-- Steven _______________________________________________ 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 08/23/14 04:36, Steven D'Aprano wrote:
But another reason for disliking call syntax for this is that it gets confusing:
def function(param:Sequence(Spam, Eggs))->Mapping(Cheese, Toast):
suffers compared to this:
def function(param:Sequence[Spam, Eggs])->Mapping[Cheese, Toast]:
In the second case, the type annotations stand out more easily because they use square brackets instead of round.
You're only giving examples with builtins. What about: def function(param:SomeClass(Spam, Eggs))->SomeOtherClass(Cheese, Toast): If there are template instantiations (in the C++ sense) there that's another meaning for parens which already have two meanings. And now they're also context-dependent!.. At least they were all the same everywhere before. So SomeClass[Spam, Eggs] is a fabulous idea -- in my mind it's just a special way of looking up a certain type. But as I said before[1] I'd hate to see annotations only be used for type checking. I'd love to be able to do: Integer[ge=5] but this comes with the question about what should actually passed to __getitem__ I'd avoid changing its signature to accept **kwargs. So I'd say that Unicode[15, pattern='[0-9a-z]+'] should translate to: {0: 15, 'pattern': '[0-9a-z]+'} which is a frozen ordered dict. As that's actually a better tuple that also happens to accept string keys, I doubt that'd interfere with the existing usage. We need this for duck typing to work as well. To my understanding, the type marker "File" means "only accept what's returned by open()". If however, we want to accept any file-like object, we could use Object[write=Callable]. Or maybe more specifically: Object[write=Callable[Bytes]].
-1 on re-using call syntax: it is more confusing to read inside a parameter list, too flexible, and clashes with existing use calling types.
+1 on subscripting syntax: it is more easily distinguishable from calling, just flexible enough, and doesn't clash with existing functionality.
Well, we need that flexibility at the very least for making duck typing work with type-annotated functions. best, burak [1]: https://mail.python.org/pipermail/python-ideas/2014-August/028988.html [2]: https://github.com/arskom/spyne/pull/313#issuecomment-29029067

On 08/21/2014 03:23 PM, Antoine Pitrou wrote:
One last thing: let’s not add |List|, |TList|, etc. Just use |list| and other existing types to generate new types.
I'm -1 on that, for exactly the same reason as above.
Another reason is that it makes it even easier for people to use the usually too narrow and therefore annoying list[int] instead of the correct Iterable[int] or Sequence[int] Another thought is whether, assuming class MyList(list): ... the type "MyList[int]" would do the expected thing. (And if not, how you would write the correct type generation code.) Georg

Georg Brandl wrote:
Another thought is whether, assuming
class MyList(list): ...
the type "MyList[int]" would do the expected thing. (And if not, how you would write the correct type generation code.)
I would expect that to be a error, unless MyList were declared as accepting a type parameter, using whatever means is decided on for that in the type language. -- Greg

On Sun, Aug 24, 2014 at 01:11:34PM +1200, Greg Ewing wrote:
Georg Brandl wrote:
Another thought is whether, assuming
class MyList(list): ...
the type "MyList[int]" would do the expected thing. (And if not, how you would write the correct type generation code.)
I would expect that to be a error, unless MyList were declared as accepting a type parameter, using whatever means is decided on for that in the type language.
I don't really understand what you're trying to say here, so I may be misinterpreting you. I *think* that you're trying to say that for every type in the standard library, and every class created by third parties (including subclasses), the author will have to "declare" (in some unknown sense) that it can be used for type annotations like MyList[T], for some type T. I expect that will be painful and unnecessary. I expect that if we go down this path, somebody will have to add the appropriate code to the metaclass of all types (i.e. type itself) so that AnyArbitraryType[spam] will do the right thing without the author of AnyArbitraryType needing to do any additional work. This is not entirely uncharted territory. Mypy has blazed the trail for us. The way I think Mypy works is that the classes in typing.py return themselves when subscripted. The arguments to __getitem__ are ignored except during static analysis. Here's the metaclass from Mypy: class GenericMeta(type): """Metaclass for generic classes that support indexing by types.""" def __getitem__(self, args): # Just ignore args; they are for compile-time checks only. return self https://github.com/JukkaL/mypy/blob/master/lib-typing/3.2/typing.py As I understand it, we are considering two possibilities: (1) all types are directly usable, so we can say def func(param:list[int])->list: This will require type to grow a __getitem__ method similar to the metaclass above. (2) Standard library types are not directly usable, you have to go through the typing module, which has wrappers that include the __getitem__ metaclass magic: from typing import List def func(param:List[int])->List: There are pros and cons to both approaches. -- Steven

Steven D'Aprano wrote:
I don't really understand what you're trying to say here, so I may be misinterpreting you. I *think* that you're trying to say that for every type in the standard library, and every class created by third parties (including subclasses), the author will have to "declare" (in some unknown sense) that it can be used for type annotations like MyList[T], for some type T.
I suppose the run-time incarnations of the type descriptions could be looser, but if you want to use them for static checking, the static checker is going to have to know what MyList[T] means in some detail (what effect the parameter has on the method types, etc.) The programmer will have to specify all that somehow. The way this is done in other languages with static type checking is to give the class declaration a parameter list. I was envisaging that the mypy type description syntax would have something equivalent. Not sure what form it would take, though. -- Greg

On 08/24/2014 07:54 AM, Greg Ewing wrote:
Steven D'Aprano wrote:
I don't really understand what you're trying to say here, so I may be misinterpreting you. I *think* that you're trying to say that for every type in the standard library, and every class created by third parties (including subclasses), the author will have to "declare" (in some unknown sense) that it can be used for type annotations like MyList[T], for some type T.
I suppose the run-time incarnations of the type descriptions could be looser, but if you want to use them for static checking, the static checker is going to have to know what MyList[T] means in some detail (what effect the parameter has on the method types, etc.) The programmer will have to specify all that somehow.
The way this is done in other languages with static type checking is to give the class declaration a parameter list. I was envisaging that the mypy type description syntax would have something equivalent. Not sure what form it would take, though.
Exactly. Does mypy handle that? For example, for a custom mapping type class Mapping(object): def setitem(self, key, value): ... how would one specify a) that you can use Mapping[T1, T2] as a type annotation and b) the type annotations for the "key" and "value" arguments? Georg

On Sun, Aug 24, 2014 at 10:00:32AM +0200, Georg Brandl wrote:
On 08/24/2014 07:54 AM, Greg Ewing wrote:
Steven D'Aprano wrote:
I don't really understand what you're trying to say here, so I may be misinterpreting you. I *think* that you're trying to say that for every type in the standard library, and every class created by third parties (including subclasses), the author will have to "declare" (in some unknown sense) that it can be used for type annotations like MyList[T], for some type T.
I suppose the run-time incarnations of the type descriptions could be looser, but if you want to use them for static checking, the static checker is going to have to know what MyList[T] means in some detail (what effect the parameter has on the method types, etc.) The programmer will have to specify all that somehow.
The way this is done in other languages with static type checking is to give the class declaration a parameter list. I was envisaging that the mypy type description syntax would have something equivalent. Not sure what form it would take, though.
Exactly. Does mypy handle that? For example, for a custom mapping type
class Mapping(object): def setitem(self, key, value): ...
how would one specify a) that you can use Mapping[T1, T2] as a type annotation and b) the type annotations for the "key" and "value" arguments?
I'm not an expert on Mypy, but I think the answer is, you can't. In order for Mypy to recognise your Mapping as an actual mapping, you have to either inherit from dict or some other mapping which Mypy knows about, or from typing.Generic. http://mypy-lang.org/tutorial.html#genericclasses from typing import typevar, Generic T = typevar('T') class Mapping(Generic[T, T]): ... Now Mypy will know that your Mapping class should be considered a mapping from some type to another type, and you can use it: def func(x:Mapping[int, str], n:int)->str: return x[n+1] which will pass the static type checker. (This is just what I understand from reading some of the docs, I welcome correction from anyone who knows better.) But that's just Mypy. Another type checker might just trust you, regardless of what terrible lies you tell it, so long as they're consistent lies: def func(x:set[int, str], n:int)->str: return x[n+1] Since you've said x is a set which maps ints to strs, the code will pass the static check, but fail at run-time since sets don't actually support __getitem__. A third type checker might do structural typing instead of nominal typing, and be able to recognise that set doesn't have __getitem__ but Mapping does. Remember that this proposal isn't about adding Mypy to the standard library or merging it with CPython. It remains a third-party implementation. People are encouraged to work on Mypy, or fork it, or build their own static tools, which may or may not do a better job of static analysis. Or runtime tools for that matter. -- Steven

On Aug 24, 2014, at 1:50, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Aug 24, 2014 at 10:00:32AM +0200, Georg Brandl wrote:
On 08/24/2014 07:54 AM, Greg Ewing wrote:
Steven D'Aprano wrote:
I don't really understand what you're trying to say here, so I may be misinterpreting you. I *think* that you're trying to say that for every type in the standard library, and every class created by third parties (including subclasses), the author will have to "declare" (in some unknown sense) that it can be used for type annotations like MyList[T], for some type T.
I suppose the run-time incarnations of the type descriptions could be looser, but if you want to use them for static checking, the static checker is going to have to know what MyList[T] means in some detail (what effect the parameter has on the method types, etc.) The programmer will have to specify all that somehow.
The way this is done in other languages with static type checking is to give the class declaration a parameter list. I was envisaging that the mypy type description syntax would have something equivalent. Not sure what form it would take, though.
Exactly. Does mypy handle that? For example, for a custom mapping type
class Mapping(object): def setitem(self, key, value): ...
how would one specify a) that you can use Mapping[T1, T2] as a type annotation and b) the type annotations for the "key" and "value" arguments?
I'm not an expert on Mypy, but I think the answer is, you can't. In order for Mypy to recognise your Mapping as an actual mapping, you have to either inherit from dict or some other mapping which Mypy knows about, or from typing.Generic.
http://mypy-lang.org/tutorial.html#genericclasses
from typing import typevar, Generic T = typevar('T') class Mapping(Generic[T, T]): ...
Now Mypy will know that your Mapping class should be considered a mapping from some type to another type, and you can use it:
No it doesn't, it just knows that it's a generic type, meaning that you can index it with types, and those types will be used to fill in the corresponding typevars on its methods. Consider Tuple[int, str]. That isn't a mapping from int to str, it's a 2-element tuple whose first element is int and whose second is str. And I'm sure you can come up with other uses for generic types of two type parameters that aren't mappings. This is exactly the same as in C++, ML and its descendants, and Java and its descendants. For example: class MyMapping(Generic[T, U]): def __getitem__(self, key: T) -> U: # ... Now a function that takes a MyMapping[int, str] will bind T to int and U to str, so the type checker knows that the argument to __getitem__ is an int and the return value is a str. And that still doesn't make it a Mapping. If collections.abc.Mapping (or typing.Mapping, if they're separate in the final version) is a Protocol, or otherwise implements structural matching, _that_ is what makes MyMapping a Mapping. And if if doesn't, then only inheriting or registering can make it a Mapping. Just like ABCs at runtime.

On 8/20/2014 9:42 PM, Stephen J. Turnbull wrote:
Terry Reedy writes:
If the annotation for a library are in a separate skeleton file
You mean stub file, right? (Stub is the term used so far in this thread.)
Then yes, that I what i mean ;-).
To develop Terry's idea a bit more explicitly, ISTM that during early development, you use Python 3 with annotations in file. Then when you need to test Python 2, you use a tool (as yet to be developed) which generates a Python file that has had all annotations stripped, and a stub file[1]. Throw away the stub file, test the Python file, and return to development.
An alternative is to develop code and stub separately. There are people (not core developers) already working on stub files for the stdlib and other libraries. In fact there are multiple groups using different syntaxes. Presumably some will join forces if there is a blessed, standard, syntax. There are no plans to add annotations to the stdlib code itself. Even if one does not add annotations to one own code, which many projects, especially smaller ones, will not, there will be the possibility of having calls into the stdlib and other annotated or stubbed libraries statically checked on 3.5 (or later). One will be able to at least partially benefit without writing annotations oneself.
For distribution (to *both* Python 3 and Python 2 users) you ignore the original annotated code and distribute the generated Python file and the stubs file. Probably there would be a tool for combining the Python and stub files back into an annotated Python file.
The two tools (stripper and annotator) can be very accurate (although the annotator may not produce precisely the style desired by users, the tools probably can be designed so that the re-annotated file accurately reflects the style used by the authors).
I imagine both tools will emerge.
Is the implied workflow really so burdensome? Especially to downstream?
Note that Terry works on IDLE,
I have been watching this thread precisely from that viewpoint.
so it seems likely that at least one Python IDE would support this workflow very well. :-)
Part this summer's Idle GSOC project was developing a framework to run 3rd party code checkers on code in an Idle editor from the editor. I just need to review and possibly revise the code Saimadhav wrote and decide among the alternatives tried. Acesss to existing pyflakes, pylint, or anything else, once installed (easy now with pip), will soon be possible. Anything in the stdlib, rather than at PyPI or wherever, would likely get even more direct support. For instance, one can check syntax without running the edit buffer, and I believe that just imports and runs tokenize.tokenize. I can imagine doing something similar with stripper, splitter, and joiner functions. (This might be in an extension module so the feature can to disabled, as might be appropriate for a beginner class.) -- Terry Jan Reedy

In mypy FAQ: http://www.mypy-lang.org/faq.html "All of my code is still in Python 2. What are my options? Mypy currently supports Python 3 syntax. Python 2 support is still in early stages of development. However, Python 2 support will be improving." I don't know how. Does someone know if a decorator that add "__annotations__" attribute to the function can do the job ? with something like that: @mypy(int, int, returns=int) def gcd(a, b): <tralala> 2014-08-20 16:11 GMT+02:00 Brett Cannon <brett@python.org>:
On Wed Aug 20 2014 at 9:29:34 AM Paul Moore <p.f.moore@gmail.com> wrote:
We certainly *could* do that. However, I haven't seen sufficient other uses of annotations. If there is only one use for annotations (going forward), annotations would be unambiguous. If we allow different types of annotations, there would have to be a way to tell whether a particular annotation is intended as a type annotation or not. Currently mypy ignores all modules that don't import typing.py (using any form of import statement), and we could continue this convention. But it would mean
On 14 August 2014 00:30, Guido van Rossum <guido@python.org> wrote: that
something like this would still require the typing import in order to be checked by mypy:
import typing
def gcd(int a, int b) -> int: <tralala>
Sorry, I'm slowly going through this thread, so my apologies if this has been covered later, but it seems to me that projects (and in particular libraries) that want to target 2.7 as well as 3.x will be forced to avoid this feature. And avoid any dependencies that use it.
Specifically, the annotation syntax is purely Python 3.x, so without some form of translation or import hook, 2.7 won't accept it. And adding a dependency (even if it's only a very lightweight typing.py plus a hook installed somewhere/somehow) for something that only has value as part of a developer-level type check doesn't seem like a good solution.
So, I'd like someone to explain (maybe by pointing me to relevant mypy docs, I haven't investigated them yet) how I should modify my existing code that supports 2.7 and 3.x so that it uses the new functionality *without* extra runtime dependencies that only deliver build/test-time benefits (that's the problem I always had with setuptools/pkg_resources, and I'd not like to see it repeated here).
I suspect the answer is "you don't". Just like everything else that is syntactically exclusive to Python 3, it's a perk you get with Python 3-only code that you simply can't get if you want to maintain backwards-compatibility.
_______________________________________________ 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 Aug 20, 2014, at 8:28 AM, Paul Moore <p.f.moore@gmail.com> wrote:
Specifically, the annotation syntax is purely Python 3.x, so without some form of translation or import hook, 2.7 won't accept it. And adding a dependency (even if it's only a very lightweight typing.py plus a hook installed somewhere/somehow) for something that only has value as part of a developer-level type check doesn't seem like a good solution.
So, I'd like someone to explain (maybe by pointing me to relevant mypy docs, I haven't investigated them yet) how I should modify my existing code that supports 2.7 and 3.x so that it uses the new functionality *without* extra runtime dependencies that only deliver build/test-time benefits (that's the problem I always had with setuptools/pkg_resources, and I'd not like to see it repeated here).
As you’ve suggested, using annotations won’t work on Python 2 where there are no annotations. This feature would not available for applications that wish to support Python 2. However, that stipulation isn’t unique to this feature. If you want be able to run on older versions of the language, you must forgo the features that are available in newer versions of the language. You can’t use with_statement unless you’re willing to drop Python 2.4 support, you can’t use print_function unless you’re willing to drop 2.5 support, and you can’t use annotations unless you’re willing to drop all Python 2 support. This is the nature of language improvements.

On 8/20/2014 10:27 AM, Ryan Hiebert wrote:
On Aug 20, 2014, at 8:28 AM, Paul Moore <p.f.moore@gmail.com> wrote:
Specifically, the annotation syntax is purely Python 3.x, so without some form of translation or import hook, 2.7 won't accept it. And adding a dependency (even if it's only a very lightweight typing.py plus a hook installed somewhere/somehow) for something that only has value as part of a developer-level type check doesn't seem like a good solution.
So, I'd like someone to explain (maybe by pointing me to relevant mypy docs, I haven't investigated them yet) how I should modify my existing code that supports 2.7 and 3.x so that it uses the new functionality *without* extra runtime dependencies that only deliver build/test-time benefits (that's the problem I always had with setuptools/pkg_resources, and I'd not like to see it repeated here).
As you’ve suggested, using annotations won’t work on Python 2 where there are no annotations.
Correct.
This feature would not available for applications that wish to support Python 2.
Not exactly true, which is to say, true for run-time use, not true for pre-compile call checking. Guido mostly wants type hints for the latter. Call checking mostly just needs to be done once (on 3.5+). To expand on what I said elsewhere: suppose a 2&3 app A uses 2&3 library L, which has a separate annotation file. You run the call checks on 3.5. Assuming that the 2&3 code of each use bytes and unicode and not str, calls should be good on all versions. (They will be unless the same code produces objects of different types on different versions.) -- Terry Jan Reedy

On Thu, Aug 14, 2014 at 5:44 AM, Guido van Rossum <guido@python.org> wrote:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I strongly support the concept of standardized typing information. There'll be endless bikeshedding on names, though - personally, I don't like the idea of "from typing import ..." as there's already a "types" module and I think it'd be confusing. (Also, "mypy" sounds like someone's toy reimplementation of Python, which it does seem to be :) but that's not really well named for "type checker using stdlib annotations".) But I think the idea is excellent, and it deserves stdlib support. The cast notation sounds to me like it's what Pike calls a "soft cast" - it doesn't actually *change* anything (contrast a C or C++ type cast, where (float)42 is 42.0), it just says to the copmiler/type checker "this thing is actually now this type". If the naming is clear on this point, it leaves open the possibility of actual recursive casting - where casting a List[str] to List[int] is equivalent to [int(x) for x in lst]. Whether or not that's a feature worth adding can be decided in the distant future :) +1 on the broad proposal. +0.5 on defining the notation while leaving the actual type checking to an external program. ChrisA

Both solutions have merit, but the idea of some implementations of the type checker having covariance and some contravariance is fairly disturbing.
Why can't we have both? That's the only way to properly type things, since immutable-get-style APIs are always going to be convariant, set-only style APIs (e.g. a function that takes 1 arg and returns None) are going to be contravariant and mutable get-set APIs (like most python collections) should really be invariant. On Wed, Aug 13, 2014 at 5:32 PM, Chris Angelico <rosuav@gmail.com> wrote:
On Thu, Aug 14, 2014 at 5:44 AM, Guido van Rossum <guido@python.org> wrote:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I strongly support the concept of standardized typing information. There'll be endless bikeshedding on names, though - personally, I don't like the idea of "from typing import ..." as there's already a "types" module and I think it'd be confusing. (Also, "mypy" sounds like someone's toy reimplementation of Python, which it does seem to be :) but that's not really well named for "type checker using stdlib annotations".) But I think the idea is excellent, and it deserves stdlib support.
The cast notation sounds to me like it's what Pike calls a "soft cast" - it doesn't actually *change* anything (contrast a C or C++ type cast, where (float)42 is 42.0), it just says to the copmiler/type checker "this thing is actually now this type". If the naming is clear on this point, it leaves open the possibility of actual recursive casting - where casting a List[str] to List[int] is equivalent to [int(x) for x in lst]. Whether or not that's a feature worth adding can be decided in the distant future :)
+1 on the broad proposal. +0.5 on defining the notation while leaving the actual type checking to an external program.
ChrisA _______________________________________________ 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 Wed, Aug 13, 2014 at 5:53 PM, Haoyi Li <haoyi.sg@gmail.com> wrote:
Both solutions have merit, but the idea of some implementations of the type checker having covariance and some contravariance is fairly disturbing.
Why can't we have both? That's the only way to properly type things, since immutable-get-style APIs are always going to be convariant, set-only style APIs (e.g. a function that takes 1 arg and returns None) are going to be contravariant and mutable get-set APIs (like most python collections) should really be invariant.
That makes sense. Can you put something in the mypy tracker about this? (Or send a pull request. :-) -- --Guido van Rossum (python.org/~guido)

On 08/14/2014 12:32 PM, Chris Angelico wrote:
I don't like the idea of "from typing import ..." as there's already a "types" module and I think it'd be confusing.
Maybe from __statictyping__ import ... More explicit, and being a dunder name suggests that it's something special that linters should ignore if they don't understand it. -- Greg

It’s great to see this finally happening! I did some research on existing optional-typing approaches [1]. What I learned in the process was that linting is the most important use case for optional typing; runtime checks is too little, too late. That being said, having optional runtime checks available *is* also important. Used in staging environments and during unit testing, this case is able to cover cases obscured by meta-programming. Implementations like “obiwan” and “pytypedecl” show that providing a runtime type checker is absolutely feasible. The function annotation syntax currently supported in Python 3.4 is not well-suited for typing. This is because users expect to be able to operate on the types they know. This is currently not feasible because: 1. forward references are impossible 2. generics are impossible without custom syntax (which is the reason Mypy’s Dict exists) 3. optional types are clumsy to express (Optional[int] is very verbose for a use case this common) 4. union types are clumsy to express All those problems are elegantly solved by Google’s pytypedecl via moving type information to a separate file. Because for our use case that would not be an acceptable approach, my intuition would be to: 1. Provide support for generics (understood as an answer to the question: “what does this collection contain?”) in Abstract Base Classes. That would be a PEP in itself. 2. Change the function annotation syntax so that it’s not executed at import time but rather treated as strings. This solves forward references and enables us to… 3. Extend the function annotation syntax with first-class generics support (most languages like "list<str>”) 4. Extend the function annotation syntax with first-class union type support. pytypedecl simply uses “int or None”, which I find very elegant. 5. Speaking of None, possibly further extend the function annotation syntax with first-class optionality support. In the Facebook codebase in Hack we have tens of thousands of optional ints (nevermind other optional types!), this is a case that’s going to be used all the time. Hack uses ?int, that’s the most succinct style you can get. Yes, it’s special but None is a special type, too. All in all, I believe Mypy has the highest chance of becoming our typing linter, which is great! I just hope we can improve on the syntax, which is currently lacking. Also, reusing our existing ABCs where applicable would be nice. With Mypy’s typing module I feel like we’re going to get a new, orthogonal set of ABCs, which will confuse users to no end. Finally, the runtime type checker would make the ecosystem complete. This is just the beginning of the open issues I was juggling with and the reason my own try at the PEP was coming up slower than I’d like. [1] You can find a summary of examples I looked at here: http://lukasz.langa.pl/typehinting/ -- Best regards, Łukasz Langa WWW: http://lukasz.langa.pl/ Twitter: @llanga IRC: ambv on #python-dev On Aug 13, 2014, at 12:44 PM, Guido van Rossum <guido@python.org> wrote:
[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.]
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online (https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations (b) Python's use of mutabe containers by default is wrong (c) Python should adopt some kind of Abstract Data Types
Proposals (b) and (c) don't feel particularly actionable (if you disagree please start a new thread, I'd be happy to discuss these further if there's interest) but proposal (a) feels right to me.
So what is mypy? It is a static type checker for Python written by Jukka for his Ph.D. thesis. The basic idea is that you add type annotations to your program using some custom syntax, and when running your program using the mypy interpreter, type errors will be found during compilation (i.e., before the program starts running).
The clever thing here is that the custom syntax is actually valid Python 3, using (mostly) function annotations: your annotated program will still run with the regular Python 3 interpreter. In the latter case there will be no type checking, and no runtime overhead, except to evaluate the function annotations (which are evaluated at function definition time but don't have any effect when the function is called).
In fact, it is probably more useful to think of mypy as a heavy-duty linter than as a compiler or interpreter; leave the type checking to mypy, and the execution to Python. It is easy to integrate mypy into a continuous integration setup, for example.
To read up on mypy's annotation syntax, please see the mypy-lang.org website. Here's just one complete example, to give a flavor:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Note that the #type: comment is part of the mypy syntax; mypy uses comments to declare types in situations where no syntax is available -- although this particular line could also be written as follows:
result = Dict[str, int]()
Either way the entire function is syntactically valid Python 3, and a suitable implementation of typing.py (containing class definitions for List and Dict, for example) can be written to make the program run correctly. One is provided as part of the mypy project.
I should add that many of mypy's syntactic choices aren't actually new. The basis of many of its ideas go back at least a decade: I blogged about this topic in 2004 (http://www.artima.com/weblogs/viewpost.jsp?thread=85551 -- see also the two followup posts linked from the top there).
I'll emphasize once more that mypy's type checking happens in a separate pass: no type checking happens at run time (other than what the interpreter already does, like raising TypeError on expressions like 1+"1").
There's a lot to this proposal, but I think it's possible to get a PEP written, accepted and implemented in time for Python 3.5, if people are supportive. I'll go briefly over some of the action items.
(1) A change of direction for function annotations
PEP 3107, which introduced function annotations, is intentional non-committal about how function annotations should be used. It lists a number of use cases, including but not limited to type checking. It also mentions some rejected proposals that would have standardized either a syntax for indicating types and/or a way for multiple frameworks to attach different annotations to the same function. AFAIK in practice there is little use of function annotations in mainstream code, and I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them.
(We may have to have some backwards compatibility provision to avoid breaking code that currently uses annotations for some other purpose. Fortunately the only issue, at least initially, will be that when running mypy to type check such code it will produce complaints about the annotations; it will not affect how such code is executed by the Python interpreter. Nevertheless, it would be good to deprecate such alternative uses of annotations.)
(2) A specification for what to add to Python 3.5
There needs to be at least a rough consensus on the syntax for annotations, and the syntax must cover a large enough set of use cases to be useful. Mypy is still under development, and some of its features are still evolving (e.g. unions were only added a few weeks ago). It would be possible to argue endlessly about details of the notation, e.g. whether to use 'list' or 'List', what either of those means (is a duck-typed list-like type acceptable?) or how to declare and use type variables, and what to do with functions that have no annotations at all (mypy currently skips those completely).
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter. The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module. This module defines several dozen new classes (and a few decorators and other helpers) that can be used in expressing argument types. If you want to type-check your code you have to download and install mypy and run it separately.
The curious thing here is that while standardizing a syntax for type annotations, we technically still won't be adopting standard rules for type checking. This is intentional. First of all, fully specifying all the type checking rules would make for a really long and boring PEP (a much better specification would probably be the mypy source code). Second, I think it's fine if the type checking algorithm evolves over time, or if variations emerge. The worst that can happen is that you consider your code correct but mypy disagrees; your code will still run.
That said, I don't want to completely leave out any specification. I want the contents of the typing.py module to be specified in the PEP, so that it can be used with confidence. But whether mypy will complain about your particular form of duck typing doesn't have to be specified by the PEP. Perhaps as mypy evolves it will take options to tell it how to handle certain edge cases. Forks of mypy (or entirely different implementations of type checking based on the same annotation syntax) are also a possibility. Maybe in the distant future a version of Python will take a different stance, once we have more experience with how this works out in practice, but for Python 3.5 I want to restrict the scope of the upheaval.
Appendix -- Why Add Type Annotations?
The argument between proponents of static typing and dynamic typing has been going on for many decades. Neither side is all wrong or all right. Python has traditionally fallen in the camp of extremely dynamic typing, and this has worked well for most users, but there are definitely some areas where adding type annotations would help.
- Editors (IDEs) can benefit from type annotations; they can call out obvious mistakes (like misspelled method names or inapplicable operations) and suggest possible method names. Anyone who has used IntelliJ or Xcode will recognize how powerful these features are, and type annotations will make such features more useful when editing Python source code.
- Linters are an important tool for teams developing software. A linter doesn't replace a unittest, but can find certain types of errors better or quicker. The kind of type checking offered by mypy works much like a linter, and has similar benefits; but it can find problems that are beyond the capabilities of most linters.
- Type annotations are useful for the human reader as well! Take the above word_count() example. How long would it have taken you to figure out the types of the argument and return value without annotations? Currently most people put the types in their docstrings; developing a standard notation for type annotations will reduce the amount of documentation that needs to be written, and running the type checker might find bugs in the documentation, too. Once a standard type annotation syntax is introduced, it should be simple to add support for this notation to documentation generators like Sphinx.
- Refactoring. Bob's talk has a convincing example of how type annotations help in (manually) refactoring code. I also expect that certain automatic refactorings will benefit from type annotations -- imagine a tool like 2to3 (but used for some other transformation) augmented by type annotations, so it will know whether e.g. x.keys() is referring to the keys of a dictionary or not.
- Optimizers. I believe this is actually the least important application, certainly initially. Optimizers like PyPy or Pyston wouldn't be able to fully trust the type annotations, and they are better off using their current strategy of optimizing code based on the types actually observed at run time. But it's certainly feasible to imagine a future optimizer also taking type annotations into account.
-- --Guido "I need a new hobby" 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/

On Wed, Aug 13, 2014 at 6:00 PM, Łukasz Langa <lukasz@langa.pl> wrote:
It’s great to see this finally happening!
Yes. :-)
I did some research on existing optional-typing approaches [1]. What I learned in the process was that linting is the most important use case for optional typing; runtime checks is too little, too late.
That being said, having optional runtime checks available *is* also important. Used in staging environments and during unit testing, this case is able to cover cases obscured by meta-programming. Implementations like “obiwan” and “pytypedecl” show that providing a runtime type checker is absolutely feasible.
Yes. And the proposal here might well enable such applications (by providing a standard way to spell complex types). But I think it's going to be less important than good support for linting, so that's what I want to focus on first.
The function annotation syntax currently supported in Python 3.4 is not well-suited for typing. This is because users expect to be able to operate on the types they know. This is currently not feasible because: 1. forward references are impossible
(Mypy's hack for this is that a string literal can be used as a forward reference.) 2. generics are impossible without custom syntax (which is the reason
Mypy’s Dict exists) 3. optional types are clumsy to express (Optional[int] is very verbose for a use case this common)
So define an alias 'oint'. :-)
4. union types are clumsy to express
Aliasing can help.
All those problems are elegantly solved by Google’s pytypedecl via moving type information to a separate file.
Mypy supports this too using stub files, but I think it is actually a strength that it doesn't require new syntax (although if the idea becomes popular we could certainly add syntax to support those things where mypy currently requires magic comments). Honestly I'm not sure what to do about mypy vs. pytypedecl. Should they compete, collaborate, converge? Do we need a bake-off or a joint hackathon? Food for thought.
Because for our use case that would not be an acceptable approach, my intuition would be to:
1. Provide support for generics (understood as an answer to the question: “what does this collection contain?”) in Abstract Base Classes. That would be a PEP in itself.
2. Change the function annotation syntax so that it’s not executed at
import time but rather treated as strings. This solves forward references and enables us to…
3. Extend the function annotation syntax with first-class generics support
(most languages like "list<str>”)
4. Extend the function annotation syntax with first-class union type
support. pytypedecl simply uses “int or None”, which I find very elegant.
5. Speaking of None, possibly further extend the function annotation syntax
with first-class optionality support. In the Facebook codebase in Hack we have tens of thousands of optional ints (nevermind other optional types!), this is a case that’s going to be used all the time. Hack uses ?int, that’s the most succinct style you can get. Yes, it’s special but None is a special type, too.
Hm. I think that selling such (IMO) substantial changes to Python's syntax is going to be much harder than just the idea of a standard typing syntax implemented as a new stdlib module. While mypy's syntax is perhaps not as concise or elegant as would be possible if we were to design the syntax from the ground up, it's actually pretty darn readable, and it is compatible with Python 3.2. It has decent ways to spell generics, forward references, unions and optional types already. And while I want to eventually phase out other uses of function annotations, your change #2 would break all existing packages that use them for other purposes (like Ethan Furman's scription).
All in all, I believe Mypy has the highest chance of becoming our typing linter, which is great! I just hope we can improve on the syntax, which is currently lacking. Also, reusing our existing ABCs where applicable would be nice. With Mypy’s typing module I feel like we’re going to get a new, orthogonal set of ABCs, which will confuse users to no end. Finally, the runtime type checker would make the ecosystem complete.
We can discuss these things separately. Language evolution is an exercise in compromise. We may be able to reuse the existing ABCs, and mypy could still support Python 3.2 (or, with the codeck hack, 2.7) by having the typing module export aliases to those ABCs. I won't stop you from implementing a runtime type checker, but I think it should be a separate project.
This is just the beginning of the open issues I was juggling with and the reason my own try at the PEP was coming up slower than I’d like.
Hopefully I've motivated you to speed up!
[1] You can find a summary of examples I looked at here: http://lukasz.langa.pl/typehinting/
-- --Guido van Rossum (python.org/~guido)

On Wed, Aug 13, 2014 at 12:44 PM, Guido van Rossum <guido@python.org> wrote:
[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.]
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online ( https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations (b) Python's use of mutabe containers by default is wrong (c) Python should adopt some kind of Abstract Data Types
Proposals (b) and (c) don't feel particularly actionable (if you disagree please start a new thread, I'd be happy to discuss these further if there's interest) but proposal (a) feels right to me.
So what is mypy? It is a static type checker for Python written by Jukka for his Ph.D. thesis. The basic idea is that you add type annotations to your program using some custom syntax, and when running your program using the mypy interpreter, type errors will be found during compilation (i.e., before the program starts running).
The clever thing here is that the custom syntax is actually valid Python 3, using (mostly) function annotations: your annotated program will still run with the regular Python 3 interpreter. In the latter case there will be no type checking, and no runtime overhead, except to evaluate the function annotations (which are evaluated at function definition time but don't have any effect when the function is called).
In fact, it is probably more useful to think of mypy as a heavy-duty linter than as a compiler or interpreter; leave the type checking to mypy, and the execution to Python. It is easy to integrate mypy into a continuous integration setup, for example.
To read up on mypy's annotation syntax, please see the mypy-lang.org website. Here's just one complete example, to give a flavor:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Note that the #type: comment is part of the mypy syntax; mypy uses comments to declare types in situations where no syntax is available -- although this particular line could also be written as follows:
result = Dict[str, int]()
Either way the entire function is syntactically valid Python 3, and a suitable implementation of typing.py (containing class definitions for List and Dict, for example) can be written to make the program run correctly. One is provided as part of the mypy project.
I should add that many of mypy's syntactic choices aren't actually new. The basis of many of its ideas go back at least a decade: I blogged about this topic in 2004 ( http://www.artima.com/weblogs/viewpost.jsp?thread=85551 -- see also the two followup posts linked from the top there).
I'll emphasize once more that mypy's type checking happens in a separate pass: no type checking happens at run time (other than what the interpreter already does, like raising TypeError on expressions like 1+"1").
There's a lot to this proposal, but I think it's possible to get a PEP written, accepted and implemented in time for Python 3.5, if people are supportive. I'll go briefly over some of the action items.
*(1) A change of direction for function annotations*
PEP 3107 <http://legacy.python.org/dev/peps/pep-3107/>, which introduced function annotations, is intentional non-committal about how function annotations should be used. It lists a number of use cases, including but not limited to type checking. It also mentions some rejected proposals that would have standardized either a syntax for indicating types and/or a way for multiple frameworks to attach different annotations to the same function. AFAIK in practice there is little use of function annotations in mainstream code, and I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them.
(We may have to have some backwards compatibility provision to avoid breaking code that currently uses annotations for some other purpose. Fortunately the only issue, at least initially, will be that when running mypy to type check such code it will produce complaints about the annotations; it will not affect how such code is executed by the Python interpreter. Nevertheless, it would be good to deprecate such alternative uses of annotations.)
*(2) A specification for what to add to Python 3.5*
There needs to be at least a rough consensus on the syntax for annotations, and the syntax must cover a large enough set of use cases to be useful. Mypy is still under development, and some of its features are still evolving (e.g. unions were only added a few weeks ago). It would be possible to argue endlessly about details of the notation, e.g. whether to use 'list' or 'List', what either of those means (is a duck-typed list-like type acceptable?) or how to declare and use type variables, and what to do with functions that have no annotations at all (mypy currently skips those completely).
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter. The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module. This module defines several dozen new classes (and a few decorators and other helpers) that can be used in expressing argument types. If you want to type-check your code you have to download and install mypy and run it separately.
The curious thing here is that while standardizing a syntax for type annotations, we technically still won't be adopting standard rules for type checking. This is intentional. First of all, fully specifying all the type checking rules would make for a really long and boring PEP (a much better specification would probably be the mypy source code). Second, I think it's fine if the type checking algorithm evolves over time, or if variations emerge. The worst that can happen is that you consider your code correct but mypy disagrees; your code will still run.
That said, I don't want to *completely* leave out any specification. I want the contents of the typing.py module to be specified in the PEP, so that it can be used with confidence. But whether mypy will complain about your particular form of duck typing doesn't have to be specified by the PEP. Perhaps as mypy evolves it will take options to tell it how to handle certain edge cases. Forks of mypy (or entirely different implementations of type checking based on the same annotation syntax) are also a possibility. Maybe in the distant future a version of Python will take a different stance, once we have more experience with how this works out in practice, but for Python 3.5 I want to restrict the scope of the upheaval.
*Appendix -- Why Add Type Annotations?* The argument between proponents of static typing and dynamic typing has been going on for many decades. Neither side is all wrong or all right. Python has traditionally fallen in the camp of extremely dynamic typing, and this has worked well for most users, but there are definitely some areas where adding type annotations would help.
- Editors (IDEs) can benefit from type annotations; they can call out obvious mistakes (like misspelled method names or inapplicable operations) and suggest possible method names. Anyone who has used IntelliJ or Xcode will recognize how powerful these features are, and type annotations will make such features more useful when editing Python source code.
- Linters are an important tool for teams developing software. A linter doesn't replace a unittest, but can find certain types of errors better or quicker. The kind of type checking offered by mypy works much like a linter, and has similar benefits; but it can find problems that are beyond the capabilities of most linters.
- Type annotations are useful for the human reader as well! Take the above word_count() example. How long would it have taken you to figure out the types of the argument and return value without annotations? Currently most people put the types in their docstrings; developing a standard notation for type annotations will reduce the amount of documentation that needs to be written, and running the type checker might find bugs in the documentation, too. Once a standard type annotation syntax is introduced, it should be simple to add support for this notation to documentation generators like Sphinx.
- Refactoring. Bob's talk has a convincing example of how type annotations help in (manually) refactoring code. I also expect that certain automatic refactorings will benefit from type annotations -- imagine a tool like 2to3 (but used for some other transformation) augmented by type annotations, so it will know whether e.g. x.keys() is referring to the keys of a dictionary or not.
- Optimizers. I believe this is actually the least important application, certainly initially. Optimizers like PyPy or Pyston <https://github.com/dropbox/pyston> wouldn't be able to fully trust the type annotations, and they are better off using their current strategy of optimizing code based on the types actually observed at run time. But it's certainly feasible to imagine a future optimizer also taking type annotations into account.
-- --Guido "I need a new hobby" van Rossum (python.org/~guido)
First, I am really happy that you are interested in this and that your point (2) of what you want to see done is very limited and acknowledges that it isn't going to specify everything! Because that isn't possible. :) Unfortunately I feel that adding syntax like this to the language itself is not useful without enforcement because it that leads to code being written with unintentionally incorrect annotations that winds up deployed in libraries that later become a problem as soon as an actual analysis tool attempts to run over something that uses that unknowingly incorrectly specified code in a place where it cannot be easily updated (like the standard library). At the summit in Montreal earlier this year Łukasz Langa (cc'd) volunteered to lead writing the PEP on Python type hinting based on the many existing implementations of such things (including mypy, cython, numba and pytypedecl <https://github.com/google/pytypedecl>). I believe he has an initial draft he intends to send out soon. I'll let him speak to that. Looks like Łukasz already responded, I'll stop writing now and go read that. :) Personal opinion from experience trying: You can't express the depth of types for an interface within the Python language syntax itself (assuming hacks such as specially formatted comments, strings or docstrings do not count). Forward references to things that haven't even been defined yet are common. You often want an ability to specify a duck type interface rather than a specific type. I think he has those points covered better than I do. -gps PS If anyone want to see a run time type checker make code run at half speed, look at the one pytypedecl <https://github.com/google/pytypedecl> offers. I'm sure it could be sped up, but run-time checkers in an interpreter are always likely to be slow.

On Wed, Aug 13, 2014 at 6:09 PM, Gregory P. Smith <greg@krypto.org> wrote:
First, I am really happy that you are interested in this and that your point (2) of what you want to see done is very limited and acknowledges that it isn't going to specify everything! Because that isn't possible. :)
What a shame. :-)
Unfortunately I feel that adding syntax like this to the language itself is not useful without enforcement because it that leads to code being written with unintentionally incorrect annotations that winds up deployed in libraries that later become a problem as soon as an actual analysis tool attempts to run over something that uses that unknowingly incorrectly specified code in a place where it cannot be easily updated (like the standard library).
We could refrain from using type annotations in the stdlib (similar to how we refrain from using Unicode identifiers). Mypy's stubs mechanism makes it possible to ship the type declarations for stdlib modules with mypy instead of baking them into the stdlib.
At the summit in Montreal earlier this year Łukasz Langa (cc'd) volunteered to lead writing the PEP on Python type hinting based on the many existing implementations of such things (including mypy, cython, numba and pytypedecl <https://github.com/google/pytypedecl>). I believe he has an initial draft he intends to send out soon. I'll let him speak to that.
Mypy has a lot more than an initial draft. Don't be mistaken by its status as "one person's Ph.D. project" -- Jukka has been thinking about this topic for a decade, and mypy works remarkably well already. It also has some very active contributors already.
Looks like Łukasz already responded, I'll stop writing now and go read that. :)
Personal opinion from experience trying: You can't express the depth of types for an interface within the Python language syntax itself (assuming hacks such as specially formatted comments, strings or docstrings do not count). Forward references to things that haven't even been defined yet are common. You often want an ability to specify a duck type interface rather than a specific type. I think he has those points covered better than I do.
I think mypy has solutions for the syntactic issues, and the rest can be addressed by introducing a few more magic helper functions. It's remarkably readable. -- --Guido van Rossum (python.org/~guido)

On 14 August 2014 15:11, Guido van Rossum <guido@python.org> wrote:
On Wed, Aug 13, 2014 at 6:09 PM, Gregory P. Smith <greg@krypto.org> wrote:
At the summit in Montreal earlier this year Łukasz Langa (cc'd) volunteered to lead writing the PEP on Python type hinting based on the many existing implementations of such things (including mypy, cython, numba and pytypedecl). I believe he has an initial draft he intends to send out soon. I'll let him speak to that.
Mypy has a lot more than an initial draft. Don't be mistaken by its status as "one person's Ph.D. project" -- Jukka has been thinking about this topic for a decade, and mypy works remarkably well already. It also has some very active contributors already.
FWIW, I was strongly encouraging folks at SciPy that were interested in static typing to look at mypy as an example of a potentially acceptable syntax for standardised optional static typing. Aside from being +1 on the general idea of picking *something* as "good enough" and iterating from there, I don't have a strong personal opinion, though. (And yes, my main interest is in improving the ability to do effective linting for larger projects. "pylint -E" is a lot better than nothing, but it has its limits in the absence of additional static hints) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Aug 13, 2014, at 10:11 PM, Guido van Rossum wrote:
We could refrain from using type annotations in the stdlib (similar to how we refrain from using Unicode identifiers). Mypy's stubs mechanism makes it possible to ship the type declarations for stdlib modules with mypy instead of baking them into the stdlib.
That would address my visceral reaction to the proposal. I haven't had time to read the entire thread, or supporting material, but my main concern is about readability. I don't think it's clear whether the impact of adding type annotations is a pure win or loss. OT1H maybe it helps because you would have to read less of the body of the code to guess what the implicit types are for the arguments and return values of a function. I myself often have to "annotate" (i.e. add comments) around code such as: # Map UUIDs to user object. users_by_uid = {} Admittedly, it's often quite difficult when reading foreign code to understand what are the types of the various constructs involved. It can involve reading more deeply into the call structure, or as I usually find, writing a bit of exploratory code and then just stepping through with the debugger. So type annotations could be a win here. OTOH there *is* a cost that could negatively impact readability. It's more text visually assaulting you. :) It can reduce the speed at which you can scan code. I'd say most of the time, you can pretty much deduce what's happening fairly easily (with molasses-inducing outliers like above) so type annotations may make things more confusing, especially if you have to mentally map between annotation types and run time types that are similar but not quite the same. If even more complex types are used, then you have to go digging into other material (docs or code) to understand what the author meant by some of the annotations. Type annotations likely increase line length or vertical real-estate, both of which can negatively impact readability. And if annotations are buggy, then it imposes even more cognitive dissonance. In practice, who knows whether wholesale adoption of type annotation in the stdlib *code* would be a net win or loss? I don't think it's at all clear. The other concern I'd have is whether annotating new stdlib code would become mandatory, and whether there'd be a wholesale campaign to annotate existing code. Using stubs and not baking them into the stdlib alleviates those concerns too. My gut reaction is more negative than positive but I remain open minded until I've had time to read more. Cheers, -Barry

Barry Warsaw writes:
OTOH there *is* a cost that could negatively impact readability. It's more text visually assaulting you. :)
Not if you have a syntax-highlighting editor that has a no-see-um face or text property.

On Aug 16, 2014, at 12:07 PM, Stephen J. Turnbull wrote:
Barry Warsaw writes:
OTOH there *is* a cost that could negatively impact readability. It's more text visually assaulting you. :)
Not if you have a syntax-highlighting editor that has a no-see-um face or text property.
Feel free to share your font-lock definitions <wink> -Barry

On Wednesday, August 13, 2014 12:45 PM, Guido van Rossum <guido@python.org> wrote:
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I just realized why this bothers me. This function really, really ought to be taking an Iterable[String] (except that we don't have a String ABC). If you hadn't statically typed it, it would work just fine with, say, a text file—or, for that matter, a binary file. By restricting it to List[str], you've made it a lot less usable, for no visible benefit. And, while this is less serious, I don't think it should be guaranteeing that the result is a Dict rather than just some kind of Mapping. If you want to change the implementation tomorrow to return some kind of proxy or a tree-based sorted mapping, you can't do so without breaking all the code that uses your function. And if even Guido, in the motivating example for this feature, is needlessly restricting the usability and future flexibility of a function, I suspect it may be a much bigger problem in practice. This example also shows exactly what's wrong with simple generics: if this function takes an Iterable[String], it doesn't just return a Mapping[String, int], it returns a Mapping of _the same String type_. If your annotations can't express that, any value that passes through this function loses type information. And not being able to tell whether the keys in word_count(f) are str or bytes *even if you know that f was a text file* seems like a pretty major loss.

On Aug 13, 2014, at 6:39 PM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Wednesday, August 13, 2014 12:45 PM, Guido van Rossum <guido@python.org> wrote:
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I just realized why this bothers me.
This function really, really ought to be taking an Iterable[String]
You do realize String also happens to be an Iterable[String], right? One of my big dreams about Python is that one day we'll drop support for strings being iterable. Nothing of value would be lost and that would enable us to use isinstance(x, Iterable) and more importantly isinstance(x, Sequence). Funny that this surfaces now, too. -- Best regards, Łukasz Langa WWW: http://lukasz.langa.pl/ Twitter: @llanga IRC: ambv on #python-dev

On Aug 13, 2014, at 18:56, Łukasz Langa <lukasz@langa.pl> wrote:
On Aug 13, 2014, at 6:39 PM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Wednesday, August 13, 2014 12:45 PM, Guido van Rossum <guido@python.org> wrote:
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I just realized why this bothers me.
This function really, really ought to be taking an Iterable[String]
You do realize String also happens to be an Iterable[String], right?
Of course, but that's not a new problem, so I didn't want to bring it up. The fact that the static type checker couldn't reject word_count(f.read()) is annoying, but that's not the fault of the static type checking proposal.
One of my big dreams about Python is that one day we'll drop support for strings being iterable. Nothing of value would be lost and that would enable us to use isinstance(x, Iterable) and more importantly isinstance(x, Sequence). Funny that this surfaces now, too.
IIRC, str doesn't implement Container, and therefore doesn't implement Sequence, because its __contains__ method is substring match instead of containment. So if you really want to treat sequences of strings separately from strings, you can. If only that really _were_ more important than Iterable, but I think the opposite is true. But anyway, this is probably off topic, so I'll stop here.

On Aug 13, 2014, at 8:08 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Aug 13, 2014, at 18:56, Łukasz Langa <lukasz@langa.pl> wrote:
One of my big dreams about Python is that one day we'll drop support for strings being iterable. Nothing of value would be lost and that would enable us to use isinstance(x, Iterable) and more importantly isinstance(x, Sequence). Funny that this surfaces now, too.
IIRC, str doesn't implement Container, and therefore doesn't implement Sequence, because its __contains__ method is substring match instead of containment. So if you really want to treat sequences of strings separately from strings, you can.
str and bytes objects respond True to both isinstance(x, Container) and isinstance(x, Sequence). But you’re right, off topic. -- Best regards, Łukasz Langa WWW: http://lukasz.langa.pl/ Twitter: @llanga IRC: ambv on #python-dev

A long while back I posted a recipe for using annotations for type checking. I'm certainly not the first person to do this, and what I did was deliberately simple: https://code.activestate.com/recipes/578528-type-checking-using-python-3x-an... The approach I used was to use per-function decorators to say that a given function should be type checked. The type system I enforce in that recipe is much less than what mypy allows, but I can't see a real reason that it couldn't be extended to cover exactly the same range of type specifiers. The advantage I perceive in this approach is that it is purely optional, per module and per function. As well, it doesn't actually require making ANY change to Python 3.5 to implement it. Or as a minimal change, an extra decorator could simply be available in functools or elsewhere in the standard library, which implemented the full semantics of mypy. Now admittedly, this would be type checking, but not *static* type checking. There may not be an easy way to make a pre-runtime "lint" tool do the checking there. On the other hand, as a number of posters have noted, there's also no way to enforce, e.g. 'Iterable[String]' either statically. I'm not the BDFL of course, but I do not really get what advantage there is to the pre-runtime check that can catch a fairly small subset of type constraints rather than check at runtime everything that is available then (as the decorator approach could get you). On Wed, Aug 13, 2014 at 8:16 PM, Łukasz Langa <lukasz@langa.pl> wrote:
On Aug 13, 2014, at 8:08 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Aug 13, 2014, at 18:56, Łukasz Langa <lukasz@langa.pl> wrote:
One of my big dreams about Python is that one day we'll drop support for strings being iterable. Nothing of value would be lost and that would enable us to use isinstance(x, Iterable) and more importantly isinstance(x, Sequence). Funny that this surfaces now, too.
IIRC, str doesn't implement Container, and therefore doesn't implement Sequence, because its __contains__ method is substring match instead of containment. So if you really want to treat sequences of strings separately from strings, you can.
str and bytes objects respond True to both isinstance(x, Container) and isinstance(x, Sequence).
But you’re right, off topic.
-- Best regards, Łukasz Langa
WWW: http://lukasz.langa.pl/ Twitter: @llanga IRC: ambv on #python-dev
_______________________________________________ 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 Wed, Aug 13, 2014 at 6:39 PM, Andrew Barnert < abarnert@yahoo.com.dmarc.invalid> wrote:
On Wednesday, August 13, 2014 12:45 PM, Guido van Rossum <guido@python.org> wrote:
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I just realized why this bothers me.
This function really, really ought to be taking an Iterable[String] (except that we don't have a String ABC). If you hadn't statically typed it, it would work just fine with, say, a text file—or, for that matter, a binary file. By restricting it to List[str], you've made it a lot less usable, for no visible benefit.
Heh. :-) I had wanted to write an additional paragraph explaining that it's easy to change this to use typing.Iterable instead of typing.List, but I forgot to add that.
And, while this is less serious, I don't think it should be guaranteeing that the result is a Dict rather than just some kind of Mapping. If you want to change the implementation tomorrow to return some kind of proxy or a tree-based sorted mapping, you can't do so without breaking all the code that uses your function.
Yeah, there's a typing.Mapping for that.
And if even Guido, in the motivating example for this feature, is needlessly restricting the usability and future flexibility of a function, I suspect it may be a much bigger problem in practice.
Well, so it was actually semi-intentional. :-)
This example also shows exactly what's wrong with simple generics: if this function takes an Iterable[String], it doesn't just return a Mapping[String, int], it returns a Mapping of _the same String type_. If your annotations can't express that, any value that passes through this function loses type information.
In most cases it really doesn't matter though -- some types are better left concrete, especially strings and numbers. If you read the mypy docs you'll find that there are generic types, so that it's possible to define a function as taking an Iterable[T] and returning a Mapping[T, int]. What's not currently possible is expressing additional constraints on T such as that it must be a String. When I last talked to Jukka he explained that he was going to add something for that too (@Jukka: structured types?).
And not being able to tell whether the keys in word_count(f) are str or bytes *even if you know that f was a text file* seems like a pretty major loss.
On this point one of us must be confused. Let's assume it's me. :-) Mypy has a few different IO types that can express the difference between text and binary files. I think there's some work that needs to be done (and of course the built-in open() function has a terribly ambiguous return type :-( ), but it should be possible to say that a text file is an Interable[str] and a binary file is an Iterable[bytes]. So together with the structured (?) types it should be possible to specify the signature of word_count() just as you want it. However, in most cases it's overkill, and you wouldn't want to do that for most code. Also, it probably wouldn't work for more realistic examples -- as soon as you replace the split() method call with something that takes punctuation into account, you're probably going to write it in a way that works only for text strings anyway, and very few people will want or need to write the polymorphic version. (But if they do, mypy has a handy @overload decorator that they can use. :-) Anyway, I agree it would be good to make sure that some of these more advanced things can actually be spelled before we freeze our commitment to a specific syntax, but let's not assume that just because you can't spell every possible generic use case it's no good. -- --Guido van Rossum (python.org/~guido)

On Wed, Aug 13, 2014 at 8:41 PM, Guido van Rossum <guido@python.org> wrote:
On Wed, Aug 13, 2014 at 6:39 PM, Andrew Barnert < abarnert@yahoo.com.dmarc.invalid> wrote:
This example also shows exactly what's wrong with simple generics: if this function takes an Iterable[String], it doesn't just return a Mapping[String, int], it returns a Mapping of _the same String type_. If your annotations can't express that, any value that passes through this function loses type information.
In most cases it really doesn't matter though -- some types are better left concrete, especially strings and numbers. If you read the mypy docs you'll find that there are generic types, so that it's possible to define a function as taking an Iterable[T] and returning a Mapping[T, int]. What's not currently possible is expressing additional constraints on T such as that it must be a String. When I last talked to Jukka he explained that he was going to add something for that too (@Jukka: structured types?).
I wrote another message where I touched this. Mypy is likely to support something like this in the future, but I doubt it's usually worth the complexity. If a type signature is very general, at some point it describes the implementation in sufficient detail that you can't modify the code without changing the type. For example, we could plausibly allow anything that just supports split(), but if we change the implementation to use something other than split(), the signature would have to change. If we use more specific types (such as str), we leave us the freedom to modify the implementation within the bounds of the str interface. Standard library functions often only accept concrete str objects, so the moment you start using an abstract string type you lose access to much of the stdlib.
And not being able to tell whether the keys in word_count(f) are str or bytes *even if you know that f was a text file* seems like a pretty major loss.
On this point one of us must be confused. Let's assume it's me. :-) Mypy has a few different IO types that can express the difference between text and binary files. I think there's some work that needs to be done (and of course the built-in open() function has a terribly ambiguous return type :-( ), but it should be possible to say that a text file is an Interable[str] and a binary file is an Iterable[bytes]. So together with the structured (?) types it should be possible to specify the signature of word_count() just as you want it. However, in most cases it's overkill, and you wouldn't want to do that for most code.
See my other message where I show that you can do this right now, except for the problem with open().
Also, it probably wouldn't work for more realistic examples -- as soon as you replace the split() method call with something that takes punctuation into account, you're probably going to write it in a way that works only for text strings anyway, and very few people will want or need to write the polymorphic version. (But if they do, mypy has a handy @overload decorator that they can use. :-)
Anyway, I agree it would be good to make sure that some of these more advanced things can actually be spelled before we freeze our commitment to a specific syntax, but let's not assume that just because you can't spell every possible generic use case it's no good.
It's always easy to come up with interesting corner cases where a type system would break down, but luckily, these are often almost non-existent in the wild :-) I've learned that examples should be motivated by patterns in existing, 'real' code, as otherwise you'll waste your time on things that happen maybe once a million lines (or maybe only in code that *you* write). Jukka
-- --Guido van Rossum (python.org/~guido)

On Wed, Aug 13, 2014 at 6:39 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Wednesday, August 13, 2014 12:45 PM, Guido van Rossum <guido@python.org> wrote:
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I just realized why this bothers me.
This function really, really ought to be taking an Iterable[String] (except that we don't have a String ABC). If you hadn't statically typed it, it would work just fine with, say, a text file—or, for that matter, a binary file. By restricting it to List[str], you've made it a lot less usable, for no visible benefit.
And, while this is less serious, I don't think it should be guaranteeing that the result is a Dict rather than just some kind of Mapping. If you want to change the implementation tomorrow to return some kind of proxy or a tree-based sorted mapping, you can't do so without breaking all the code that uses your function.
I see this is a matter of programming style. In a library module, I'd usually use about as general types as feasible (without making them overly complex). However, if we have just a simple utility function that's only used within a single program, declaring everything using abstract types buys you little, IMHO, but may make things much more complicated. You can always refactor the code to use more general types if the need arises. Using simple, concrete types seems to decrease the cognitive load, but that's just my experience. Also, programmers don't always read documentation/annotations and can abuse the knowledge of the concrete return type of any function (they can figure this out easily by using repr()/type()). In general, as long as dynamically typed programs may call your function, changing the concrete return type of a library function risks breaking code that makes too many assumptions. Thus I'd rather use concrete types for function return types -- but of course everybody is free to not follow this convention.
And if even Guido, in the motivating example for this feature, is needlessly restricting the usability and future flexibility of a function, I suspect it may be a much bigger problem in practice.
This example also shows exactly what's wrong with simple generics: if this function takes an Iterable[String], it doesn't just return a Mapping[String, int], it returns a Mapping of _the same String type_. If your annotations can't express that, any value that passes through this function loses type information.
If I define a subclass X of str, split() still returns a List[str] rather than List[X], unless I override something, so this wouldn't work with the above example:
class X(str): pass ... type(X('x y').split()[0]) <class 'str'>
And not being able to tell whether the keys in word_count(f) are str or bytes *even if you know that f was a text file* seems like a pretty major loss.
Mypy considers bytes incompatible with str, and vice versa. The annotation Iterable[str] says that Iterable[bytes] (such as a binary file) would not be a valid argument. Text files and binary files have different types, though the return type of open(...) is not inferred correctly right now. It would be easy to fix this for the most common cases, though. You could use AnyStr to make the example work with bytes as well: def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result Again, if this is just a simple utility function that you use once or twice, I see no reason to spend a lot of effort in coming up with the most general signature. Types are an abstraction and they can't express everything precisely -- there will always be a lot of cases where you can't express the most general type. However, I think that relatively simple types work well enough most of the time, and give the most bang for the buck. Jukka

On Wed, Aug 13, 2014 at 9:06 PM, Jukka Lehtosalo <jlehtosalo@gmail.com> wrote:
You could use AnyStr to make the example work with bytes as well:
def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int]
for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Again, if this is just a simple utility function that you use once or twice, I see no reason to spend a lot of effort in coming up with the most general signature. Types are an abstraction and they can't express everything precisely -- there will always be a lot of cases where you can't express the most general type. However, I think that relatively simple types work well enough most of the time, and give the most bang for the buck.
I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.) -- --Guido van Rossum (python.org/~guido)

I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.)
To borrow Scala syntax, it would look something like def word_count[AnyStr <: String](input: Iterable[AnyStr]): Dict[AnyStr, Int] Where *word_count* is a generic function on the type *AnyStr*, which is not just *any* type but a type bound by the restriction it is a subclass of *String*. Thus you can force that the *AnyStr* going in and the *AnyStr* going out are the same one. I'm not sure if mypy allows for type-bounds, but that's the way you achieve what you want if it does, or will, in future =P On Wed, Aug 13, 2014 at 10:24 PM, Guido van Rossum <guido@python.org> wrote:
On Wed, Aug 13, 2014 at 9:06 PM, Jukka Lehtosalo <jlehtosalo@gmail.com> wrote:
You could use AnyStr to make the example work with bytes as well:
def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int]
for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Again, if this is just a simple utility function that you use once or twice, I see no reason to spend a lot of effort in coming up with the most general signature. Types are an abstraction and they can't express everything precisely -- there will always be a lot of cases where you can't express the most general type. However, I think that relatively simple types work well enough most of the time, and give the most bang for the buck.
I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.)
-- --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/

I'd be more interested in Jukka's specific proposal. (Note that in mypy, AnyStr is the type that you call String here -- it's either bytes or str; and it seems that you're introducing AnyStr here as a type variable -- mypy conventionally uses T for this, but you have to define it first.) On Wed, Aug 13, 2014 at 10:39 PM, Haoyi Li <haoyi.sg@gmail.com> wrote:
I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.)
To borrow Scala syntax, it would look something like
def word_count[AnyStr <: String](input: Iterable[AnyStr]): Dict[AnyStr, Int]
Where *word_count* is a generic function on the type *AnyStr*, which is not just *any* type but a type bound by the restriction it is a subclass of *String*. Thus you can force that the *AnyStr* going in and the *AnyStr* going out are the same one.
I'm not sure if mypy allows for type-bounds, but that's the way you achieve what you want if it does, or will, in future =P
On Wed, Aug 13, 2014 at 10:24 PM, Guido van Rossum <guido@python.org> wrote:
On Wed, Aug 13, 2014 at 9:06 PM, Jukka Lehtosalo <jlehtosalo@gmail.com> wrote:
You could use AnyStr to make the example work with bytes as well:
def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int]
for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Again, if this is just a simple utility function that you use once or twice, I see no reason to spend a lot of effort in coming up with the most general signature. Types are an abstraction and they can't express everything precisely -- there will always be a lot of cases where you can't express the most general type. However, I think that relatively simple types work well enough most of the time, and give the most bang for the buck.
I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.)
-- --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/
-- --Guido van Rossum (python.org/~guido)

Guido van Rossum schrieb am 14.08.2014 um 07:24:
On Wed, Aug 13, 2014 at 9:06 PM, Jukka Lehtosalo wrote:
You could use AnyStr to make the example work with bytes as well:
def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int]
for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Again, if this is just a simple utility function that you use once or twice, I see no reason to spend a lot of effort in coming up with the most general signature. Types are an abstraction and they can't express everything precisely -- there will always be a lot of cases where you can't express the most general type. However, I think that relatively simple types work well enough most of the time, and give the most bang for the buck.
I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.)
That's how Cython's "fused types" (generics) work, at least. They go by name: same name of the type, same type. Otherwise, use alias names, which make the types independent from each other. http://docs.cython.org/src/userguide/fusedtypes.html While it's a matter of definition what way to go here (same type or not), practice has shown that it's clearly the right decision to make identical types the default. Stefan

On Thu, Aug 14, 2014 at 9:12 PM, Stefan Behnel <stefan_ml@behnel.de> wrote:
Guido van Rossum schrieb am 14.08.2014 um 07:24:
On Wed, Aug 13, 2014 at 9:06 PM, Jukka Lehtosalo wrote:
You could use AnyStr to make the example work with bytes as well:
def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int]
for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Again, if this is just a simple utility function that you use once or twice, I see no reason to spend a lot of effort in coming up with the most general signature. Types are an abstraction and they can't express everything precisely -- there will always be a lot of cases where you can't express the most general type. However, I think that relatively simple types work well enough most of the time, and give the most bang for the buck.
I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.)
That's how Cython's "fused types" (generics) work, at least. They go by name: same name of the type, same type. Otherwise, use alias names, which make the types independent from each other.
http://docs.cython.org/src/userguide/fusedtypes.html
While it's a matter of definition what way to go here (same type or not), practice has shown that it's clearly the right decision to make identical types the default.
I don't understand those docs at all, but I do think I understand the rule "same name, same type" and I think I like it. Let me be clear -- in this example: def word_count(input: Iterable[AnyStr]) -> Mapping[AnyStr, int]: ... the implication would be that if the input is Iterable[bytes] the output is Mapping[bytes, int] while if the input is Iterable[str] the output is Mapping[str, int]. Have I got that right? I hope so, because I think it is a nice simplifying rule that covers a lot of cases in practice. (Note: AnyStr is a predefined type in mypy that means "str or bytes".) BTW there are a lot of messy things to consider around bytes, and IIUC mypy currently doesn't really cover them. Often when you write code that accepts a bytes instance, in practice it will accept anything that supports the buffer protocol (e.g. bytearray and memoryview). Except when you are going to use it as a dict key, then bytearray won't work. And if you say that you are returning bytes, you probably shouldn't be returning a memoryview or bytearray. I don't expect that any type system we can come up with will be quite precise enough to cover all the cases, so we probably shouldn't lose too much sleep over this. -- --Guido van Rossum (python.org/~guido)

Guido van Rossum schrieb am 15.08.2014 um 06:34:
On Thu, Aug 14, 2014 at 9:12 PM, Stefan Behnel wrote:
Guido van Rossum schrieb am 14.08.2014 um 07:24:
On Wed, Aug 13, 2014 at 9:06 PM, Jukka Lehtosalo wrote:
You could use AnyStr to make the example work with bytes as well:
def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int]
for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Again, if this is just a simple utility function that you use once or twice, I see no reason to spend a lot of effort in coming up with the most general signature. Types are an abstraction and they can't express everything precisely -- there will always be a lot of cases where you can't express the most general type. However, I think that relatively simple types work well enough most of the time, and give the most bang for the buck.
I heartily agree. But just for the type theorists amongst us, if I really wanted to write the most general type, how would I express that the AnyStr in the return type matches the one in the argument? (I think pytypedecl would use something like T <= AnyStr.)
That's how Cython's "fused types" (generics) work, at least. They go by name: same name of the type, same type. Otherwise, use alias names, which make the types independent from each other.
http://docs.cython.org/src/userguide/fusedtypes.html
While it's a matter of definition what way to go here (same type or not), practice has shown that it's clearly the right decision to make identical types the default.
I don't understand those docs at all
I'm not surprised. ;) The main idea is that you declare (typedef) a "fused" type that means "any of the following list of types". Then you use it in a function signature and the compiler explodes it into multiple specialised implementations that get separately optimised for the specific type(s) they use. Compile time generic functions, essentially. You get the cross product of all different fused types that your function uses, but in practice, you almost always want only one specialisation for each type, regardless of how often you used it in the argument list.
but I do think I understand the rule "same name, same type" and I think I like it. Let me be clear -- in this example:
def word_count(input: Iterable[AnyStr]) -> Mapping[AnyStr, int]: ...
the implication would be that if the input is Iterable[bytes] the output is Mapping[bytes, int] while if the input is Iterable[str] the output is Mapping[str, int]. Have I got that right? I hope so, because I think it is a nice simplifying rule that covers a lot of cases in practice.
Yes, absolutely. One caveat for Python: static analysis tools (including Cython) will usually have the AST available and thus see the type name used. Once the annotation is in the __annotations__ dict, however, it's lost and reduced to the base type object instance. So renaming types would have to be an explicit operation that the type object knows about. Otherwise, you'd loose semantics at runtime (not sure it matters much in practice, but it would when used for documentation purposes). Not very DRY, but as I said, the hugely more normal case is to want all types the same.
BTW there are a lot of messy things to consider around bytes, and IIUC mypy currently doesn't really cover them. Often when you write code that accepts a bytes instance, in practice it will accept anything that supports the buffer protocol (e.g. bytearray and memoryview).
Yes, totally. I've been teaching people that for years now, but it's so much easier for them to write "x: bytes" than to remember to be forgiving about input and think about what that means for their specific code. Not typing the input at all is actually the best solution in many cases, but getting that into the head of users who are just discovering the beauty of an optionally typed Python language is a true up-hill battle. Sometimes I even encourage them to use memory views instead of expecting "real" byte string input, even though that can make working with the data less "stringish". But it's what the users of their code will want. Cython essentially uses NumPy-ish syntax for (compile time) memory views, i.e. you'd write "int[:] x" to unpack a 1-dimensional buffer of item type C int, or "unsigned char[:] b" for a plain uchar buffer. Here's a bunch of examples: http://docs.cython.org/src/userguide/memoryviews.html This is a very well established syntax by now, with lots of code out there using it. Makes working with arbitrary buffer providers a charm. Note that Cython has its own C level memory view implementation, so this is way more efficient than Python's generic memoryview objects (but PEP 3118 based, so compatible).
Except when you are going to use it as a dict key, then bytearray won't work. And if you say that you are returning bytes, you probably shouldn't be returning a memoryview or bytearray.
Right. Hashability, strict output, all that.
I don't expect that any type system we can come up with will be quite precise enough to cover all the cases, so we probably shouldn't lose too much sleep over this.
Well, Cython's type system has pretty much all you'd need. But it's linked to the compiler in the sense that some features exist because Cython can do them at compile time. Not everything can be done in pure Python at runtime or import time. Stefan

On Aug 13, 2014, at 21:06, Jukka Lehtosalo <jlehtosalo@gmail.com> wrote:
You could use AnyStr to make the example work with bytes as well:
def word_count(input: Iterable[AnyStr]) -> Dict[AnyStr, int]: result = {} #type: Dict[AnyStr, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Defining a function as taking an Iterable[AnyStr] and returning a Dict[AnyStr, int], without any way to declare that the two AnyStr are the same type, is exactly what I meant by losing critical information. I pass in something that I know is a text file, and I get out something that are either strings or bytes and I don't know which; what am I going to do with that? If you can't propagate types, static typing doesn't help. My point is that the depth you seem to be reaching for is not actually a sweet spot for power vs. simplicity. You could make it simpler by just dropping generics in favor of a handful of special-purpose modifiers (optional, iterable-of), or you could make it more useful by having real parametric types, or at least the equivalent of C++ template templates or Swift arbitrary constraints. What makes Java-style simple generics with subclass constraints the right level of complexity?

Guido, as requesting, I read your whole post before replying. Please to the same. This response is both critical and supportive. On 8/13/2014 3:44 PM, Guido van Rossum wrote:
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/).
My main concern with static typing is that it tends to be anti-duck-typing, while I consider duck-typing to be a major *feature* of Python. The example in the page above is "def fib(n: int):". Fib should get an count (non-negative integer) value, but it need not be an int, and 'half' the ints do not qualify. Reading the tutorial, I could not tell if it supports numbers.Number (which should approximate the domain from above.) Now consider an extended version (after Lucas). def fib(n, a, b): i = 0 while i <= n: print(i,a) i += 1 a, b = b, a+b The only requirement of a, b is that they be addable. Any numbers should be allowed, as in fib(10, 1, 1+1j), but so should fib(5, '0', '1'). Addable would be approximated from below by Union(Number, str).
Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online (https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations
-+ Syntax with no meaning is a bit strange. On the other hand, syntax not bound to semantics, or at least not bound to just one meaning is quite pythonic. '+' has two standard meanings, plus custom meanings embodied in .__add__ methods. + The current semantics of annotations is that they are added to functions objects as .__annotations__ (for whatever use) *and* used as part of inspect.signature and included in help(ob) responses. In other words, annotations are already used in the stdlib.
def f(i:int) -> float: pass
from inspect import signature as sig str(sig(f)) '(i:int) -> float' help(f) Help on function f in module __main__:
f(i:int) -> float Idle calltips include them also. A appropriately flexible standardized notation would enhance this usage and many others. +-+ I see the point of "The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter." On the other hand, "pip install mypytyping" is not a huge burden. On the third hand, in the stdlib allows use in the stdlib.
(b) Python's use of mutabe [mutable] containers by default is wrong
The premise of this is partly wrong and partly obsolete. As far as I can remember, Python *syntax* only use tuples, not lists: "except (ex1, ex2):", "s % (val1, val2)", etc. The use of lists as the common format for data interchange between functions has largely been replaced by iterators. This fact makes Python code much more generic, and anti-generic static typing more wrong. In remaining cases, 'wrong' is as much a philosophical opinion as a fact.
(c) Python should adopt some kind of Abstract Data Types
I would have to look at the talk to know what Jukka means.
Proposals (b) and (c) don't feel particularly actionable (if you disagree please start a new thread, I'd be happy to discuss these further if there's interest) but proposal (a) feels right to me.
So what is mypy? It is a static type checker for Python written by Jukka for his Ph.D. thesis. The basic idea is that you add type annotations to your program using some custom syntax, and when running your program using the mypy interpreter, type errors will be found during compilation (i.e., before the program starts running).
The clever thing here is that the custom syntax is actually valid Python 3, using (mostly) function annotations: your annotated program will still run with the regular Python 3 interpreter. In the latter case there will be no type checking, and no runtime overhead, except to evaluate the function annotations (which are evaluated at function definition time but don't have any effect when the function is called).
In fact, it is probably more useful to think of mypy as a heavy-duty linter than as a compiler or interpreter; leave the type checking to mypy, and the execution to Python. It is easy to integrate mypy into a continuous integration setup, for example.
To read up on mypy's annotation syntax, please see the mypy-lang.org <http://mypy-lang.org> website.
I did not see a 'reference' page, but the tutorial comes pretty close. http://mypy-lang.org/tutorial.html Beyond that, typings.py would be definitive, https://github.com/JukkaL/mypy/blob/master/lib-typing/3.2/typing.py
Here's just one complete example, to give a flavor:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]:
The input annotation should be Iterable[str], which mypy does have.
result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
The information that input is an Iterable[str] can be used either within the definition of word_count or at places where word_count is called. A type aware checker, either in the editor or compiler, could check that the only uses of 'input' within the function is as input to functions declared to accept an Iterable or in for statements. Checking that the input to word_count is specifically Iterable[str] as opposed to any other Iterable may not be possible. But I think what can be done, including enhancing help information, might be worth it. For instance, the parameter to s.join is named 'iterable'. Something more specific, either 'iterable_of_strings' or 'strings: Iterable[str]' would be more helpful. Indeed, there have been people posting on python list who thought that 'iterable' means iterable and that .join would call str() on each object. I think there are other cases where a parameter is given a bland under-informative type name instead of a context-specific semantic name just because there was no type annotation available. There are places where the opposite problem occurs, too specific instead of too general, where iterable parameters are still called 'list'.
Note that the #type: comment is part of the mypy syntax; mypy uses comments to declare types in situations where no syntax is available -- although this particular line could also be written as follows:
result = Dict[str, int]()
Either way the entire function is syntactically valid Python 3, and a suitable implementation of typing.py (containing class definitions for List and Dict, for example) can be written to make the program run correctly. One is provided as part of the mypy project.
I should add that many of mypy's syntactic choices aren't actually new. The basis of many of its ideas go back at least a decade: I blogged about this topic in 2004 (http://www.artima.com/weblogs/viewpost.jsp?thread=85551 -- see also the two followup posts linked from the top there).
I'll emphasize once more that mypy's type checking happens in a separate pass: no type checking happens at run time (other than what the interpreter already does, like raising TypeError on expressions like 1+"1").
There's a lot to this proposal, but I think it's possible to get a PEP written, accepted and implemented in time for Python 3.5, if people are supportive. I'll go briefly over some of the action items.
*(1) A change of direction for function annotations*
PEP 3107 <http://legacy.python.org/dev/peps/pep-3107/>, which introduced function annotations, is intentional non-committal about how function annotations should be used. It lists a number of use cases, including but not limited to type checking. It also mentions some rejected proposals that would have standardized either a syntax for indicating types and/or a way for multiple frameworks to attach different annotations to the same function. AFAIK in practice there is little use of function annotations in mainstream code, and I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them.
There are many uses for type information and I think Python should remain neutral among them.
(We may have to have some backwards compatibility provision to avoid breaking code that currently uses annotations for some other purpose. Fortunately the only issue, at least initially, will be that when running mypy to type check such code it will produce complaints about the annotations; it will not affect how such code is executed by the Python interpreter. Nevertheless, it would be good to deprecate such alternative uses of annotations.)
I can imagine that people who have used annotations might feel a bit betrayed by deprecation of a new-in-py3 feature. But I do not think it necessary to do so. Tools that work with mypy annotations, including mypy itself, should only assume mypy typing if typing is imported. No 'import typing', no 'Warning: annotation does not follow typing rules." If 'typing' were a package with a 'mypy' module, the door would be left open to other 'blessed' typing modules.
*(2) A specification for what to add to Python 3.5*
There needs to be at least a rough consensus on the syntax for annotations, and the syntax must cover a large enough set of use cases to be useful. Mypy is still under development, and some of its features are still evolving (e.g. unions were only added a few weeks ago). It would be possible to argue endlessly about details of the notation, e.g. whether to use 'list' or 'List', what either of those means (is a duck-typed list-like type acceptable?) or how to declare and use type variables, and what to do with functions that have no annotations at all (mypy currently skips those completely).
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter. The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module. This module defines several dozen new classes (and a few decorators and other helpers) that can be used in expressing argument types. If you want to type-check your code you have to download and install mypy and run it separately.
The curious thing here is that while standardizing a syntax for type annotations, we technically still won't be adopting standard rules for type checking.
Fine with me, as that is not the only use. And even for type checking, there is the choice between accept unless clearly wrong, versus reject unless clearly right.
This is intentional. First of all, fully specifying all the type checking rules would make for a really long and boring PEP (a much better specification would probably be the mypy source code). Second, I think it's fine if the type checking algorithm evolves over time, or if variations emerge.
As in the choice between accept unless clearly wrong, versus reject unless clearly right.
The worst that can happen is that you consider your code correct but mypy disagrees; your code will still run.
That said, I don't want to /completely/ leave out any specification. I want the contents of the typing.py module to be specified in the PEP, so that it can be used with confidence. But whether mypy will complain about your particular form of duck typing doesn't have to be specified by the PEP. Perhaps as mypy evolves it will take options to tell it how to handle certain edge cases. Forks of mypy (or entirely different implementations of type checking based on the same annotation syntax) are also a possibility. Maybe in the distant future a version of Python will take a different stance, once we have more experience with how this works out in practice, but for Python 3.5 I want to restrict the scope of the upheaval.
As usual, we should review the code before acceptance. It is not clear to me how much of the tutorial is implemented, as it says "Some of these features might never see the light of day. " ???
*Appendix -- Why Add Type Annotations? * The argument between proponents of static typing and dynamic typing has been going on for many decades. Neither side is all wrong or all right. Python has traditionally fallen in the camp of extremely dynamic typing, and this has worked well for most users, but there are definitely some areas where adding type annotations would help.
The answer to why on the mypy page is 'easier to find bugs', 'easier maintenance'. I find this under-convincing as sufficient justification in itself. I don't think there are many bugs on the tracker due to calling functions with the wrong type of object. Logic errors, ignored corner cases, and system idiosyncrasies are much more of a problem. Your broader list is more convincing.
- Editors (IDEs) can benefit from type annotations; they can call out obvious mistakes (like misspelled method names or inapplicable operations) and suggest possible method names. Anyone who has used IntelliJ or Xcode will recognize how powerful these features are, and type annotations will make such features more useful when editing Python source code.
- Linters are an important tool for teams developing software. A linter doesn't replace a unittest, but can find certain types of errors better or quicker. The kind of type checking offered by mypy works much like a linter, and has similar benefits; but it can find problems that are beyond the capabilities of most linters.
Currently, Python linters do not have standard type annotations to work with. I suspect that programs other than mypy would use them if available.
- Type annotations are useful for the human reader as well! Take the above word_count() example. How long would it have taken you to figure out the types of the argument and return value without annotations?
Under a minute, including the fact the the annotation was overly restrictive. But then I already know that only a mutation method can require a list.
Currently most people put the types in their docstrings; developing a standard notation for type annotations will reduce the amount of documentation that needs to be written, and running the type checker might find bugs in the documentation, too. Once a standard type annotation syntax is introduced, it should be simple to add support for this notation to documentation generators like Sphinx.
- Refactoring. Bob's talk has a convincing example of how type annotations help in (manually) refactoring code. I also expect that certain automatic refactorings will benefit from type annotations -- imagine a tool like 2to3 (but used for some other transformation) augmented by type annotations, so it will know whether e.g. x.keys() is referring to the keys of a dictionary or not.
- Optimizers. I believe this is actually the least important application, certainly initially. Optimizers like PyPy or Pyston <https://github.com/dropbox/pyston> wouldn't be able to fully trust the type annotations, and they are better off using their current strategy of optimizing code based on the types actually observed at run time. But it's certainly feasible to imagine a future optimizer also taking type annotations into account.
-- Terry Jan Reedy

I'm very happy to see this happening. "Optional" type checking for python would be a great addition to the language. For large codebases, use of type checking can really help. I also like the idea of using annotations instead of decorators (or something like that). I'm already using this for python3 [1] in a non intrusive way, by using assert to disable it on production. [1] https://pypi.python.org/pypi/optypecheck -- Carlo Pires

On Wed, Aug 13, 2014 at 9:27 PM, Terry Reedy <tjreedy@udel.edu> wrote:
Guido, as requesting, I read your whole post before replying. Please to the same. This response is both critical and supportive.
On 8/13/2014 3:44 PM, Guido van Rossum wrote:
Yesterday afternoon I had an inspiring conversation with Bob Ippolito
(man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/).
My main concern with static typing is that it tends to be anti-duck-typing, while I consider duck-typing to be a major *feature* of Python. The example in the page above is "def fib(n: int):". Fib should get an count (non-negative integer) value, but it need not be an int, and 'half' the ints do not qualify. Reading the tutorial, I could not tell if it supports numbers.Number (which should approximate the domain from above.)
Very true; this is one of my fears. There are plenty of people who adore static typing and will make everything in all their libraries static with static this and static that. Maybe there could be a way to disable type checks?
Now consider an extended version (after Lucas).
def fib(n, a, b): i = 0 while i <= n: print(i,a) i += 1 a, b = b, a+b
The only requirement of a, b is that they be addable. Any numbers should be allowed, as in fib(10, 1, 1+1j), but so should fib(5, '0', '1'). Addable would be approximated from below by Union(Number, str).
Unless MyPy added some sort of type classes...
Bob gave a talk at EuroPython about
what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online (https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations
-+ Syntax with no meaning is a bit strange. On the other hand, syntax not bound to semantics, or at least not bound to just one meaning is quite pythonic. '+' has two standard meanings, plus custom meanings embodied in .__add__ methods.
+ The current semantics of annotations is that they are added to functions objects as .__annotations__ (for whatever use) *and* used as part of inspect.signature and included in help(ob) responses. In other words, annotations are already used in the stdlib.
def f(i:int) -> float: pass
from inspect import signature as sig str(sig(f)) '(i:int) -> float' help(f) Help on function f in module __main__:
f(i:int) -> float
Idle calltips include them also. A appropriately flexible standardized notation would enhance this usage and many others.
+-+ I see the point of "The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter." On the other hand, "pip install mypytyping" is not a huge burden. On the third hand, in the stdlib allows use in the stdlib.
(b) Python's use of mutabe [mutable] containers by default is wrong
The premise of this is partly wrong and partly obsolete. As far as I can remember, Python *syntax* only use tuples, not lists: "except (ex1, ex2):", "s % (val1, val2)", etc. The use of lists as the common format for data interchange between functions has largely been replaced by iterators. This fact makes Python code much more generic, and anti-generic static typing more wrong.
In remaining cases, 'wrong' is as much a philosophical opinion as a fact.
(c) Python should adopt some kind of Abstract Data Types
I would have to look at the talk to know what Jukka means.
Proposals (b) and (c) don't feel particularly actionable (if you
disagree please start a new thread, I'd be happy to discuss these further if there's interest) but proposal (a) feels right to me.
So what is mypy? It is a static type checker for Python written by
Jukka for his Ph.D. thesis. The basic idea is that you add type annotations to your program using some custom syntax, and when running your program using the mypy interpreter, type errors will be found during compilation (i.e., before the program starts running).
The clever thing here is that the custom syntax is actually valid Python 3, using (mostly) function annotations: your annotated program will still run with the regular Python 3 interpreter. In the latter case there will be no type checking, and no runtime overhead, except to evaluate the function annotations (which are evaluated at function definition time but don't have any effect when the function is called).
In fact, it is probably more useful to think of mypy as a heavy-duty linter than as a compiler or interpreter; leave the type checking to mypy, and the execution to Python. It is easy to integrate mypy into a continuous integration setup, for example.
To read up on mypy's annotation syntax, please see the mypy-lang.org <http://mypy-lang.org> website.
I did not see a 'reference' page, but the tutorial comes pretty close. http://mypy-lang.org/tutorial.html Beyond that, typings.py would be definitive, https://github.com/JukkaL/mypy/blob/master/lib-typing/3.2/typing.py
Here's just one complete example, to give a flavor:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]:
The input annotation should be Iterable[str], which mypy does have.
result = {} #type: Dict[str, int]
for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
The information that input is an Iterable[str] can be used either within the definition of word_count or at places where word_count is called. A type aware checker, either in the editor or compiler, could check that the only uses of 'input' within the function is as input to functions declared to accept an Iterable or in for statements.
Checking that the input to word_count is specifically Iterable[str] as opposed to any other Iterable may not be possible. But I think what can be done, including enhancing help information, might be worth it.
For instance, the parameter to s.join is named 'iterable'. Something more specific, either 'iterable_of_strings' or 'strings: Iterable[str]' would be more helpful. Indeed, there have been people posting on python list who thought that 'iterable' means iterable and that .join would call str() on each object. I think there are other cases where a parameter is given a bland under-informative type name instead of a context-specific semantic name just because there was no type annotation available. There are places where the opposite problem occurs, too specific instead of too general, where iterable parameters are still called 'list'.
Note that the #type: comment is part of the mypy syntax; mypy uses
comments to declare types in situations where no syntax is available -- although this particular line could also be written as follows:
result = Dict[str, int]()
Either way the entire function is syntactically valid Python 3, and a suitable implementation of typing.py (containing class definitions for List and Dict, for example) can be written to make the program run correctly. One is provided as part of the mypy project.
I should add that many of mypy's syntactic choices aren't actually new. The basis of many of its ideas go back at least a decade: I blogged about this topic in 2004 (http://www.artima.com/weblogs/viewpost.jsp?thread=85551 -- see also the two followup posts linked from the top there).
I'll emphasize once more that mypy's type checking happens in a separate pass: no type checking happens at run time (other than what the interpreter already does, like raising TypeError on expressions like 1+"1").
There's a lot to this proposal, but I think it's possible to get a PEP written, accepted and implemented in time for Python 3.5, if people are supportive. I'll go briefly over some of the action items.
*(1) A change of direction for function annotations*
PEP 3107 <http://legacy.python.org/dev/peps/pep-3107/>, which introduced
function annotations, is intentional non-committal about how function annotations should be used. It lists a number of use cases, including but not limited to type checking. It also mentions some rejected proposals that would have standardized either a syntax for indicating types and/or a way for multiple frameworks to attach different annotations to the same function. AFAIK in practice there is little use of function annotations in mainstream code, and I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them.
There are many uses for type information and I think Python should remain neutral among them.
(We may have to have some backwards compatibility provision to avoid
breaking code that currently uses annotations for some other purpose. Fortunately the only issue, at least initially, will be that when running mypy to type check such code it will produce complaints about the annotations; it will not affect how such code is executed by the Python interpreter. Nevertheless, it would be good to deprecate such alternative uses of annotations.)
I can imagine that people who have used annotations might feel a bit betrayed by deprecation of a new-in-py3 feature. But I do not think it necessary to do so. Tools that work with mypy annotations, including mypy itself, should only assume mypy typing if typing is imported. No 'import typing', no 'Warning: annotation does not follow typing rules." If 'typing' were a package with a 'mypy' module, the door would be left open to other 'blessed' typing modules.
*(2) A specification for what to add to Python 3.5*
There needs to be at least a rough consensus on the syntax for annotations, and the syntax must cover a large enough set of use cases to be useful. Mypy is still under development, and some of its features are still evolving (e.g. unions were only added a few weeks ago). It would be possible to argue endlessly about details of the notation, e.g. whether to use 'list' or 'List', what either of those means (is a duck-typed list-like type acceptable?) or how to declare and use type variables, and what to do with functions that have no annotations at all (mypy currently skips those completely).
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter. The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module. This module defines several dozen new classes (and a few decorators and other helpers) that can be used in expressing argument types. If you want to type-check your code you have to download and install mypy and run it separately.
The curious thing here is that while standardizing a syntax for type annotations, we technically still won't be adopting standard rules for type checking.
Fine with me, as that is not the only use. And even for type checking, there is the choice between accept unless clearly wrong, versus reject unless clearly right.
This is intentional. First of all, fully specifying all
the type checking rules would make for a really long and boring PEP (a much better specification would probably be the mypy source code). Second, I think it's fine if the type checking algorithm evolves over time, or if variations emerge.
As in the choice between accept unless clearly wrong, versus reject unless clearly right.
The worst that can happen is that you
consider your code correct but mypy disagrees; your code will still run.
That said, I don't want to /completely/ leave out any specification. I
want the contents of the typing.py module to be specified in the PEP, so that it can be used with confidence. But whether mypy will complain about your particular form of duck typing doesn't have to be specified by the PEP. Perhaps as mypy evolves it will take options to tell it how to handle certain edge cases. Forks of mypy (or entirely different implementations of type checking based on the same annotation syntax) are also a possibility. Maybe in the distant future a version of Python will take a different stance, once we have more experience with how this works out in practice, but for Python 3.5 I want to restrict the scope of the upheaval.
As usual, we should review the code before acceptance. It is not clear to me how much of the tutorial is implemented, as it says "Some of these features might never see the light of day. " ???
*Appendix -- Why Add Type Annotations?
* The argument between proponents of static typing and dynamic typing has been going on for many decades. Neither side is all wrong or all right. Python has traditionally fallen in the camp of extremely dynamic typing, and this has worked well for most users, but there are definitely some areas where adding type annotations would help.
The answer to why on the mypy page is 'easier to find bugs', 'easier maintenance'. I find this under-convincing as sufficient justification in itself. I don't think there are many bugs on the tracker due to calling functions with the wrong type of object. Logic errors, ignored corner cases, and system idiosyncrasies are much more of a problem.
Your broader list is more convincing.
- Editors (IDEs) can benefit from type annotations; they can call out
obvious mistakes (like misspelled method names or inapplicable operations) and suggest possible method names. Anyone who has used IntelliJ or Xcode will recognize how powerful these features are, and type annotations will make such features more useful when editing Python source code.
- Linters are an important tool for teams developing software. A linter doesn't replace a unittest, but can find certain types of errors better or quicker. The kind of type checking offered by mypy works much like a linter, and has similar benefits; but it can find problems that are beyond the capabilities of most linters.
Currently, Python linters do not have standard type annotations to work with. I suspect that programs other than mypy would use them if available.
- Type annotations are useful for the human reader as well! Take the
above word_count() example. How long would it have taken you to figure out the types of the argument and return value without annotations?
Under a minute, including the fact the the annotation was overly restrictive. But then I already know that only a mutation method can require a list.
Currently most people put the types in their docstrings; developing a
standard notation for type annotations will reduce the amount of documentation that needs to be written, and running the type checker might find bugs in the documentation, too. Once a standard type annotation syntax is introduced, it should be simple to add support for this notation to documentation generators like Sphinx.
- Refactoring. Bob's talk has a convincing example of how type annotations help in (manually) refactoring code. I also expect that certain automatic refactorings will benefit from type annotations -- imagine a tool like 2to3 (but used for some other transformation) augmented by type annotations, so it will know whether e.g. x.keys() is referring to the keys of a dictionary or not.
- Optimizers. I believe this is actually the least important application, certainly initially. Optimizers like PyPy or Pyston <https://github.com/dropbox/pyston> wouldn't be able to fully trust the
type annotations, and they are better off using their current strategy of optimizing code based on the types actually observed at run time. But it's certainly feasible to imagine a future optimizer also taking type annotations into account.
-- Terry Jan Reedy
_______________________________________________ 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 If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."

On Aug 14, 2014, at 7:37, Ryan Gonzalez <rymg19@gmail.com> wrote:
On 8/13/2014 3:44 PM, Guido van Rossum wrote:
Now consider an extended version (after Lucas).
def fib(n, a, b): i = 0 while i <= n: print(i,a) i += 1 a, b = b, a+b
The only requirement of a, b is that they be addable. Any numbers should be allowed, as in fib(10, 1, 1+1j), but so should fib(5, '0', '1'). Addable would be approximated from below by Union(Number, str).
Unless MyPy added some sort of type classes...
By "type classes", do you mean this in the Haskell sense, or do you mean classes used just for typing--whether more granular ABCs (like an Addable which both Number and AnyStr and probably inherit) or typing.py type specifiers (like an Addable defined as Union(Number, AnyStr)? It's also worth noting that the idea that this function should take a Number or a str seems way off. It's questionable whether it should accept str, but if it does, shouldn't it also accept bytes, bytearray, and other string-like types? What about sequences? And meanwhile, whether or not it accepts str, it should probably accept np.ndarray and other types of element-wise adding types. If you create an Addable type, it has to define, globally, which of those counts as addable, but different functions will have different definitions that make sense. In fact, look at the other discussion going on. People want to ensure that sum only works on numbers or number-like types (and does that include NumPy arrays or not?), while others want to change it to work on all sequences, or only mutable sequences with += plus str because it effectively has magical += handling under the covers, etc. If we can't even decide what Addable means for one specific function that everyone has experience with... On the other hand, if sum could have been annotated to tell us the author's intention (or, rather, the consensus of the dev list), then all of these recurring arguments about summing str would go away. Until someone defined a number-like class (maybe even one that meets the ABC, but by calling register on it) and sum won't work for him.

Andrew Barnert <abarnert@yahoo.com> wrote:
On Aug 14, 2014, at 7:37, Ryan Gonzalez <rymg19@gmail.com> wrote:
On 8/13/2014 3:44 PM, Guido van Rossum wrote:
Now consider an extended version (after Lucas).
def fib(n, a, b): i = 0 while i <= n: print(i,a) i += 1 a, b = b, a+b
The only requirement of a, b is that they be addable. Any numbers should be allowed, as in fib(10, 1, 1+1j), but so should fib(5, '0', '1'). Addable would be approximated from below by Union(Number, str).
Unless MyPy added some sort of type classes...
By "type classes", do you mean this in the Haskell sense, or do you mean classes used just for typing--whether more granular ABCs (like an Addable which both Number and AnyStr and probably inherit) or typing.py type specifiers (like an Addable defined as Union(Number, AnyStr)?
The Haskell way. Having ABCs and type classes can get confusing, but, when I can, I use type classes for the more unrelated concepts(such as Addable) and ABCs for the more parent-child concepts(such as Node in an AST or Generator in a set of generators). The fine line might actually make it a bad choice to add to Python/mypy, though.
It's also worth noting that the idea that this function should take a Number or a str seems way off. It's questionable whether it should accept str, but if it does, shouldn't it also accept bytes, bytearray, and other string-like types? What about sequences? And meanwhile, whether or not it accepts str, it should probably accept np.ndarray and other types of element-wise adding types. If you create an Addable type, it has to define, globally, which of those counts as addable, but different functions will have different definitions that make sense.
In fact, look at the other discussion going on. People want to ensure that sum only works on numbers or number-like types (and does that include NumPy arrays or not?), while others want to change it to work on all sequences, or only mutable sequences with += plus str because it effectively has magical += handling under the covers, etc. If we can't even decide what Addable means for one specific function that everyone has experience with...
On the other hand, if sum could have been annotated to tell us the author's intention (or, rather, the consensus of the dev list), then all of these recurring arguments about summing str would go away. Until someone defined a number-like class (maybe even one that meets the ABC, but by calling register on it) and sum won't work for him.
-- Sent from my Android phone with K-9 Mail. Please excuse my brevity.

On 8/14/2014 11:21 AM, Andrew Barnert wrote:
On Aug 14, 2014, at 7:37, Ryan Gonzalez <rymg19@gmail.com <mailto:rymg19@gmail.com>> wrote:
On 8/13/2014 3:44 PM, Guido van Rossum wrote:
Now consider an extended version (after Lucas).
def fib(n, a, b): i = 0 while i <= n: print(i,a) i += 1 a, b = b, a+b
The only requirement of a, b is that they be addable. Any numbers should be allowed, as in fib(10, 1, 1+1j), but so should fib(5, '0', '1'). Addable would be approximated from below by Union(Number, str).
Unless MyPy added some sort of type classes...
By "type classes", do you mean this in the Haskell sense, or do you mean classes used just for typing--whether more granular ABCs (like an Addable which both Number and AnyStr and probably inherit) or typing.py type specifiers (like an Addable defined as Union(Number, AnyStr)?
What I like is the idea of protocol based types, as in Andrey Vlasovskikh's slide 26 http://blog.pirx.ru/media/files/2013/python-optional-typing/#26 class Addable(Protocol): @abstractmethod def __add__(self, other): pass Even this does not capture the actual requirement that a and b be addable together, in that order. Addable_together is inherently a pair concept. Syntactically, that could be handled by passing a pair. def fib(n:Comparable_to_int, pair:Addable_together) -> Type resulting from pair: However, the actual Python rules for Addable_together are rather complex. A static type checker for this would be extremely difficult to write. The best dynamic type checker is to try and let Python say no by raising.
It's also worth noting that the idea that this function should take a Number or a str seems way off.
As written, it *does* take any pair that can be repeatedly added together.
It's questionable whether it should accept str,
I recently read Douglas Hofstadler's Fluid Concepts and Creative Analogies: Computer Models of the Fundamental Mechanisms of Thought. In the first chapter he discussed puzzles like: A series begins 0, 1, 01, 101, ..., what is the next item. He started with number sequences and moved on to digit string sequences like the above, where the digit strings are *not* interpreted as number. Generic functions and duck-typing encourages this sort of creative thinking.
but if it does, shouldn't it also accept bytes, bytearray, and other string-like types?
Of course. A descriptive type should not invalidate anything that is allowed.
What about sequences? And meanwhile, whether or not it accepts str, it should probably accept np.ndarray and other types of element-wise adding types.
The function above already does accept such.
If you create an Addable type, it has to define, globally, which of those counts as addable, but different functions will have different definitions that make sense.
A single arg having .__(r)add__ is trivial. The real difficultly is expressing *addable to each other* and relating the result type to the types of the members of the pair. -- Terry Jan Reedy

On Thu, Aug 14, 2014 at 4:27 AM, Terry Reedy <tjreedy@udel.edu> wrote:
My main concern with static typing is that it tends to be anti-duck-typing, while I consider duck-typing to be a major *feature* of Python. The example in the page above is "def fib(n: int):". Fib should get an count (non-negative integer) value, but it need not be an int, and 'half' the ints do not qualify. Reading the tutorial, I could not tell if it supports numbers.Number (which should approximate the domain from above.)
This is a good point. But I think that static typing and duck typing are not mutually exclusive. TypeScript does this very nicely by defining structural interfaces (http://www.typescriptlang.org/Handbook#interfaces). With them is possible to define a given behaviour and any object capable of providing that behaviour is accepted, without having to be part of any specific type hierarchy or having to explicitly register as implementation of certain specification. That's basically what duck typing means. For example: interface Named { name: string; say(): string; } function doSomething(x: Named) { console.log(x.name); } doSomething({name: "hello", say: function() { return this.name }}); // OK doSomething({something: "hello"}); // ERROR I think something like this is a must have for mypy. In Python, I've been playing with something similar (https://github.com/ceronman/typeannotations) but for runtime checking only:
class Person(Interface): ... name = str ... age = int ... def say_hello(name: str) -> str: ... pass
Any object defining those the name, age and say_hello() members is a valid implementation of that interface. For example:
class Developer: ... def __init__(self, name, age): ... self.name = name ... self.age = age ... def say_hello(self, name: str) -> str: ... return 'hello ' + name ... isinstance(Developer('bill', 20), Person) True
Are there any plans for adding something like this to mypy? Manuel.

On Thu, Aug 14, 2014 at 12:04 PM, Manuel Cerón <ceronman@gmail.com> wrote:
For example:
interface Named { name: string; say(): string; }
function doSomething(x: Named) { console.log(x.name); }
doSomething({name: "hello", say: function() { return this.name }}); // OK doSomething({something: "hello"}); // ERROR
That is so Java....! -- Juancarlo *Añez*

On Thu, Aug 14, 2014 at 06:34:43PM +0200, Manuel Cerón wrote:
On Thu, Aug 14, 2014 at 4:27 AM, Terry Reedy <tjreedy@udel.edu> wrote:
My main concern with static typing is that it tends to be anti-duck-typing, while I consider duck-typing to be a major *feature* of Python. The example in the page above is "def fib(n: int):". Fib should get an count (non-negative integer) value, but it need not be an int, and 'half' the ints do not qualify. Reading the tutorial, I could not tell if it supports numbers.Number (which should approximate the domain from above.)
This is a good point. But I think that static typing and duck typing are not mutually exclusive. [...] Are there any plans for adding something like this to mypy?
The mypy FAQs claim to be focusing on nominative typing, but haven't ruled out structural typing in the future: http://www.mypy-lang.org/faq.html -- Steven

Hi, So a couple more comments: - there seems to be no detailed description of mypy's typing.py. There are some docstrings in the module but they don't really explain the intended semantics of using those types (and, most importantly, combining and extending them). Or is the tutorial the description? - mypy is a young module; is it actually proven on real-world codebases? if a PEP gets written, typing.py should be proposed on the "provisional" basis as described in PEP 411 - for the two reasons above I would expect a PEP to spell out and somehow motivate the typing semantics (by "typing" I do not mean the actual algorithm of a type checker, but the semantics of combining types and the mechanisms for extension), rather than defer to third-party docs and source code - many discussion topics seems to revolve around the syntax for declarations; besides syntactic sugar for declarations, though, there should probably be a hook to describe typing in a more programmatic way, especially when parametric types ("generic"?) are involved. I can't think of an example, so I realize this comment may not be very helpful (!), but I'm sure we'll hit the limits of what declarative syntax can give us, especially once people try it out on non-trivial code bases. Do such hooks already exist? - the typing module apparently has its own brand of generic functions (with multiple dispatch?). Are they actually necessary for typing declarations and type inference? If so, it would be nice if this could be unified with functools.singledispatch, or at least put in the functools namespace, and sufficiently similar API-wise that it doesn't stand out. - it would be nice if the PEP could discuss at least a little bit the performance expectations of using the type system, and if there are design decisions that can have a (positive or negative) performance impact on the algorithms using it. I realize this is not top-priority for the linting use case, but it's still important (especially for linting large code bases, which is one of the proclaimed use cases). (this reminds me how annoyed I am at the slowness of Sphinx on non-small reST docs) Regards Antoine.

It seems to me that rather than adding a module which is only used by one project so far to the standard library is a bit premature. I support optional typing, but why push this to stdlib now? Wouldn't it be better to wait until most IDEs/linters all agree on this syntax, until freezing it in stdlib? So far typing seems to be a part of mypy, shouldn't it spend some time on PyPI first? I'm also sure about there not being other uses of annotations -- clize aside, there are not many widely used Python3-only 3rd party libraries, so it's no surprise that nothing big is built around Python 3 features. Maybe the way from PEP 3107's "here's a feature, use it for whatever you like" to "annotations are for typing declarations, using mypy/typing syntax" should include a step of "if you use annotations for typing, use mypy/typing syntax for it". (And perhaps it should end there.)

On Thu Aug 14 2014 at 10:53:37 AM Petr Viktorin <encukou@gmail.com> wrote:
It seems to me that rather than adding a module which is only used by one project so far to the standard library is a bit premature.
I support optional typing, but why push this to stdlib now? Wouldn't it be better to wait until most IDEs/linters all agree on this syntax, until freezing it in stdlib? So far typing seems to be a part of mypy, shouldn't it spend some time on PyPI first?
Because as you have noticed in this thread there are already a ton of competing solutions and no consensus has been reached. Sometimes Guido and/or python-dev have to step in and simply say "there is obvious need and the community is not reaching consensus, so we will make the decision ourselves".
I'm also sure about there not being other uses of annotations -- clize aside, there are not many widely used Python3-only 3rd party libraries, so it's no surprise that nothing big is built around Python 3 features.
Maybe the way from PEP 3107's "here's a feature, use it for whatever you like" to "annotations are for typing declarations, using mypy/typing syntax" should include a step of "if you use annotations for typing, use mypy/typing syntax for it". (And perhaps it should end there.)
That's a possibility. Another thing to support this approach is that if something like List[str] is used over `[str]` then the returned object can subclass some common superclass which can be typechecked for to know that the annotation is from typing.py and not clize/scription and continue to function. That way you can avoid any decorators adding some attribute on functions to know that types have been specified while allowing function annotations to be used for anything. Otherwise a @typing.ignore decorator could also exist for alternative formats to use (since typing has been the most common use case and decorating your single main() function with @typing.ignore is not exactly heavy-handed).

On Thu, Aug 14, 2014 at 9:01 AM, Brett Cannon <brett@python.org> wrote:
On Thu Aug 14 2014 at 10:53:37 AM Petr Viktorin <encukou@gmail.com> wrote:
It seems to me that rather than adding a module which is only used by one project so far to the standard library is a bit premature.
I support optional typing, but why push this to stdlib now? Wouldn't it be better to wait until most IDEs/linters all agree on this syntax, until freezing it in stdlib? So far typing seems to be a part of mypy, shouldn't it spend some time on PyPI first?
Because as you have noticed in this thread there are already a ton of competing solutions and no consensus has been reached. Sometimes Guido and/or python-dev have to step in and simply say "there is obvious need and the community is not reaching consensus, so we will make the decision ourselves".
My overarching concern with the entire proposal is that adding this would just be yet more syntax added to the language with not much use that doesn't go far enough. We'd ultimately need pytd or something else regardless when it comes to full scale Python static analysis. But that *isn't necessarily* a bad thing. Specifying an actual basic annotation syntax that can do some subset of what you want to annotate in the language should in theory still be useful to a real code analysis tool. If it isn't, it will simply ignore it. If it is, it can use it and build on it even though it needs the ability to specify on the side. If you do add a module for this, at least consider hiding it behind a "from __future__ import some_module_full_of_annotation_related_things" instead of making it a new no-op top level module. -gps
I'm also sure about there not being other uses of annotations -- clize aside, there are not many widely used Python3-only 3rd party libraries, so it's no surprise that nothing big is built around Python 3 features.
Maybe the way from PEP 3107's "here's a feature, use it for whatever you like" to "annotations are for typing declarations, using mypy/typing syntax" should include a step of "if you use annotations for typing, use mypy/typing syntax for it". (And perhaps it should end there.)
That's a possibility. Another thing to support this approach is that if something like List[str] is used over `[str]` then the returned object can subclass some common superclass which can be typechecked for to know that the annotation is from typing.py and not clize/scription and continue to function. That way you can avoid any decorators adding some attribute on functions to know that types have been specified while allowing function annotations to be used for anything. Otherwise a @typing.ignore decorator could also exist for alternative formats to use (since typing has been the most common use case and decorating your single main() function with @typing.ignore is not exactly heavy-handed).
_______________________________________________ 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 Thu, Aug 14, 2014 at 10:46:43AM -0700, Gregory P. Smith wrote:
My overarching concern with the entire proposal is that adding this would just be yet more syntax added to the language with not much use that doesn't go far enough.
This isn't new syntax. Functional annotations have been in Python since Python 3.0. What's new here is blessing one specific meaning for that syntax as the One Offical use for annotations. I'd rather: - bless function annotations for static type checking as the default meaning, but allowing code to opt-out and use annotations for something else; - encourage the various type checkers and linters to come up with a standard syntax for type annotations. I'm not convinced that mypy syntax is yet mature enough to be that standard. But, perhaps if the typing module is given provisional status, maybe it could be a good start.
If you do add a module for this, at least consider hiding it behind a "from __future__ import some_module_full_of_annotation_related_things" instead of making it a new no-op top level module.
I'm not sure what this comment means. Did you read Guido's first post in this thread? I thought he was clear that to get type checking, you would do this: from typing import Dict def function(d: Dict[str, int])-> int: ... I bet you can guess what that does, but in case you can't, it declares that argument d is a Dict with str keys and int values, and the return result is an int. I'm not sure where you get the idea of a no-op top level module from. "from __future__ import ..." is inappropriate too, since that is intended for changes to compiler semantics (e.g. new syntax). This is existing syntax, and the actual type checking itself will be delegated to a separate product, mypy, which as Guido stated will behave as a linter. That means that the type checks won't do anything unless you have mypy installed, and you can still run your code under any Python compiler you like. As I understand it, CPython itself requires no changes to make this work, just the addition of typing.py from the mypy project and a policy change to the use of annotations. -- Steven

On Wed, Aug 13, 2014 at 3:14 PM, Guido van Rossum <guido@python.org> wrote:
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter.
To the bottom of things... About the second time I wrote about Python ("Why not Python", 2007) I dismissed it as serious software development environment because the lack of static type checking hindered the creation of proper software development environments. http://blog.neogeny.org/why-not-python.html So, Why do I now have doubts about adding support for static type checking? I've been programming in almost-only Python for several years now, and this discussion had me think hard about "Why?". The answer is simple: I never was as productive as I've been since I've centered on Python. But, again Why? Despite what my '07 article says, the IDE I use is pythonized-VIM and the command line. Where does the productivity come from? 1. Readability with the right amount of succinctness. Python programs are very small, but understandable. 2. The breadth and design consistency of the standard library. Some 70%? of what I need is there, and the design consistency makes it easy (intiutive) to use. 3. PyPi covers another 28%. 4. The Zen of Python (import this) permeates all of the above, including most third-party packages. The ecosystem is consistent too. It's a culture. What do I fear? I think it is that Python be transformed into a programming language different from the one that now makes me so productive. I studied Ruby, and I don't like it. I've been studying Go, and I don't like it. One must like the concepts and the power, sure, but the syntax required for some day-to-day stuff stinks like trouble; simple stuff is so complicated to express and so easy to get wrong... I hate "List[str]" and "Dict[str:int]". Where did those come from? Shouldn't they (as others have proposed) be "[str]" and "{str:int}"? What about tuples? Why not write a similar, but different programming language that targets the Cython runtime and includes all the desired features? All said, this is my proposal. The PSF could support (even fund) MyPy and similar projects, promoting their maturity and their convergence. The changes in 3.5 would be limited but enough to enable those efforts, and those of the several IDE tool-smiths (changes in annotations, and maybe in ABCs). Basically, treat MyPy as PyPy or NumPy (which got '::'). It's in Python's history to enable third-party developments and then adopt what's mature or become the de-facto standard. Then, on a separate line of work, it would be good to think about how to enable different programming languages to target the CPython environment (because of #2, #3, and #4 above), maybe by improving AST creation and AST-to-bytecode? There could be other languages targeting the CPython runtime, which is the relationship that Scala, Jython, IronPython, and others have to their own runtimes. -1 for standardizing static type checking in 3.5 Cheers, -- Juancarlo *Añez*

As requested, I've read the whole post, and the whole thread, before responding :-) On Wed, Aug 13, 2014 at 12:44:21PM -0700, Guido van Rossum wrote:
(a) Python should adopt mypy's syntax for function annotations [...]
I'm very excited to see functional annotations being treated seriously, I think the introduction of static typing, even optional, has the potential to radically change the nature of Python language and I'm not sure if that will be good or bad :-) but it is reassuring to hear that the intention is that it will be treated more like an optional linter than as a core part of the language. On the other hand, are you aware of Cobra, which explicitly was modelled on Python but with optional static typing? http://cobra-language.com/ [...]
*(1) A change of direction for function annotations* [...] I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them.
And in a later email, Guido also stated:
I want to eventually phase out other uses of function annotations
That disappoints me and I hope you will reconsider. I've spent some time thinking about using annotations for purposes other than type checking, but because most of my code has to run on Python 2, there's nothing concrete. One example is that I started exploring ways to use annotations as documentation for the statistics module in 3.4, except that annotations are banned from the standard library. (Naturally I haven't spent a lot of time on something that I knew was going to be rejected.) I came up with ideas like this: def mean(data) -> 'μ = ∑(x)/n': def pvariance(data) -> 'σ² = ∑(x - μ)² ÷ n': which might have been a solution to this request: http://bugs.python.org/issue21046 had annotations been allowed in the stdlib. Regardless of whether this specific idea is a good one or not, I will be disappointed if annotations are limited to one and only one use. I don't mind if there is a standard, default, set of semantics so long as there is a way to opt-out and use something else: @use_spam_annotations def frobnicate(x: spam, y: eggs)->breakfast: ... for example. Whatever the mechanism, I think Python should not prohibit or deprecate other annotation semantics.
*(2) A specification for what to add to Python 3.5*
There needs to be at least a rough consensus on the syntax for annotations, and the syntax must cover a large enough set of use cases to be useful. Mypy is still under development, and some of its features are still evolving (e.g. unions were only added a few weeks ago). It would be possible to argue endlessly about details of the notation, e.g. whether to use 'list' or 'List', what either of those means (is a duck-typed list-like type acceptable?) or how to declare and use type variables, and what to do with functions that have no annotations at all (mypy currently skips those completely).
It doesn't sound to me like the mypy syntax is mature enough to bless, let alone to start using it in the standard library.
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter. The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module.
What happens when the typing.py module in the standard library gets out of sync with the typing.py module in mypy? [...]
*Appendix -- Why Add Type Annotations?* The argument between proponents of static typing and dynamic typing has been going on for many decades. Neither side is all wrong or all right. Python has traditionally fallen in the camp of extremely dynamic typing, and this has worked well for most users, but there are definitely some areas where adding type annotations would help.
Some people have probably already seen this, but I have found this article to be very useful for understanding why static and dynamic type checking can be complementary rather than opposed: http://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/ -- Steven

On 14 August 2014 18:31, Steven D'Aprano <steve@pearwood.info> wrote:
opt-out and use something else:
@use_spam_annotations def frobnicate(x: spam, y: eggs)->breakfast: ... Which reminds me. Why is mypy exempt from the general recommendation to only use annotations on functions that are tagged with a decorator?
Paul.

On Wed, Aug 20, 2014 at 03:06:09PM +0100, Paul Moore wrote:
On 14 August 2014 18:31, Steven D'Aprano <steve@pearwood.info> wrote:
opt-out and use something else:
@use_spam_annotations def frobnicate(x: spam, y: eggs)->breakfast: ... Which reminds me. Why is mypy exempt from the general recommendation to only use annotations on functions that are tagged with a decorator?
That's the whole point of Guido's proposal: he wants to bless, not mypy precisely since that's an alternative implementation of Python, but mypy's syntax as the standard use of annotations. Since it will be the official standard, there's no need to flag it with a decorator, even if CPython will not itself do anything with those annotations. If Guido's suggestion is accepted, any linter, editor, IDE, Python compiler, or other static tool will be entitled to assume that annotations are type hints, using the mypy-derived syntax, at least unless explicitly told not to. -- Steven

Does mypy support annotation of functions implemented in C code? If I extend cpython via C-API, does mypy provide a mechanism to annotate those functions?

Neal Becker schrieb am 14.08.2014 um 19:33:
Does mypy support annotation of functions implemented in C code?
If I extend cpython via C-API, does mypy provide a mechanism to annotate those functions?
No, mypy isn't about C or even CPython. However, you can already do that, although not easily, I think. What you'd need is an __annotations__ dict on the function object and a bit of trickery to make CPython believe it's a function. Cython gives you that for free (by simply providing the normal Python semantics), but you can get the same thing with some additional work when writing your own C code. There's also the argument clinic, but IIRC it doesn't support signature annotations for some reason, guess it wasn't considered relevant (yet). Stefan

Guido van Rossum schrieb am 13.08.2014 um 21:44:
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online ( https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations [...] proposal (a) feels right to me.
FWIW, Cython currently understands these forms of function argument annotations in Python code: x: dict x: {"type": dict} x: {"type": "dict"} x: {"ctype": "long double"} The "ctype" values that are usable here obviously only include those that can be converted from and to Python types, e.g. no arbitrary pointers. It'd be nice to have a way to declare container item types, but that's never really been a priority in the Cython project so far. Declaring protocols, OTOH, is pretty useless for a compiler, as it's obvious from the code which protocols are being used on a given value (iteration, item access, etc.). It's clear that restricting everything to one kind of annotation isn't enough, as there are use cases for a mix of different type systems, Python itself plus at least C/C++ in CPython and Cython, Java in Jython, C# in IronPython, plus others that people might want to interface with. C/C++ are generally interesting, for example, also for .NET or JVM users. Although I wasn't very impressed by Bob Ippolito's talk at EuroPython, I'm generally not opposed to type annotations to provide 1) additional contract information for libraries, 2) documentation, 3) better static analysis or 4) type hints for compilers. But these are actually quite different use cases that may each suggest a different strictness in the type declarations. For 3) and 4), function signatures aren't enough and should be accompanied by declarations for local variables. 4) should also support additional type systems for language integration. But 1) and 2) aren't completely overlapping either. 1) would need declarations that can be used for hard type checking, whereas 2) can be much more relaxed, generic and incomplete. Trying to get all of these under one umbrella might not be a good idea, but letting people add three different annotations for each function argument definitely isn't either. Stefan

Chiming in as well (first post to python-ideas, fingers crossed!), because Spyne (I'm the author) will have to interoperate with this sooner or later :) To put my opinions in context: http://spyne.io/docs/2.10/manual/03_types.html On 08/13/14 22:44, Guido van Rossum wrote:
[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.]
Not only that, but I went through most of the thread as well :)
To read up on mypy's annotation syntax, please see the mypy-lang.org <http://mypy-lang.org> website. Here's just one complete example, to give a flavor:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
I couldn't agree more with Dennis Brakhane saying:
"I would be very sad to see annotations being limited to convey type information."
It'd be very tedious for us corporate web-service guys having to specify things twice to get the advantages of Python's static typing -- whatever they might end up being. But it's not just that -- str[120] is a kind of string (of length 120) the same way list[str] is a kind of list (that only contains strings). But this gets out of hand fast: Why not say str[6,32,'[A-Fa-f0-9]+'] is a hex string between lengths 6 and 32? So my proposition is to have __getitem__ accept **kwargs. That way the above becomes: HexString = str[min_length=6, max_length=32, pattern='[A-Fa-f0-9]+'] This would elegantly blend in with duck typing as well. object[write=callable] is a file-like object. Or maybe I should say object[write=callable[bytes]] ? Alternatively, having what I call "specializers" in typing.py could work just as well: HexString = str[min_length(6), max_length(32), pattern('[A-Fa-f0-9]+')] IntToStrMapping = dict[key(int), value(str)] But it looks hideous and as a bonus, invalid specializations like dict[pattern('[a-z0-9]+')] will have to be dealt with. Or, it's also possible to do what we're doing with Spyne and leave builtins like str, list etc. alone and introduce a separate set of markers to be used exclusively for type annotation purposes: HexString = Unicode(min_length=6, max_length=32, pattern='[A-Fa-f0-9]+') IntToStrMapping = Dict(key=int, value=str) This has the disadvantage of doing generics with parens, which turned out to be pretty confusing for some new users of Spyne. While we're into the run-time checked types territory, why make do with declarative restrictions and not pass arbitrary callables to validate incoming objects? e.g. PngData = bytes[validator=ensure_png] where ensure_png is a callable[bytes] that ensures that the incoming bytes object is indeed a valid png data. Now you might think this proposal an abomination in the context of a community of "consenting adults". But when you let your functions be called by untrusted sources (which is, I'll argue, pretty much anyone whose code listens to a socket one way or the other) precisely expressing and applying such constraints is very very important. IMO, how this kind of information is passed around needs to be standardized for the same reasons Python got a standard event loop. We already have a high amount of largely incompatible systems of varying levels of sophistication and purposes for enforcing constraints to incoming data. I hope all this makes sense. Best regards, Burak

This proposal effectively negates any benefit to adding annotations to Python. Annotations' only function was to provide unspecified information about parameters and return values to other Python at runtime. That goes with the acceptance of this proposal: It's still their sole function, but now we're *not allowed to use them* to do that any more. Annotations alone are syntax that corresponds to exactly no semantic meaning, and far from encouraging developers to assign meaning to them, you're banning them from doing so. In its current form, Python code can't even make use of the annotations that are allowed: mypy's type system is not Python's, and without a specification of how the static type checker's type system actually works, anything looking at the annotations from the Python side would have to come up with its own way to interpret them. You could have picked *any* other way to communicate static typing information, and it wouldn't neuter a feature so completely: - use a @typechecked decorator to signify that a decorated function's annotations are for use as type information - use a certain set of annotations to denote type information, without deprecating the use of annotations for other purposes - parse a standardized docstring format for type information - add new syntax for expressing type information without conflicting with annotations: def foo(int bar), for example Static typing is what it is and I won't try to convince you that it's not worth adding to the language, but it's not worth rendering a feature useless when you could *not* render that feature useless and retain all the same benefits. Ed Kellett On 13 August 2014 20:44, Guido van Rossum <guido@python.org> wrote:
[There is no TL;DR other than the subject line. Please read the whole thing before replying. I do have an appendix with some motivations for adding type annotations at the end.]
Yesterday afternoon I had an inspiring conversation with Bob Ippolito (man of many trades, author of simplejson) and Jukka Lehtosalo (author of mypy: http://mypy-lang.org/). Bob gave a talk at EuroPython about what Python can learn from Haskell (and other languages); yesterday he gave the same talk at Dropbox. The talk is online (https://ep2014.europython.eu/en/schedule/sessions/121/) and in broad strokes comes down to three suggestions:
(a) Python should adopt mypy's syntax for function annotations (b) Python's use of mutabe containers by default is wrong (c) Python should adopt some kind of Abstract Data Types
Proposals (b) and (c) don't feel particularly actionable (if you disagree please start a new thread, I'd be happy to discuss these further if there's interest) but proposal (a) feels right to me.
So what is mypy? It is a static type checker for Python written by Jukka for his Ph.D. thesis. The basic idea is that you add type annotations to your program using some custom syntax, and when running your program using the mypy interpreter, type errors will be found during compilation (i.e., before the program starts running).
The clever thing here is that the custom syntax is actually valid Python 3, using (mostly) function annotations: your annotated program will still run with the regular Python 3 interpreter. In the latter case there will be no type checking, and no runtime overhead, except to evaluate the function annotations (which are evaluated at function definition time but don't have any effect when the function is called).
In fact, it is probably more useful to think of mypy as a heavy-duty linter than as a compiler or interpreter; leave the type checking to mypy, and the execution to Python. It is easy to integrate mypy into a continuous integration setup, for example.
To read up on mypy's annotation syntax, please see the mypy-lang.org website. Here's just one complete example, to give a flavor:
from typing import List, Dict
def word_count(input: List[str]) -> Dict[str, int]: result = {} #type: Dict[str, int] for line in input: for word in line.split(): result[word] = result.get(word, 0) + 1 return result
Note that the #type: comment is part of the mypy syntax; mypy uses comments to declare types in situations where no syntax is available -- although this particular line could also be written as follows:
result = Dict[str, int]()
Either way the entire function is syntactically valid Python 3, and a suitable implementation of typing.py (containing class definitions for List and Dict, for example) can be written to make the program run correctly. One is provided as part of the mypy project.
I should add that many of mypy's syntactic choices aren't actually new. The basis of many of its ideas go back at least a decade: I blogged about this topic in 2004 (http://www.artima.com/weblogs/viewpost.jsp?thread=85551 -- see also the two followup posts linked from the top there).
I'll emphasize once more that mypy's type checking happens in a separate pass: no type checking happens at run time (other than what the interpreter already does, like raising TypeError on expressions like 1+"1").
There's a lot to this proposal, but I think it's possible to get a PEP written, accepted and implemented in time for Python 3.5, if people are supportive. I'll go briefly over some of the action items.
(1) A change of direction for function annotations
PEP 3107, which introduced function annotations, is intentional non-committal about how function annotations should be used. It lists a number of use cases, including but not limited to type checking. It also mentions some rejected proposals that would have standardized either a syntax for indicating types and/or a way for multiple frameworks to attach different annotations to the same function. AFAIK in practice there is little use of function annotations in mainstream code, and I propose a conscious change of course here by stating that annotations should be used to indicate types and to propose a standard notation for them.
(We may have to have some backwards compatibility provision to avoid breaking code that currently uses annotations for some other purpose. Fortunately the only issue, at least initially, will be that when running mypy to type check such code it will produce complaints about the annotations; it will not affect how such code is executed by the Python interpreter. Nevertheless, it would be good to deprecate such alternative uses of annotations.)
(2) A specification for what to add to Python 3.5
There needs to be at least a rough consensus on the syntax for annotations, and the syntax must cover a large enough set of use cases to be useful. Mypy is still under development, and some of its features are still evolving (e.g. unions were only added a few weeks ago). It would be possible to argue endlessly about details of the notation, e.g. whether to use 'list' or 'List', what either of those means (is a duck-typed list-like type acceptable?) or how to declare and use type variables, and what to do with functions that have no annotations at all (mypy currently skips those completely).
I am proposing that we adopt whatever mypy uses here, keeping discussion of the details (mostly) out of the PEP. The goal is to make it possible to add type checking annotations to 3rd party modules (and even to the stdlib) while allowing unaltered execution of the program by the (unmodified) Python 3.5 interpreter. The actual type checker will not be integrated with the Python interpreter, and it will not be checked into the CPython repository. The only thing that needs to be added to the stdlib is a copy of mypy's typing.py module. This module defines several dozen new classes (and a few decorators and other helpers) that can be used in expressing argument types. If you want to type-check your code you have to download and install mypy and run it separately.
The curious thing here is that while standardizing a syntax for type annotations, we technically still won't be adopting standard rules for type checking. This is intentional. First of all, fully specifying all the type checking rules would make for a really long and boring PEP (a much better specification would probably be the mypy source code). Second, I think it's fine if the type checking algorithm evolves over time, or if variations emerge. The worst that can happen is that you consider your code correct but mypy disagrees; your code will still run.
That said, I don't want to completely leave out any specification. I want the contents of the typing.py module to be specified in the PEP, so that it can be used with confidence. But whether mypy will complain about your particular form of duck typing doesn't have to be specified by the PEP. Perhaps as mypy evolves it will take options to tell it how to handle certain edge cases. Forks of mypy (or entirely different implementations of type checking based on the same annotation syntax) are also a possibility. Maybe in the distant future a version of Python will take a different stance, once we have more experience with how this works out in practice, but for Python 3.5 I want to restrict the scope of the upheaval.
Appendix -- Why Add Type Annotations?
The argument between proponents of static typing and dynamic typing has been going on for many decades. Neither side is all wrong or all right. Python has traditionally fallen in the camp of extremely dynamic typing, and this has worked well for most users, but there are definitely some areas where adding type annotations would help.
- Editors (IDEs) can benefit from type annotations; they can call out obvious mistakes (like misspelled method names or inapplicable operations) and suggest possible method names. Anyone who has used IntelliJ or Xcode will recognize how powerful these features are, and type annotations will make such features more useful when editing Python source code.
- Linters are an important tool for teams developing software. A linter doesn't replace a unittest, but can find certain types of errors better or quicker. The kind of type checking offered by mypy works much like a linter, and has similar benefits; but it can find problems that are beyond the capabilities of most linters.
- Type annotations are useful for the human reader as well! Take the above word_count() example. How long would it have taken you to figure out the types of the argument and return value without annotations? Currently most people put the types in their docstrings; developing a standard notation for type annotations will reduce the amount of documentation that needs to be written, and running the type checker might find bugs in the documentation, too. Once a standard type annotation syntax is introduced, it should be simple to add support for this notation to documentation generators like Sphinx.
- Refactoring. Bob's talk has a convincing example of how type annotations help in (manually) refactoring code. I also expect that certain automatic refactorings will benefit from type annotations -- imagine a tool like 2to3 (but used for some other transformation) augmented by type annotations, so it will know whether e.g. x.keys() is referring to the keys of a dictionary or not.
- Optimizers. I believe this is actually the least important application, certainly initially. Optimizers like PyPy or Pyston wouldn't be able to fully trust the type annotations, and they are better off using their current strategy of optimizing code based on the types actually observed at run time. But it's certainly feasible to imagine a future optimizer also taking type annotations into account.
-- --Guido "I need a new hobby" 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/

There's been a lot to read in this and related threads over the past nine days. (According to Gmail. It qualitatively seems much longer to me.) I think I've followed along reasonably well. Forgive me if I missed the answers to these questions. The proposal on the table is to adopt MyPy's type annotations for Python 3.mumble. I presume MyPy already supports them. 1. Can MyPy be used today as a standalone static type checker for Python 3.x code without actually compiling anything? That is, can I just sprinkle type annotations into my code and run a front-end pass of MyPy much the same way I'd run pylint, vulture, flake8, or other lint-ish program? 2. Assuming the answer to #1 is "yes," if you start sprinkling type annotations into your code and running "mypy *.py", will it tell you when it needs a missing type annotation to more fully check things, or will it silently process code without annotations and not let you know that it's not really checking much? 3. Will we get into a phase like the early days of "const" in ANSI C where addition of "const" in one location in your existing code base forced you into a never-ending iterations of adding const all over the place? I forget what that was called ("const propagation"?), but I recall that it generally wasn't a fun activity. 4. If the answer to #1 is "no," are there plans to produce a front-end-only MyPy which can be used as a linter for Python code? Thx, Skip

On Friday, August 22, 2014, Skip Montanaro <skip@pobox.com> wrote:
There's been a lot to read in this and related threads over the past nine days. (According to Gmail. It qualitatively seems much longer to me.) I think I've followed along reasonably well. Forgive me if I missed the answers to these questions. The proposal on the table is to adopt MyPy's type annotations for Python 3.mumble. I presume MyPy already supports them.
1. Can MyPy be used today as a standalone static type checker for Python 3.x code without actually compiling anything? That is, can I just sprinkle type annotations into my code and run a front-end pass of MyPy much the same way I'd run pylint, vulture, flake8, or other lint-ish program?
Yes. mypy -S performs checking without execution. 2. Assuming the answer to #1 is "yes," if you start sprinkling type
annotations into your code and running "mypy *.py", will it tell you when it needs a missing type annotation to more fully check things, or will it silently process code without annotations and not let you know that it's not really checking much?
No. It happily and silently accepts dynamic code with no annotations. It can't do much for you with that code, but it doesn't complain. 3. Will we get into a phase like the early days of "const" in ANSI C where
addition of "const" in one location in your existing code base forced you into a never-ending iterations of adding const all over the place? I forget what that was called ("const propagation"?), but I recall that it generally wasn't a fun activity.
Nope.

On Fri, Aug 22, 2014 at 7:30 AM, Bob Ippolito <bob@redivi.com> wrote:
On Friday, August 22, 2014, Skip Montanaro <skip@pobox.com> wrote:
There's been a lot to read in this and related threads over the past nine days. (According to Gmail. It qualitatively seems much longer to me.) I think I've followed along reasonably well. Forgive me if I missed the answers to these questions. The proposal on the table is to adopt MyPy's type annotations for Python 3.mumble. I presume MyPy already supports them.
1. Can MyPy be used today as a standalone static type checker for Python 3.x code without actually compiling anything? That is, can I just sprinkle type annotations into my code and run a front-end pass of MyPy much the same way I'd run pylint, vulture, flake8, or other lint-ish program?
Yes. mypy -S performs checking without execution.
2. Assuming the answer to #1 is "yes," if you start sprinkling type
annotations into your code and running "mypy *.py", will it tell you when it needs a missing type annotation to more fully check things, or will it silently process code without annotations and not let you know that it's not really checking much?
No. It happily and silently accepts dynamic code with no annotations. It can't do much for you with that code, but it doesn't complain.
However, mypy has a flag --html-report which generates a marked-up source code listing that shows which regions of the code have or haven't been checked, using similar color-coding as coverage.py.
3. Will we get into a phase like the early days of "const" in ANSI C where
addition of "const" in one location in your existing code base forced you into a never-ending iterations of adding const all over the place? I forget what that was called ("const propagation"?), but I recall that it generally wasn't a fun activity.
Nope.
The Python interpreter won't force you. Of course you can still play this game (e.g. by insisting that no red zones should exist in the above-mentioned source listing) but that's no different from insisting on zero lint warnings -- it is a choice a developer (usually a team) makes, not something the software enforces. -- --Guido van Rossum (python.org/~guido)
participants (51)
-
Alex Gaynor
-
Alex Hammel
-
Andrew Barnert
-
Andrey Vlasovskikh
-
Antoine Pitrou
-
Barry Warsaw
-
Ben Finney
-
Bob Ippolito
-
Brett Cannon
-
Burak Arslan
-
Carlo Pires
-
Cem Karan
-
Chris Angelico
-
Christian Heimes
-
David Mertz
-
Dennis Brakhane
-
Devin Jeanpierre
-
Donald Stufft
-
Ed Kellett
-
Ethan Furman
-
Georg Brandl
-
Greg Ewing
-
Gregory P. Smith
-
Gregory Salvan
-
Guido van Rossum
-
Haoyi Li
-
Jim Baker
-
Joseph Martinot-Lagarde
-
Juancarlo Añez
-
Jukka Lehtosalo
-
M.-A. Lemburg
-
Manuel Cerón
-
Neal Becker
-
Nick Coghlan
-
Paul Moore
-
Petr Viktorin
-
Rock Neurotiko
-
Ryan
-
Ryan Gonzalez
-
Ryan Hiebert
-
Skip Montanaro
-
Stefan Behnel
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Sunjay Varma
-
Terry Reedy
-
Todd
-
Tony Lownds
-
Wes Turner
-
Yann Kaiser
-
Łukasz Langa