On Oct 13, 2019, at 18:39, Steven D'Aprano <st...@pearwood.info> wrote:
> 
> Even when the class can be identified as equivalent to 
> its set of values, such as for numeric types, there are all sorts of 
> complexities that will only lead to confusion:
> 
>    from numbers import Real
>    1.0 in Real  # okay, unproblematic
>    2+3j in complex  # also okay
>    1 in complex  # mathematically true, but isinstance-wise false
>    float('INF') in Real  # isinstance-wise true, but mathematically false.

I agree with your conclusion, but I think this is the wrong way to argue it.

`1 in complex` is _not_ mathematically true. Simplifying a bit, the elements of 
the algebra of complex numbers are ordered pairs of real numbers, and `1` is 
not a pair. When you’re working in, say, complex analysis, you just assume the 
usual morphism that maps every real to a unique complex (and some complexes to 
a unique real), and you can loosely multiply a real by a complex and so on. But 
when you’re working on the actual algebras or the sets/types beneath them, you 
can’t do that. `R` is not a subset of `C`, and `1 in C` is just not true. In 
other words, the distinction in Python—that 1 and 1+0j are in some sense “the 
same number” but in another sense not even the same type of number—is exactly 
the same as in math; you don’t need anything extra to justify it.

As for infinity: the problem there is that the algebra of IEEE floats just 
isn’t the same thing as the real algebra (and in fact it’s closer to rationals 
than to reals). By pretending otherwise, Python has already bitten that bullet. 
And if the type you want to call `Real` does include IEEE floats, then 
mathematically it does include `inf`.

Finally, it’s not just numeric types, but all types that are identifiable as 
the set of their values (plus possibly some additional structure). The fact 
that there are a potentially infinite number of `Dog` instances doesn’t mean 
they don’t form a set, or that `fido` isn’t a member of that set; `N` (and, in 
fact, Python `int`) is just as infinite, and `R` is just as non-denumerable.

All that being said, it isn’t _useful_ to treat Python types as pure 
type-theory constructs. They make for a good analogy, and a good grounding to 
argue for changes to Python’s type system, but they’re not the same thing. What 
`isinstance` means is not inclusion in a set, or at least not one that can be 
expressed even in an approximate way in Python. Python allows for subclasses 
that aren’t subtypes, and instance checking that approximates structural 
subtypes, and even explicitly lying to the type system for good practical 
reasons, and all of that is what `isinstance` means. And that’s not confusing 
in practice, but using `in` to mean the same thing would be.

Also, from a practical point of view, why do we need another way to spell the 
isa relationship when we already have a perfectly good—and more flexible (e.g., 
surely `spam in (Eggs, Cheese)` wouldn’t mean the same thing as 
`isinstance(spam, (Eggs, Cheese))`)—way to do it. TOOWTDI isn’t an iron-clad 
rule, but this is about as close to a paradigm case where it should apply as 
you’re ever going to see.

Finally, in languages that care a lot more about this kind of thing (like 
Haskell, or Scala), either types aren’t first-class objects at all, or they’re 
first-class objects into a two-kinded system; they’re definitely not sets at 
the language level. So why should Python try to go farther than any of them?

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

Reply via email to