If I can make a wild suggestion: why not create a little language for type 
specifications?

If you look at other programming languages you’ll see that the “type definition 
sub-language” is often completely different from the “execution sub-language”, 
with only some symbols in common and used in vaguely related ways.  `bool 
(*myfuncptr)(int, float*)` uses a completely different set of syntactic rules 
than `rv = (*myfunptr)(*myintptr, &myfloat)`. So with some grains of salt you 
could say that C is comprised of a declarative typing sublanguage and an 
imperative execution sublanguage.

Python typing uses basically a subset of the execution expression syntax as its 
declarative typing language.

What if we created a little language that is clearly flagged, for example as 
t”….” or t’….’? Then we could simply define the typestring language to be 
readable, so you could indeed say t”(int, str) -> bool”. And we could even 
allow escapes (similar to f-strings) so that the previous expression could also 
be specified, if you really wanted to, as t”{typing.Callable[[int, str], bool}”.

Jack

> On 7 Oct 2021, at 18:41, S Pradeep Kumar <gohan...@gmail.com> wrote:
> 
> Hello all,
> 
> Typing-sig has been discussing user-friendly syntax for the type used to 
> represent callables. [1] Since this affects the Python language syntax, we 
> wanted to get some high-level feedback from you before putting up a detailed 
> PEP.
> 
> TL;DR: We want to propose syntax for Callable types, e.g., `(int, str) -> 
> bool` instead of `typing.Callable[[int, str], bool]`. Questions: 1. Are there 
> concerns we need to keep in mind about such a syntax change? 2. Should we 
> propose syntax for additional features in the same PEP or in a future PEP?
> 
> 
> # Motivation
> 
> Guido has pointed out that `Callable` is one of the most frequently used type 
> forms but has the least readable syntax. For example, say we have a callback 
> `formatter` that accepts a record and a list of permissions:
> 
> ```python
> def print_purchases(
>     user,
>     formatter,  # <-- callback
> ):
>     <...>
>     output = formatter(record, permissions)
>     print(output)
> ```
> 
> To give it a type, we currently have to write:
> 
> ```python
> from typing import Callable
> 
> def print_purchases(
>     user: User,
>     formatter: Callable[[PurchaseRecord, List[AuthPermission]], 
> FormattedItem]  # Hard to read.
> ) -> None:
> 
>     <...>
>     output = formatter(record, permissions)
>     print(output)
> ```
> 
> `Callable` can be hard to understand for new users since it doesn't resemble 
> existing function signatures and there can be multiple square brackets. It 
> also requires an import from `typing`, which is inconvenient. Around 40% of 
> the time [2], users just give up on precisely typing the parameters and 
> return type of their callbacks and just leave it as a blank Callable. In 
> other cases, they don't add a type for their callback at all. Both mean that 
> they lose the safety guarantees from typing and leave room for bugs.
> 
> We believe that adding friendly syntax for Callable types will improve 
> readability and encourage users to type their callbacks more precisely. Other 
> modern, gradually-typed languages like TypeScript (JS), Hack (PHP), etc. have 
> special syntax for Callable types.
> 
> (As a precedent, PEP 604 recently added clean, user-friendly syntax for the 
> widely-used `Union` type. Instead of importing `Union` and writing `expr: 
> Union[AddExpr, SubtractExpr], we can just write `expr: AddExpr | 
> SubtractExpr`.)
> 
> 
> # Proposal and Questions
> 
> We have a two-part proposal and questions for you:
> 
> 1. Syntax to replace Callable
> 
> After a lot of discussion, there is strong consensus in typing-sig about 
> adding syntax to replace Callable. So, the above example would be written as:
> ```python
> def print_purchases(
>     user: User,
>     formatter: (PurchaseRecord, List[AuthPermission]) -> FormattedItem,
> ) -> None:
> 
>     <...>
>     output = formatter(record, permissions)
>     print(output)
> ```
> This feels more natural and succinct. 
> 
> Async callbacks currently need to be written as 
> ```
> from typing import Callable
> 
> async_callback: Callable[[HttpRequest], Awaitable[HttpResponse]]
> ```
> 
> With the new syntax, they would be written as 
> ```
> async_callback: async (HttpRequest) -> HttpResponse
> ```
> which again seems more natural. There is similar syntax for the type of 
> decorators that pass on *args and **kwargs to the decorated function.
> 
> Note that we considered and rejected using a full def-signature syntax like 
> ````
> (record: PurchaseRecord, permissions: List[AuthPermission], /) -> 
> FormattedItem
> ````
> because it would be more verbose for common cases and could lead to subtle 
> bugs; more details in [3].
> 
> The Callable type is also usable as an expression, like in type aliases 
> `IntOperator = (int, int) -> int` and `cast((int) -> int, f)` calls.
> 
> **Question 1**: Are there concerns we should keep in mind about such a syntax 
> proposal?
> 
> 
> 
> 2. Syntax for callback types beyond Callable
> 
> `Callable` can't express the type of all possible callbacks. For example, it 
> doesn't support callbacks where some parameters have default values: 
> `formatter(record)` (the user didn't pass in `permissions`). It *is* possible 
> to express these advanced cases using Callback Protocols (PEP 544) [4] but it 
> gets verbose.
> 
> There are two schools of thought on typing-sig on adding more syntax on top 
> of (1):
> 
> (a) Some, including Guido, feel that it would be a shame to not have syntax 
> for core Python features like default values, keyword arguments, etc.
> 
>     One way to represent default values or optionally name parameters would 
> be:
> 
>     ```
>     # permissions is optional
>     formatter: (PurchaseRecord, List[AuthPermission]=...) -> FormattedItem
>     
>     # permissions can be called using a keyword argument.
>     formatter: (PurchaseRecord, permissions: List[AuthPermission]) -> 
> FormattedItem
>     ```
> 
>     There are also alternative syntax proposals.
> 
> (b) Others want to wait till we have more real-world experience with the 
> syntax in (1).
> 
>     The above cases occur < 5% of the time in typed or untyped code [5]. And 
> the syntax in (1) is forward-compatible with the additional proposals. So we 
> could add them later if needed or leave them out, since we can always use 
> callback protocols.
> 
> **Question 2**: Do you have preferences either way? Do we propose (1) alone 
> or (1) + (2)?
> 
> 
> Once we get some high-level feedback here, we will draft a PEP going into the 
> details for various use cases.
> 
> Best,
> Pradeep Kumar Srinivasan
> Steven Troxler
> Eric Traut
> 
> PS: We've linked to more details below. Happy to provide more details as 
> needed.
> 
> [1]: typing-sig thread about the proposal: 
> https://mail.python.org/archives/list/typing-...@python.org/message/JZLYRAXJV34WAV5TKEOMA32V7ZLPOBFC/
>  
> <https://mail.python.org/archives/list/typing-...@python.org/message/JZLYRAXJV34WAV5TKEOMA32V7ZLPOBFC/>
> [2]: Stats about loosely-typed Callables: 
> https://github.com/pradeep90/annotation_collector#typed-projects---callable-type
>  
> <https://github.com/pradeep90/annotation_collector#typed-projects---callable-type>
> [3]: Comparison and rejection of proposals: 
> https://www.dropbox.com/s/sshgtr4p30cs0vc/Python%20Callable%20Syntax%20Proposals.pdf?dl=0
>  
> <https://www.dropbox.com/s/sshgtr4p30cs0vc/Python%20Callable%20Syntax%20Proposals.pdf?dl=0>
> [4]: Callback protocols: 
> https://www.python.org/dev/peps/pep-0544/#callback-protocols 
> <https://www.python.org/dev/peps/pep-0544/#callback-protocols>
> [5]: Stats on callbacks not expressible with Callable: 
> https://drive.google.com/file/d/1k_TqrNKcbWihRZdhMGf6K_GcLmn9ny3m/view?usp=sharing
>  
> <https://drive.google.com/file/d/1k_TqrNKcbWihRZdhMGf6K_GcLmn9ny3m/view?usp=sharing>_______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-le...@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at 
> https://mail.python.org/archives/list/python-dev@python.org/message/VBHJOS3LOXGVU6I4FABM6DKHH65GGCUB/
> Code of Conduct: http://python.org/psf/codeofconduct/

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/7UZPUMLOVU3235CAAFUP2UWXEZ5XKIUJ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to