On April 22, 2021 3:15:27 PM GMT+02:00, Paul Moore <p.f.moore@gmail.com> wrote:
On Thu, 22 Apr 2021 at 13:23, Adrian Freund <mail@freundtech.com> wrote:
According to PEP 484 all missing annotations in checked functions should be handled as Any. Any is compatible with all types.
Yep, that's what I understood to be the case.
I think from a technical standpoint it should be possible to infer protocols for arguments for most functions, but there are some edge cases where this would not be possible, making it impractical to make this the default behavior. Having an annotation to make a type checker infer a protocol would be interesting though.
Absolutely, I see no problem with "use duck typing for this argument" being opt-in.
For example:
def f(x: int): ... def g(x: str): ...
def main(t): if t[0] == 'version': f(t[1]) elif t[0] == 'name': g(t[1])
You could statically type t as Union[Tuple[Literal['version'], int], Tuple[Literal['name'], str]], but inferring a Protocol for this would be either very hard or even impossible, especially with even more complex conditions.
Yes, but that's inferred static typing which is *not* what I was proposing. I think I understood what you were proposing, but my example might have been less than ideal. Sorry for that. I mixed some static types in there to simplify it. The union wasn't meant at what it should infer but was meant as a comparison to what we would to currently, with static, nominal typing.
Let me try again without static types. def file(x): print(x.read()) # x has to have .read(): object def string(x): print(str(x)) # x has to have .__str__(self): object def main(t): if t[0] == 'file': file(t[1]) elif t[0] == 'string': string(t[1]) Here we can infer that t has to have a __getitem__(self, idx: int), but we can't infer it's return type
I was suggesting that the checker could easily infer that t must have a __getitem__ method, and nothing more. So the protocol to infer is
class TypeOfT(Protocol): def __getitem__(self, idx): ...
It would be nice to go one step further and infer
class TypeOfT(Protocol): def __getitem__(self, idx: int): ...
but that's *absolutely* as far as I'd want to go. Note in particular that I don't want to constrain the return value The problem is that this isn't enough to have a type safe program. You need to also constrain the return type to make sure the returned value can be safely passed to other functions. If you don't do this large parts of your codebase will either need explicit annotations or will be unchecked. - we've no way to know what type it might have in the general case. IMO, inferring anything else would over-constrain t - there's nothing in the available information, for example, that says t must be a tuple, or a list, or that t[3] should have any particular type, or anything like that. You can infer the return type of a function by looking at all the returns it contains, and inferring the types of the returned expressions. That isn't too hard and pytype for example already does it.
You can infer the return type a protocol function should have by looking at all the places it's result are used. If you have inferred return types then constraining return types using inferred protocols would be practical in my opinion.
My instinct is that working out that t needs to have a __getitem__ that takes an int is pretty straightforward, as all you have to do is look at where t is used in the function. Four places, all followed by [] with a literal integer in the brackets. That's it. I fully appreciate that writing *code* to do that can be a lot harder than it looks, but that's an implementation question, not a matter of whether it's reasonable as a proposal in theory.
This feels like *precisely* where there seems to be a failure of communication between the static typing and the duck typing worlds. I have no idea what I said that would make you think that I wanted anything like that Union type you quoted above. And yet obviously, you somehow got that message from what I did say.
Like I said above the Union. Was just meant as an example of that we would do with static, nominal typing, not what we want with duck typing. Sorry for the misunderstanding.
Anyway, as I said this is just an interesting idea as far as I'm concerned. I've no actual need for it right now, so I'm happy to leave it to the mypy developers whether they want to do anything with it.
Paul