> Another quick datapoint: At the moment, typeshed has 16 callable protocols in the stdlib alone that don't use positional only arguments. Many of these protocols have arguments with default values. 

Sebastian: Yes, I'd shared callback protocol usage in an earlier mail: 
+ projects with detailed types (like mypy, typeshed, spark) have callback protocols making up 2.3% of all callables. [1] 
+ other projects with loosely-typed callables (sphinx, jax, etc.) don't use any callback protocols. [2] 

The remaining question was whether there were a lot more uses of non-positional callbacks in code that is not yet typed, perhaps due to the lack of easy syntax. That is why I also looked at untyped Python projects.

> These protocols can't be replaced using the shorthand syntax, but would need a more comprehensive syntax.

I agree that they aren't handled by the shorthand syntax. The question is whether we want the burden of the more comprehensive syntax to handle such a small minority of cases.

Given that these are very rare, in both typed and untyped projects, and that Łukasz's proposal will give us easy syntax for those cases, I feel we shouldn't add the extra complexity. But in any case, I hope the stats are useful for moving the discussion forward :)

[1]: https://github.com/pradeep90/annotation_collector#projects-with-well-typed-callables 

You can see the 37 callback protocols in typeshed at https://github.com/pradeep90/annotation_collector/blob/master/typeshed-callables.txt#L1314-L1364. These would all be easily handled by Łukasz's proposal of using function names as types.

[2]: https://github.com/pradeep90/annotation_collector#projects-with-loosely-typed-callables-mostly-f-callable

On Thu, Jul 22, 2021 at 3:47 AM Sebastian Rittau <srittau@rittau.biz> wrote:
Another quick datapoint: At the moment, typeshed has 16 callable protocols in the stdlib alone that don't use positional only arguments. Many of these protocols have arguments with default values. These protocols can't be replaced using the shorthand syntax, but would need a more comprehensive syntax.

 - Sebastian

Am 22.07.21 um 05:51 schrieb S Pradeep Kumar:
# How frequently does *untyped* Python code use complex callbacks?

I'd shared stats earlier about Callable usage in typed Python code [1]. By looking at the source code for stubbed files, we'd seen that practically all of the Callable uses in typed Python projects were either (a) expressible using positional-only parameters, (b) expressible using ParamSpec or TypeVarTuple, or (c) simply too dynamic.

The remaining possible source of callables is *untyped* Python code. Perhaps such code has a lot of callbacks that requires named parameters, default values, etc. Untyped Python is not constrained by our Callable type syntax, so if we are going to find complex uses anywhere, it will be in untyped Python.

I tested the above claim by looking at a few large, untyped, open-source Python projects (Django with 80k+ LOC of Python, Sentry with 150k+ LOC of Python).

Findings [2]:

+ About half of the callbacks were called with just positional arguments: `func(a, b)`.
+ More than a third of callbacks were called with `*args, **kwargs`: `func(*args, **kwargs)` or `func(prefix, *args, **kwargs)`. These would need ParamSpec.
+ The remaining ~13% were called with only `*args` or were too dynamic.

This is in line with the pattern I saw for typed Python projects [3]: 90+% of callbacks were either positional-only or ParamSpec.

# How often are named arguments used for callbacks?

Very rarely. I saw a couple of examples and they were basically all cases where the "callback" was a class, not a function:

```
def _generate_altered_foo_together(self, operation):
    x = operation(
name=model_name,
**{option_name: new_value}
    )


self._generate_altered_foo_together(operations.AlterUniqueTogether)
self._generate_altered_foo_together(operations.AlterIndexTogether)
```

In other words, the type would not be `Callable`. It would be `Type[ModelOperation]`:

```
def _generate_altered_foo_together(self, operation: Type[ModelOperation]):
```

# Conclusion

Based on the above stats, I feel confident that the vast majority of cases can be handled by the shorthand syntax proposal:

+ positional-only parameters: `(int, str) -> bool`  instead of Callable[[int, str], bool]
+ ParamSpec: `(**P) -> R`  instead of Callable[P, R]

Given how frequently ParamSpec is needed (20-30% of all callbacks), the simpler syntax above would lower the barrier to better types and increase type safety. More details are in the syntax proposal doc under the name "shorthand syntax". [4]

Finally, in order to support any remaining edge cases, we could adopt Łukasz's proposal about using the name of a function as a type (like we do with classes). This would be easier to use than a callback protocol.

At this point, if we want to add extra syntax for things like named parameters, default values, etc., I think we should justify the extra complexity using real-world cases. If not, I don't feel it's worth adding them and going through a ton of bikeshedding for something that is not going to add much value to the above. None of the syntax proposals handle overloads, so no syntax will capture all the cases that a callback protocol can. We have to draw the line somewhere; I suggest we draw it at the shorthand proposal.

# Footnotes

[1]: Earlier mail with stats for typeshed: https://mail.python.org/archives/list/typing-sig@python.org/message/TTPVR7QUK6MGSYRYMHHR4O23OZG6B6VP/

[2]: Summary of stats for untyped projects: https://github.com/pradeep90/annotation_collector#projects-with-no-types

List of callbacks in Django: https://github.com/pradeep90/annotation_collector/blob/master/data/django-callback-parameters.txt

I updated the libcst script to collect functions that called one of their parameters. This helped automatically analyze untyped code. I cross-checked that by sampling a significant number of functions (~400) from each project and manually checking how callbacks were used. I also looked for functions and parameters that had suggestive names like `func` or `callback` and checked how they were used.

[3]: Stats for many large, typed Python projects: https://github.com/pradeep90/annotation_collector#stats

[4]: Callable syntax proposals document: https://docs.google.com/document/d/11VkNk9N8whxS1SrBv2BNN1Csl5yeTlgG7WyLrCB5wgs/edit#heading=h.s6u1i3i5ct8r


On Mon, Jun 28, 2021 at 1:30 PM Guido van Rossum <guido@python.org> wrote:
At this point I feel that the choices are sufficiently clear-cut that we should just forge ahead and settle on a full proposal. And I agree with Jelle that the key point is introducing new syntax for callable types at all. The main point of contention may well be whether the "(...) -> ..." syntax shouldn't be a shorthand for lambda instead.

On Mon, Jun 28, 2021 at 1:08 PM Steven Troxler <steven.troxler@gmail.com> wrote:
Do we think it might be worth starting with the original plan of a PEP that just introduces shorthand syntax (as proposed at the start of this thread)?

That would allow us to defer a lot of discussion until later:
- How to add support for named / default / variadic arguments, if at all
- Whether it's more valuable to support param specs (e.g. for decorators) than ^
  - Pradeep's initial analysis of existing packages suggests this may be the case

I think the main thing we have to decide is whether we're aligned on any future changes being backward-compatible with shorthand, which rules out strict stub-style syntax with implicit Any but leaves all other options on the table.
_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: guido@python.org


--
--Guido van Rossum (python.org/~guido)
_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: gohanpra@gmail.com


--
S Pradeep Kumar

_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: srittau@rittau.biz


_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: gohanpra@gmail.com


--
S Pradeep Kumar