On 4/22/21 5:00 PM, Paul Moore wrote:
> On Thu, 22 Apr 2021 at 15:22, Adrian Freund <m...@freundtech.com> wrote:
>> On April 22, 2021 3:15:27 PM GMT+02:00, Paul Moore <p.f.mo...@gmail.com> 
>> wrote:
>>> 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.
> But I don't want a type safe program. At least not in an absolute
> sense. All I want is for mypy to catch the occasional error I make
> where I pass the wrong parameter. For me, that's the "gradual" in
> "gradual typing" - it's not a lifestyle, just a convenience. You seem
> to be implying that it's "all or nothing".

I don't think that inferring the required return type breaks gradual
typing, but it is required for people who want type safety.

If I understand correctly your concerns with inferring return types for
inferred protocols are that it might be to restrictive and prevent
gradual typing. Here are some examples to show how gradual typing would
still work. If you have any concrete examples where inferring the return
type would break gradual typing let me know and I'll have a look at them.


def foo(x: DuckType):  # x has to have a .bar(self) method.
# The return type of which is inferred as Any, as it isn't used
    x.bar()



def bar(x):
    x.bar()

def foo(x: DuckType):  # x has to have a .read(self) method.
# The return type of which ist inferred as  Any, as the parameter to 
bar
isn't typed.
    bar(x.read())


Contrast that with


def bar(x: DuckType):  # x has to have a .bar(self) method.
# The return type of which is inferred as Any.
    x.bar()

def foo(x: DuckType):  # x has to have a .read(self) method that returns
something with a .bar(self) method.
# If we don't infer the return type our call to bar() might be unsafe
despite both foo and bar being typed.

    bar(x.read())

> I repeat, all I'm proposing is that
>
> def f(x: int): ...
> def g(x: str): ...
>
> def main(t: DuckTyped) -> None:
>     if t[0] == 'version':
>         f(t[1])
>     elif t[0] == 'name':
>         g(t[1])
>
> gets interpreted *exactly* the same as if I'd written
>
> class TType(Protocol):
>     def __getitem__(self, int): ...
>
> def f(x: int): ...
> def g(x: str): ...
>
> def main(t: TType) -> None:
>     if t[0] == 'version':
>         f(t[1])
>     elif t[0] == 'name':
>         g(t[1])
>
> How can you claim that the second example requires that " large parts
> of your codebase will either need explicit annotations or will be
> unchecked"? And if the second example doesn't require that, nor does
> the first because it's equivalent.

Both examples don't check the calls to f and g despite f and g both
being typed functions
and being called from typed functions.

In a real codebase this will lead to a lot more instances of this
happening. It would happen every time you do anything with something
returned from a method on an inferred protocol

>
> Honestly, this conversation is just reinforcing my suspicion that
> people invested in type annotations have a blind spot when it comes to
> dealing with people and use cases that don't need to go "all in" with
> typing :-(

I don't think this is an all in or nothing. You can infer return types
of inferred protocols and still use gradual typing.  It's just that not
inferring return types causes problems for both full and gradual typing.


Adrian Freund



_______________________________________________
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/KVB33BV6T6H7PFXN574CMFZUJOVSVUBV/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to