On Friday, September 6, 2019, 5:41:23 PM PDT, Steven D'Aprano 
<st...@pearwood.info> wrote:
 
 >On Fri, Sep 06, 2019 at 07:44:19PM +0000, Andrew Barnert wrote:

>> > Union, and unions, are currently types:
>> 
>> > py> isinstance(Union, type)
>> > True
>> > 
>> > py> isinstance(Union[int, str], type)
>> > True
>> 
>> What version of Python are you using here?
>
> Ah, I didn't realise that the behaviour has changed! I was using Python 
> 3.5, but I just tried again in 3.8:
I didn't even consider that this might be an old feature that was taken away, 
rather than a new feature that was added. Oh well, at least it gave me an 
excuse to configure and build 3.9. :)
> I don't know the reason for this change, but in the absense of a > compelling 
> reason, I think it should be reversed.

My thinking is in the opposite direction. I'm assuming Guido or Jukka or 
whoever wouldn't have made this change without a compelling reason. And if we 
can't guess that reason, then someone (who cares more about pushing this change 
than me) has to do the research or ask the right people to find out. Without 
knowing that, we can't really decide whether it's worth reversing for all 
types, or just for union types specifically, or whether the proposal needs to 
be rethought from scratch (or scrapped, if that's not possible), and the 
conservative default should be the last one.
But hopefully the default won't matter, because someone will care enough to 
find out the reasons and report them to us. (Or maybe Guido will just see this 
discussion and have the answers on the top of his head.)
> Steven (me):

>> > But I disagree that int|str is a subclass of int|str|bytes. There's 
>> > no subclass relationship between the two: the (int|str).__bases__ 
>> > won't include (int|str|bytes), 

> Andrew replied:

> > First, `issubclass` is about subtyping, not about declared inheritance.

> That could be debated. help(subclass) says:

> "Return whether 'cls' is a derived from another class or is the same > class."

> but of course ABCs and virtual subclassing do exist.> My position is that 
> in Python, issubclass is about inheritance, but you can fake it if you 
> like, in which case "consenting adults" applies.
But it's not just a "consenting adults" feature that users can do whatever they 
want with, it's a feature that's used prevalently and fundamentally in the 
standard library—including being the very core of typing.py—and always with the 
consistent purpose of supporting (a couple of minor variations on the idea of) 
subtyping. So sure, technically it is inheritance testing with a bolt-on to add 
whatever you want (and of course historically, that _is_ how it evolved), but 
in practice, it's subtype testing.
Thanks to the usual practicality-beats-purity design, it isn't actually testing 
_exactly_ subtype either, of course. After all, while it would be silly to 
register `int` with `collections.abc.Sequence`, there's nothing actually 
stopping you from doing so, and that obviously won't actually make `int` a 
subtype of `Sequence`, but it will fool `issubclass` into believing it is. But, 
except when you're intentionally breaking things for good or bad reasons, what 
it tests is much closer to subtype than inheritance. In fact, even when you 
break things for good reasons, often it's to better match the subtyping 
expectation, not to violate it. (Consider `Sequence` and `Mapping`, which act 
as if they were structural subtyping tests like the other collection ABCs, 
despite the fact that it's actually impossible to distinguish the two types 
that way so they cheat with a registry. Try doing that with Swift or Go. :)
> This Stackoverflow post:
> 
> union types in scala with subtyping: A|B <: A|B|C
> 
> suggests that Scala considers that int|str is *not* a subtype of 
> int|str|bytes, but Scala.js considers that it is.
Without reading the whole question carefully, and without refreshing my fuzzy 
memory of Scala's type system, I think there are two things going on here.
First, the OP in that question seems to be trying to build his own disjunction 
type that acts like a union in other languages (including, apparently, 
Scala.js?), and he just didn't know how to code it right.
So, why would anyone do that in the first place? Well, the bigger issue is that 
Scala's unions aren't quite the same thing we're talking about here in the 
first place—despite the fact that they're what inspired this whole thread. In 
most languages, the union of int and str is a special type that's defined by 
including all int values and all str values (and nothing else), or something 
similar to that. In Scala, the union of int and str is defined as the least 
upper bound of int and str on the lattice of all types (which of course 
provably does include all int values and all str values, because int and str 
are subtypes). In simple cases this ends up doing the same thing. And when they 
differ, it's usually that Scala can infer something cool that other languages 
can't. But I do vaguely remember one case where Scala couldn't infer a type for 
me and (unlike, say, Haskell or Kotlin—which may fail more often, but can 
always tell you exactly why they failed and what you need to add to fix it) it 
couldn't tell that it couldn't infer it, and it went and did something crazy 
like… an infinitely-long compile that ate up all my disk space or something? I 
forget.
> But I will point out that the name of the function is *issubclass*, not 
> *issubtype*. 
Sure, and the `class` statement creates a `type` instance. Which are sometimes 
called `classes` and sometimes `types`. Since 2.3, all classes are types and 
all types are classes, and the root of the metaclass hierarchy is called 
`type`, and Python just mixes and matches the two words arbitrarily, and it 
almost never causes confusion. (Of course in pre-2.3 Python, they were 
completely unrelated things, and `issubclass` only worked on classes, hence the 
name.)
If you try and import a meaning from another language, then sometimes it can 
get confusing. But think about it: in, say, Java, "subclass" explicitly means 
only a concrete class inheriting implementation from another concrete class; a 
class implementing an interface is not subclassing. Which means a test for 
subclassing in the Java sense would be (a) not what Python does, and (b) 
pointless. 
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/F7BFCNAIRTHH5QRHIBACN7B5U2LLRBU4/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to