[Python-Dev] Re: Function Prototypes
On Fri, Dec 24, 2021 at 11:53:22AM +0200, Serhiy Storchaka wrote: > Some library provide function foo() which returns an instance of private > type _Foo and people start using it as a type hint. If people want to shoot themselves in the foot, is it our job to stop them? Typically the only promise we make is that the interpreter won't segfault, not to protect coders from making bad decisions in their code. If _Foo is private, you shouldn't be using it as a type hint, not even indirectly through a function prototype. *Everything* about _Foo is subject to change without notice, including its very existence. It seems a bit strange to accept the risk of unpredictable changes to a private implementation detail, while worrying about backwards- incompatible changes to a public function. > A new version > converts foo() into a class. It is usually a safe backward compatible > change, except that now all type hints became wrong. It is a safe backwards compatible change only if you don't care about the type of foo. As soon as you use foo as a function prototype, then you now care about its type, just as much as if you inspected it with type() or isinstance(). In other words, it's not *actually* a backwards compatible change in a language like Python where functions are first-class objects. We can just get away with that in a context where the type of the callable usually doesn't matter, just as we usually don't care if the repr() changes. In this case, if we introduce function prototypes, then people will learn that changing a factory function to a class is a breaking change for people who do type-checking, just as changing the repr of objects is a breaking change for people who use doctests. On the other hand, changing a function to a callable instance should not be a breaking change, if we follow my earlier suggestion that objects with a `__call__` method should be usable as prototypes too. -- Steve ___ Python-Dev mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/[email protected]/message/O7IHYHVGJMXO6WJQOIJACARRU6QJC232/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: Function Prototypes
On Fri, Dec 24, 2021 at 06:24:03PM -, Jim J. Jewett wrote: > Steven D'Aprano wrote: > > In comparison, Mark's version: > > @Callable > > def IntToIntFunc(a:int)->int: > > pass > > # in the type declaration > > func: IntToIntFunc > > uses 54 characters, plus spaces and newlines (including 7 punctuation > > characters); it takes up three extra lines, plus a blank line. As > > syntax goes it is double the size of Callable. > > I think it takes only the characters needed to write the name IntToIntFunc. That's only true if IntToIntFunc is a builtin, otherwise it needs to be defined somewhere. It doesn't just magically happen. If you are using the declaration many times, then I acknowledge that it may be worth the effort of pre-declaration and naming. (But see below.) Particularly if the signature is complicated, although I think that will be relatively rare. But in the worst case, you may only use it once. So the entire cognitive burden of pre-declaration (both writing it and reading it) applies to that one use. > The @callable def section is a one-time definition, and not logically > part of each function definition where it is used. The status quo is that we can use an anonymous type in the annotation without pre-defining it, using Callable. PEP 677 proposes a new, more compact syntax for the same. Any proposal for function prototypes using `def` is directly competing against Callable or arrow syntax for the common case that we want an anonymous, unnamed type written in place. Even in the case that we want to give the type a name that we plan to use repeatedly, this `def` syntax is still competing directly against what is already possible using the status quo: use Callable to create a named type alias. But with the `def` syntax, you can *only* use it as a named, pre-defined object. So half, maybe 90%, of your use-cases disappear. Any time that we have a short, simple Callable that doesn't require a name, why would we bother creating a do-nothing function just so we can use it as a prototype? I don't think many people would. I know I wouldn't. That would be the equivalent of filling your program with trivial plus_one(x) and times_two(y) functions instead of just using `x+1` and `2*y`. So the benefit of the `def` syntax comes from that relatively small subset of cases: - the callable signature is complicated; - we wish to refer it it multiple times; - giving it a name (like "FileOpener", say, not "IntToInt") aids clarity. That's not to be sneered at. But in those circumstances, we don't need the `def` syntax, because we can already use Callable and a type alias. So the `def` syntax adds nothing we don't already have, it is no easier to use, it is more verbose, not less. But if we can use an existing function as the prototype instead of having to declare the prototype, that shifts the balance. If we already have some function, then there is no extra cost in having to declare it and give it a name, it already has been declared and given a name. > I get that some people prefer an inline lambda to a named function, > and others hate naming an infrastructure function, but ... > > Why are you even bothering to type the callback function? If it is > complicated enough to be worth explicitly typing, then it is > complicated enough to chunk off with a name. I would say the opposite: most callback or key functions have very simple signatures. If my function takes a key function, let's say: def spam(mylist:[str], a: int, b: float, c: bool|None, key: Callable[[str], str], ) -> Eggs: mylist = sorted(mylist, key=key) ... the relevant signature is (str) -> str. Do we really need to give that a predefined named prototype? def StrToStr(s: str) -> str: pass I would argue that very few people would bother. If somebody did, they probably also defined type aliases for ListOfStr and BoolOrNone, and wish they were using Java or Pascal *wink* It seems to me that most callbacks and key functions have short signatures. Certainly all the ones I have written do: they typically take a single argument, of a known type, and return a known type. > Having to switch parsing modes to understand an internal ([int, float, > int] -> List[int]), and then to pop that back off the stack is much > harder. I notice that you just used something very close to PEP 677 arrow syntax totally unself-consciously, without any need to explain it. I think this is good evidence that far from being confusing, this is a completely natural syntax that we already interpret as a function prototype. > Hard enough that you really ought to help your reader out with a > name, What are you going to name it? Int_and_Float_and_Int_returns_List_of_Int_Function tells us nothing that (int, float, int) -> list[int] Callable[[int, float, int], list[int]] doesn't already say
[Python-Dev] Re: Function Prototypes
I've done some more thinking more about Serhiy's worry about changing a factory function to a class, and how that would change the meaning of type-hints. Say: def spam(x: Eggs, y:Cheese) -> _Aardvark: # actual factory function implementation # later, we use it as a function protocol def myfunction(a: int, callback: spam) -> str: ... If spam changes implementation from a factory function to the actual class of _Aardvark, keeping the name: class spam: # body of _Ardvark goes here that would completely change the meaning of the myfunction type declaration. I still think that is not a scenario we need to care about. In my mind, that counts as a user-applied footgun. But if people disagree, and Serhiy's argument persuades them, then we can still use functions as their own prototype. @Callable def spam(x: Eggs, y: Cheese) -> _Aardvark: # actual factory function implementation The Callable decorator would just flag the function as *permitted* to be used as a prototype, with no other change. Or perhaps we could have @FunctionPrototype. Alternatively, we could write the consumer's annotation like this: def myfunction(a: int, callback: Prototype[spam]) -> str: ... where Prototype[x] uses: - the signature of x if x is a function or method; - the signature of `x.__call__` if x is a callable instance; - the signature of `x.__new__` or `x.__init__` is x is a class; as the prototype. However it is spelled, we might require functions to opt-in before they can be used as prototypes, in other words the decorator is the author's promise that they aren't going to change the function into a class, or change its signature, without the usual deprecation warnings etc. -- Steve ___ Python-Dev mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/[email protected]/message/MOZOXB73UIYGE745ZWORG4DWYM2PD72N/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] issues-test-2 spam?
Apologies if this is the wrong place to raise this (where is the right place?) but over the last four days, I've received ten subscription notices for python/issues-test-2 on Github. Is anyone else also getting multiple subscription notices? -- Steve ___ Python-Dev mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/[email protected]/message/Q37XLFRF2H3OQFV55D7ASILCQ57XO6XE/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: issues-test-2 spam?
> Is anyone else also getting multiple subscription notices? > Yup. In an earlier thread (here? discuss.python.org?) I thought it was established that someone was working on something related to Python bug tracking in GitHub. Or something like that. I've just been deleting them. Skip ___ Python-Dev mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/[email protected]/message/MOS6XIDBWLQYHKO4BFO64WF4CXSM5YCZ/ Code of Conduct: http://python.org/psf/codeofconduct/
