[Python-3000] Draft pre-PEP: function annotations

Phillip J. Eby pje at telecommunity.com
Sat Aug 12 23:10:17 CEST 2006


At 12:38 PM 8/12/2006 -0700, Paul Prescod wrote:
>Phillip. I'm having trouble following the discussion. I briefly caught up 
>when Talin got very concrete with syntax and I would appreciate if you 
>could offer some correspondingly remedial training.
>
>Talin's example is that metadata inventor A documents that his/her users 
>should use this syntax for parameter docstrings:
>
>def myfunc( x : "The x coordinate", y : "The y coordinate" )
>       ...
>
>Then metadata inventor B documents this his/her users should use this 
>syntax for getopt strings:
>
>class MyHandler( CommandLineHandler ):
>
>    @getopt
>    def list( infile:"i" = sys.stdin, outfile:"o" = sys.stdout ):
>
>Now the user is faced with the challenge of making these two work together 
>in order to get the best of both worlds. What does the user type?

As long as both inventors used overloadable functions, the user can type 
almost *anything they want to*, as long as:

1. It's consistent,
2. It's unambiguous, and
3. They've defined the appropriate overloads.

For example, they might use a 'docopt' class that allows both to be 
specified, or a pair of 'doc' and 'opt' objects in a list.


>The mechanism of overloading, function dispatching etc. is uninteresting 
>to me until I understand what goes in the user's Python file. Syntax is 
>important.

Indeed it is.  Hence the importance of not forcing some particular 
semantics, so as to allow the user to use the types and semantics of their 
choosing.

By the way, it should be understood that when I say "overloadable 
function", I simply mean some type-extensible dispatching mechanism.  If 
you exclude built-in types from consideration, and simply have special 
attribute or method names, then duck typing works just as well.  You can 
have decorators that use hasattr() and such to do their dirty work.

It's only if you want to have sensible meaning for built-in types that 
there even begins to be an illusion that conflicts are an issue.  However, 
the only built-in types likely to even be used in such a way are lists, 
dictionaries, tuples, and strings.  If there's more than one way to 
interpret them, depending on the operation, their use is inherently 
ambiguous, and it's up to the person combining them to supply the 
differentiation.

However, if you have:

    def myfunc( x : doc("The x coordinate"), y : doc("The y coordinate") )

There is no ambiguity.  Likewise:

    def cat( infile:opt("i") = sys.stdin, outfile:opt("o") = sys.stdout ):

is unambiguous.  And the interpetation of:

    def cat(infile: [doc("input stream"), opt("i")] = sys.stdin,
            outfile: [doc("output stream"), opt("o")] = sys.stdout
    ):

is likewise unambiguous, unless the creator of the documentation or option 
features has defined some other interpretation for a list than "recursively 
apply to contained items".  In which case, you need only do something like:

    def cat(infile: docopt("input stream", "i") = sys.stdin,
            outfile: docopt("output stream", "o") = sys.stdout
    ):

with an appropriate definition of methods for the 'docopt' type.

Since many people seem to be unfamiliar with overloaded functions, I would 
just like to take this opportunity to remind you that the actual overload 
mechanism is irrelevant.  If you gave 'doc' objects a 'printDocString()' 
method and 'opt' objects a 'setOptionName()' method, the exact same logic 
regarding extensibility applies.  The 'docopt' type would simply implement 
both methods.

This is normal, simple standard Python stuff; nothing at all fancy.  The 
only thing that overloaded functions add to this is that they allow you to 
(in effect) add methods to existing types without monkeypatching.  Thus, 
you can define overloads for built-in types, and types you didn't implement 
yourself.  Even if overloaded functions didn't exist, it wouldn't be 
necessary to invent them just to allow arbitrary annotation semantics!  It 
simply requires that operations that *use* annotations always follow the 
"tell, don't ask" pattern, whether it's done by duck typing, EAFP, or 
overloaded functions.



More information about the Python-3000 mailing list