[Python-ideas] Conventions for function annotations
Guido van Rossum
guido at python.org
Thu Dec 6 06:54:25 CET 2012
Hi Nick,
I understand your position completely (and I did before). I just disagree. :-)
I think that requiring the experiment I am proposing to use a
decorator on each function that uses it (rather than just an import at
the top of the module) will cause too much friction, and the
experiment won't get off the ground. That's why I am proposing a
universal composition convention:
When an annotation for a particular argument is a tuple, then any
framework or decorator that tries to assign meanings to annotations
must search the items of the tuple for one that it can understand. For
the experimental type annotation system I am proposing this should be
simple enough -- the type annotation system can require that the
things it cares about must all be subclasses of a specific base class
(let's call it TypeConstraint). If the annotation is not a tuple, it
should be interpreted as a singleton tuple.
Yes, it is possible that a mistake leaves an annotation unclaimed. But
that's no worse than currently, where all annotations are ignored. And
for TypeConstraint there is no runtime behavior anyway (unless you
*do* add a decorator) -- its annotations are there for other tools to
parse and interpret. It's like pylint directives -- if you
accidentally misspell it 'pylnt' you get no error (but you may still
notice that something's fishy, because when pylint runs it doesn't
suppress the thing you tried to suppress :-).
--Guido
On Wed, Dec 5, 2012 at 9:27 PM, Nick Coghlan <ncoghlan at gmail.com> wrote:
> On Thu, Dec 6, 2012 at 5:22 AM, Guido van Rossum <guido at python.org> wrote:
>>
>> - Composability (Nick's pet peeve, in that he is against it). I
>> propose that we reserve plain tuples for this. If an annotation has
>> the form "x: (P, Q)" then that ought to mean that x must conform to
>> both P and Q. Even though Nick doesn't like this, I don't think we
>> should do everything with decorators. Surly, the decorators approach
>> is good for certain use cases, and should take precedence if it is
>> used. But e.g. IDEs that use annotations for suggestions and
>> refactoring should not require everything to be decorated -- that
>> would just make the code too busy.
>
>
> I'm not against using composition within a particular set of annotation
> semantics, I'm against developing a convention for arbitrary composition of
> annotations with *different* semantics.
>
> Instead, I'm advocating for the following guidelines to avoid treading on
> each others toes when experimenting with annotations and to leave scope for
> us to define standard annotation semantics at a future date:
>
> 1. Always use a decorator that expresses the annotation semantics in use
> (e.g. tab completion, type descriptions, parameter documentation)
> 2. Always *move* the annotations out to purpose-specific storage as part of
> the decorator (don't leave them in the annotations storage)
> 3. When analysing a function later, use only the purpose-specific
> attribute(s), not the raw annotations storage
> 4. To support composition with other sets of annotation semantics, always
> provide an alternate API that accepts the per-parameter details directly
> (e.g. by name or index) rather than relying solely on the annotations
>
> The reason for this is so that if, at some future point in the time,
> python-dev agrees to bless some particular set of semantics as *the* meaning
> of function annotations (such as the type hinting system being discussed),
> then that won't break anything. Otherwise, if people believe that it's OK
> for them to simply assume that the contents of the annotations mean whatever
> they mean for their particular project, then it *will* cause problems
> further down the road as annotations written for one set of semantics (e.g.
> tab completion, parameter documentation) get interpreted by a processor
> expecting different semantics (e.g. type hinting).
>
> Here's how your example experiment would look under such a scheme:
>
> from experimental_type_annotations import type_hints, Int, Str, Float
>
> # After type_hints runs, foo.__annotations__ would be empty, and the
> type
> # hinting data would instead be stored in (e.g.) a foo._type_hints
> attribute.
> @type_hints
>
> def foo(a: Int, b: Str) -> Float:
> <blah>
>
> This is then completely clear and unambigious:
> - readers can see clearly that these annotations are intended as type hints
> - the type hinting processor can see that there *is* type hinting
> information available, due to the presence of a _type_hints attribute
> - other automated processors see that there are no "default" annotations
> (which is good, since there is currently no such thing as "default"
> annotation semantics)
>
> Furthermore, (as noted elsewhere in the thread) an alternate API can then
> easily be provided that supports composition with other annotations:
>
> @type_hints(Int, Str, _return=Float)
> def foo(a, b):
> <blah>
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
--
--Guido van Rossum (python.org/~guido)
More information about the Python-ideas
mailing list