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)