In practice, I’m not as concerned with the potential pitfall of confusion between a type annotation and an argument name.
I think it may look similar when we’re using lowercase variable placeholders in hypothetical examples, but in practice if someone wrote `(a, b) -> bool` where `a` and `b` are parameter names, they’ll get an UndefinedType error complaining that `a` and `b` aren’t valid types. Even before that, I think it would be somewhat unintuitive for someone decide to specify a callable type, then want to put parameters in without types, and go fetch names for those parameters instead of just using “…”. Or if there’s a mix of typed/Any params, that would mean they’re actively alternating between types and param names.
If we want to support either all-or-nothing named params that seems reasonable to me, ie., what Guido mentioned about either fully shorthand or fully spelled out.
* In this form a "bare" name represents an argument name and has the implicit type Any, like it has in function definitions
What are your thoughts on erroring on this and mandating an explicit Any if you’re using the full form syntax – so all parameters must both have a name and a type if you aren’t using the shorthand version?
From: Jelle Zijlstra
Date: Wednesday, June 16, 2021 at 2:58 PM
To: Guido van Rossum
Cc: Shannon Zhu , Steven Troxler , typing-sig@python.org
Subject: Re: [Typing-sig] Re: Compact syntax for Callable: `(Arg1, Arg2) -> Ret`
El mié, 16 jun 2021 a las 11:42, Guido van Rossum (mailto:guido@python.org>) escribió:
On Wed, Jun 16, 2021 at 11:29 AM Jelle Zijlstra mailto:jelle.zijlstra@gmail.com> wrote:
I would favor a shorthand where a bare name means a typed positional-only argument. That means we both get a nice shorthand (e.g., `(int, str) -> int` corresponds to `Callable[[int, str], int]`) and full power to represent all signatures (you can write `(a: int, /, b: str, *, c: int = ...) -> int` if you really need it).
El mié, 16 jun 2021 a las 11:06, Guido van Rossum (mailto:guido@python.org>) escribió:
[...]
Additionally, what are the concerns with allowing a hybrid of named and unnamed params, ie.:
f: (int, str, *, foo: int = ..., **kwargs: str) -> bool
If we were to allow this, it'd be harder to give a simple rule for when the argument names can be omitted or not -- for example would we allow this?
f: (int, name: str) -> bool
This would be shorthand for a function that takes a positional-only argument of type int and a positional-or-keyword argument named "name" of type str.
But if you spell that out, you'd have to add a /, i.e.
def f(unused: int, /, name: str) -> bool: ...
I would prefer to use a simpler rule:
- Either there are *no* colons, stars, slashes or equal signs, and then it's a bunch of positional-only args given by type; example: (int, str) -> bool.
- Or the argument list follows the rules of 'def'; example: (a: int, b: str) -> bool. In this form a "bare" name represents an argument name and has the implicit type Any, like it has in function definitions. (Or Undefined, in checkers that make a distinction, like pyright.)
I would be OK with that too; I don't think mixing the two forms is an important use case.
or this?
f: (count: int, str) -> bool
or even this?
f: (a: int, b, c: str) -> bool
These two would be illegal because a positional-only argument cannot follow a positional-or-keyword argument.
Hm, so with my "simpler" rule, those would be untyped arguments (in the first example, the argument name would be confusingly 'str' and the type 'Any'; in the second, we'd have 'b: Any').
But I honestly don't know what users would expect that to mean -- probably if the name is a known type, they'd expect it to be a type, but if it wasn't, they'd expect it to be an argument name -- but that's not a rule we can implement.
That's a good point, and that seems like an additional argument to make this an error.
This confusion can also appear under your proposal though: maybe a user will write (a, b) -> bool expecting a function with two arguments of any type.
As a side note, CPython is already able to parse a subset of this syntax, though the feature is undocumented:
ast.dump(compile("(a, b) -> int", "", mode="func_type", flags=ast.PyCF_ONLY_AST))
"FunctionType(argtypes=[Name(id='a', ctx=Load()), Name(id='b', ctx=Load())], returns=Name(id='int', ctx=Load()))"
--
--Guido van Rossum (python.org/~guidohttp://python.org/~guido)
Pronouns: he/him (why is my pronoun here?)http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...