On Aug 29, 2019, at 16:03, Dominik Vilsmeier <[email protected]> wrote:
> 
> I never really understood the importance of `Optional`. Often it can be left 
> out altogether and in other cases I find `Union[T, None]` more expressive 
> (explicit) than `Optional[T]` (+ the latter saves only 3 chars).

> Especially for people not familiar with typing, the meaning of `Optional` is 
> not obvious at first sight. `Union[T, None]` on the other hand is pretty 
> clear. Also in other cases, where the default (fallback) is different from 
> `None`, you'd have to use `Union` anyway. For example a function that 
> normally returns an object of type `T` but in some circumstances it cannot 
> and then it returns the reason as a `str`, i.e. `-> Union[T, str]`; 
> `Optional` won't help here.

But this should be very rare.

Most functions that can return a fallback value return a fallback value of the 
expected return type. For example, a get(key, default) method will return the 
default param, and the caller should pass in a default value of the type 
they’re expecting to look up. So, this shouldn’t be get(key: KeyType, default: 
T) -> Union[ValueType, T], it should be get(key: KeyType, default: ValueType) 
-> ValueType. Or maybe get(key: KeyType, default: Optional[ValueType]=None) -> 
Optional[ValueType].

Most functions that want to explain why they failed do so by raising an 
exception, not by returning a string.

And what other cases are there?

Of course you could be trying to add type checking to some weird legacy 
codebase that doesn’t do things Pythonically, so you have to use Union returns. 
But that’s specific to that one weird codebase.

Meanwhile, Optional return values are common all over Python.

Also, Python’s typing system is a lot easier to grasp if you’re familiar with 
an established modern-typed language (Swift, Scala, Haskell, F#, etc.), and 
they also use Optional[T] (or optional<T> or Maybe t or some other spelling of 
the same idea) all over be place—so often that many of them have added 
shortcuts like T? to make it easier to write and less intrusive to read.

I think there may be a gap in the docs. They make perfect sense to someone with 
experience in one of those languages, but a team that has nobody with that 
experience might be a little lost. There’s a mile-high overview, a theory 
paper, and then basically just reference docs that expect you to already know 
all the key concepts that you don’t already know. Maybe that’s something that 
an outsider who’s trying to learn from the docs plus trial and error could help 
improve?

> Scanning through the docs and PEP I can't find strongly motivating examples 
> for `Optional` (over `Union[T, None]`). E.g. in the following:
> 
>    def lookup(self, name: str) -> Optional[Node]:
>        nodes = self.get(name)
>        if nodes:
>            return nodes[-1]
>        return None
> 
> I would rather write `Union[Node, None]` because that's much more explicit 
> about what happens.
> 
> Then introducing `~T` in place of `Optional[T]` just further obfuscates the 
> meaning of the code:
> 
>    def lookup(self, name: str) -> ~Node:
> 
> The `~` is easy to be missed (at least by human readers) and the meaning not 
> obvious.

That’s kind of funny, because I had to read your Union[Node, None] a couple 
times before I realized you hadn’t written Union[Node, Node]. :)

I do dislike ~ for other reasons (but I already mentioned them, Guido isn’t 
convinced, so… fine, I don’t hate it that much). But I don’t think ~ is easy to 
miss. It’s not like a period or backtick that can be mistaken for grit on your 
screen; it’s more visible than things like - that everyone expects to be able 
to pick out.

> For `Union` on the other hand it would be more helpful to have a shorter 
> syntax, `int | str` seems pretty clear, but what prevents tuples `(int, str)` 
> from being interpreted as unions by type checkers. This doesn't require any 
> changes to the built-in types and it is aligned with the already existing 
> syntax for checking multiple types with `isinstance` or `issubclass`: 
> `isinstance(x, (int, str))`. Having used this a couple of times, whenever I 
> see a tuple of types I immediately think of them as `or` options.

The biggest problem with tuple is that in every other language with a similar 
type system, (int, str) means Tuple[int, str].

I think {int, str}, which someone proposed in one of the earlier discussions, 
is nice. What else would a set of types mean (unless you’re doing mathematical 
type theory rather than programming language typing)? But it’s unfortunate that 
things like isinstance and except take a tuple of types (and it has to be a 
tuple, not any other kind of iterable), so a set might be just as confusing for 
hardcore Python types as a tuple would be for polyglots.

If the compatibility issue isn’t a big deal (and I trust Guido that is isn’t), 
I think int | str is the best option. It’a an operator that means union, it’s 
used for sum/union types in other languages, it makes perfect sense if you read 
it as “int or str”… I cant imagine anyone being confused or put off by it.
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/MQABKAQ454MVT3CWBFS5AZSIRA4ZJB5X/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to