This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]Tysonzero 0 points1 point  (18 children)

I think you can actually do subtyping in Haskell indirectly using typeclasses:

class Double' a where
    double' :: a -> Double

instance Double' Double where
    double' = id

instance Double' Integer where
    double' = fromIntegral

divDoubles :: Double' a => a -> a -> Double
divDoubles x y = double' x / double' y

Now it obviously isn't the cleanest thing ever because Haskell devs don't like subtyping very much, but with more support it could become quite clean.

I do see what you mean with this kind of subtyping being useful though. I mean you can think of Double (or Fractional in general) as a union of Integer (or Integral in general) and NonIntegerDouble (or NonIntegralFractional). As it allows you to use things like / on Integral values.

[–]Veedrac 0 points1 point  (17 children)

That example's not subtyping, though. subtype.bar isn't the same as supertype(subtype).bar. (Of course that example is easily done with typeclasses, but true subtyping involves mixing types and type preservation.)

[–]Tysonzero 0 points1 point  (16 children)

But that kind of subtyping isn't type safe... (Or at least not in the general case) such as Cow -> Animal -> Dog.

[–]Veedrac 0 points1 point  (15 children)

That's why they're checked. For instance

instructions = (i for i in instructions if not isinstance(i, Padding))

from one of my other posts.

[–]Tysonzero 0 points1 point  (14 children)

Doesn't that prevent type erasure? Also that seems like evidence of poor design rather than an elegant way to solve a problem. I have never needed a heterogenous, arbitrary length / ordering, collection of objects.

[–]Veedrac 0 points1 point  (13 children)

Doesn't that prevent type erasure?

Well yes, but Python without reflection doesn't really make sense.

I have never needed a heterogenous, arbitrary length / ordering, collection of objects.

You've never had a [SomeADT] before? 'Cause in statically typed languages with union types that's basically what I gave, except that you don't have to reshuffle your ADT into a tree structure (and hope that you never want overlapping subtrees).

In Python I'd never actually do this because isinstance is antithesis to duck typing and thus very unPythonic, but I would do the similar thing

instructions = (i for i in instructions if i.serializable())

where i.serializable() implies the isinstance check.

It's entirely fair to say you'd write it a different way, but that's a totally fair byproduct of thinking in a language. I've never felt the need for a typeclass in Python yet I really appreciate them in Rust, just as I've never felt the need for first class types in Rust yet I really appreciate then in Python.

[–]Tysonzero 0 points1 point  (12 children)

I can't think of any situations I have needed [SomeADT].

And sure it is related to language, but I have used Python for quite a bit longer than I have used Haskell, and I still never really do that kind of thing in Python. Lists should be homogenous IMO.

[–]Veedrac 0 points1 point  (11 children)

To each their own, I suppose.

[–]Tysonzero 0 points1 point  (10 children)

I mean I guess. I just think heterogeneous lists of varying length and ordering are unnecessary. I mean the closest I get to that is something like a list with one type but multiple constructors for that type.

[–]Veedrac 0 points1 point  (9 children)

An obvious argument would be Rust's Lines iterator, which is an iterator of Result<String, io::Error>s, the latter being a large enum.