PEP Idea: Extend spec of Callable to accept unpacked TypeDicts to specify keyword-only parameters

Thanks @guido for the analysis and ideas. I think this is a promising line of thinking.

I unearthed some slides that I presented at a typing meetup where we discussed options for PEP 677. Slide 3 is probably the most informative. It lists the various features supported by a callback protocol and compares / contrasts that with the features supported by each of the syntax options we were exploring at the time. Your lambda proposal is most similar to the variant we called “hybrid syntax” in my slides.

Here’s an updated table that compares the Callable and callback protocol mechanisms with PEP 677 and your proposed lambda syntax. I’ve also added some new rows for features that have been added to the type system in the intervening years.

Feature Callable Callback protocol PEP 677 lambda Syntax
1. Positional-only params :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
2. Any input signature (…) :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
3. Positional + keyword params :white_check_mark: :white_check_mark:
4. Default args :white_check_mark: :white_check_mark:
5. Variadics: *args & **kwargs :white_check_mark: :white_check_mark:
6. Keyword-only params :white_check_mark: :white_check_mark:
7. async keyword :white_check_mark: :white_check_mark:
8. ParamSpec :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
9. Concatenate :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
10. TypeVarTuples :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
11. Overloads :white_check_mark:
12. Unpacked TypedDict (PEP 692) :white_check_mark: :white_check_mark:
13. Type parameters (PEP 695) :white_check_mark: ?

The lambda syntax is more flexible than the existing Callable mechanism and the PEP 677 proposal. There are still a few features that are possible only with a callback protocol, but I think that’s fine.

I wonder if we could (or should) make line 13 work. That would address a type system feature gap that is often raised in the typing forum. I think the syntax would be straightforward and natural, but the runtime scoping rules might be challenging to implement. I’d look to @Jelle (informed by the work he did on PEP 695) to say whether it’s feasible to implement.

lambda [T] (x: T) -> T: x

Here’s how it would look when used in a return type annotation.

def get_transform() -> (lambda [T] (x: T) -> T: ...): ...
11 Likes