
On Thu, Dec 23, 2021 at 7:38 PM Barry Warsaw <barry@python.org> wrote:
On Dec 23, 2021, at 17:09, Guido van Rossum <guido@python.org> wrote:
Mark's proposal was ``` @Callable def func(params): pass ``` My question is, why does it need `@Callable`? Lukasz proposed just using any (undecorated) function, with the convention being that the body is `...` (to which I would add the convention that the function *name* be capitalized, since it is a type). My question (for Mark, or for anyone who supports `@Callable`) is why bother with the decorator. It should be easy to teach a type checker about this:
``` def SomeFn(x: float) -> int: ...
def twice(f: SomeFn) -> SomeFn: return lambda x: f(f(x)) ```
That seems pretty intuitive to me. The conventions you mention would be just that though, right? I.e. `pass` could be used, but whatever the body is it would be ignored for type checking `twice()` in this case, right?
I think this was briefly mentioned in another thread, but it seems to have been lost in the discussion here, so I want to mention it again because I think it's important: aside from the verbosity and hassle of needing to always define callable types out-of-line and give them a name, another significant downside to the function-as-type approach is that generally Python signatures are too specific for intuitive use as a callback type. Note that in the example immediately above, a typechecker should error on this call: ``` def float_to_int(y: float) -> int: return int(y) twice(float_to_int) ``` The intent of the programmer was probably that `twice` should accept any function taking a single float argument and returning an int, but in fact, given the possibility of keyword calls, the names of non-positional-only parameters are part of the function signature too. Since the body of `twice` could call `f(x=...)`, a typechecker must error on the use of `float_to_int` as the callback, since its parameter is not named `x`. In order to correctly express their intent, the programmer must instead ensure that their callable type takes a positional-only argument: ``` def SomeFn(_x: float, /) -> int: ... ``` The need to almost always use the extra `/` in the callable type signature in order to get the desired breadth of signature, and the likelihood of forgetting it the first time around until someone tries to pass a second callback value and gets a spurious error, is in my mind a major negative to functions-as-callable-type. So I agree with Steven: this issue, plus the verbosity and out-of-line-ness, mean that Callable would continue to be preferable for many cases, meaning we'd end up with two ways to do it, neither clearly preferable to the other. I don't think out-of-line function-as-type could ever be an acceptable full replacement for Callable, whereas I think PEP 677 immediately would be. Carl