Skip to content

Functions can't be passed as callable protocol #5453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
srittau opened this issue Aug 11, 2018 · 2 comments
Closed

Functions can't be passed as callable protocol #5453

srittau opened this issue Aug 11, 2018 · 2 comments

Comments

@srittau
Copy link
Contributor

srittau commented Aug 11, 2018

This came up in python/typeshed#2379. Consider the following code:

from typing import Protocol

class Caller(Protocol):
    def __call__(self) -> None: ...

def call() -> None:
    pass

def func(caller: Caller) -> None:
    pass

func(call)

Currently, mypy 0.620 rejects this:

foo.py:12: error: Argument 1 to "func" has incompatible type "Callable[[], None]"; expected "Caller"

I believe that mypy should actually allow this, because call is actually an object that fulfills the protocol. This would also open up the opportunity to create callback signatures of any complexity (for example, default and keyword-only arguments) without the need for additional features for Callable.

@ilevkivskyi
Copy link
Member

This is a great idea! And it is easy to implement. TBH I never liked NamedArg, KwArg, etc. This will look much cleaner and flexible (because we can even express overloads as callbacks).

@srittau
Copy link
Contributor Author

srittau commented Aug 11, 2018

Credit for this idea goes to @podhmo. Interestingly, typescript does something similar. While it is possible to define callables just using

type MyCallback = (foo: number) => string

or similar, you can define overloaded functions like this:

interface MyCallback {
    () => number;
    (foo: number) => string;
}

@ilevkivskyi ilevkivskyi self-assigned this Aug 11, 2018
ilevkivskyi added a commit that referenced this issue Aug 16, 2018
Fixes #5453 

The issue proposed an interesting idea. Allow callables as subtypes of protocols with `__call__`. IMO this is not just reasonable and type safe, but is also more clear that the extended callable syntax in `mypy_extensions`. For example:
```python
class Combiner(Protocol):
    def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> List[bytes]: ...

def batch_proc(data: Iterable[bytes], cb_results: Combiner) -> bytes:
    ...
```

The callback protocols:
* Have cleaner familiar syntax in contrast to `Callable[[VarArg(bytes), DefaultNamedArg(Optional[int], 'maxlen')], List[bytes]]` (compare to above)
* Allow to be more explicit/flexible about binding of type variables in generic callbacks (see tests for examples)
* Support overloaded callbacks (this is simply impossible with the current extended callable syntax)
* Are easy to implement

If this will get some traction, I would actually propose to deprecate extended callable syntax in favor of callback protocols.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants