On Thu, Apr 30, 2015 at 01:41:53PM +0200, Dima Tisnek wrote:
# Syntactic sugar "Beautiful is better than ugly, " thus nice syntax is needed. Current syntax is very mechanical. Syntactic sugar is needed on top of current PEP.
I think the annotation syntax is beautiful. It reminds me of Pascal.
# internal vs external @intify def foo() -> int: b = "42" return b # check 1 x = foo() // 2 # check 2
Does the return type apply to implementation (str) or decorated callable (int)?
I would expect that a static type checker would look at foo, and flag this as an error. The annotation says that foo returns an int, but it clearly returns a string. That's an obvious error.
Here is how I would write that:
# Perhaps typing should have a Function type? def intify(func: Callable[, str]) -> Callable[, int]: @functools.wraps(func) def inner() -> int: return int(func()) return inner
@intify def foo() -> str: b = "42" return b
That should, I hope, pass the type check, and without lying about the signature of *undecorated* foo.
The one problem with this is that naive readers will assume that *decorated* foo also has a return type of str, and be confused. That's a problem. One solution might be, "don't write decorators that change the return type", but that seems horribly restrictive. Another solution might be to write a comment:
@intify # changes return type to int def foo() -> str: ...
but that's duplicating information already in the intify decorator, and it relies on the programmer writing a comment, which people don't do unless they really need to.
I think that the only solution is education: given a decorator, you cannot assume that the annotations still apply unless you know what the decorator does.
How can same annotation or a pair of annotations be used to:
- validate return statement type
- validate subsequent use
- look reasonable in the source code
# lambda Not mentioned in the PEP, omitted for convenience or is there a rationale? f = lambda x: None if x is None else str(x ** 2) Current syntax seems to preclude annotation of `x` due to colon. Current syntax sort of allows lamba return type annotation, but it's easy to confuse with `f`.
I don't believe that you can annotate lambda functions with current syntax. For many purposes, I do not think that is important: a good type checker will often be able to infer the return type of the lambda, and from that infer what argument types are permitted:
lambda arg: arg + 1
Obviously arg must be a Number, since it has to support addition with ints.
# local variables Not mentioned in the PEP Non-trivial code could really use these.
Normally local variables will have their type inferred from the operations done to them:
s = arg[1:] # s has the same type as arg
When that is not satisfactory, you can annotate variables with a comment:
s = arg[1:] #type: List[int]
# global variables Not mentioned in the PEP Module-level globals are part of API, annotation is welcome. What is the syntax?
# comprehensions [3 * x.data for x in foo if "bar" in x.type] Arguable, perhaps annotation is only needed on `foo` here, but then how complex comprehensions, e.g. below, the intermediate comprehension could use an annotation [xx for y in [...] if ...]
A list comprehension is obviously of type List. If you need to give a more specific hint:
result = [expr for x in things if cond(x)] #type: List[Whatever]
See also the discussion of "cast" in the PEP.
# class attributes s = socket.socket(...) s.type, s.family, s.proto # int s.fileno # callable If annotations are only available for methods, it will lead to Java-style explicit getters and setters. Python language and data model prefers properties instead, thus annotations are needed on attributes.
class Thing: a = 42 # can be inferred b =  # inferred as List[Any] c =  #type: List[float]