Hacker News new | past | comments | ask | show | jobs | submit login

In C++, if I have a

    std::list<Parent *>
then some of those pointers may actually point at a Child. The client code doesn't care. This is an important kind of polymorphism.

In Haskell, you can't reasonably express this with typeclasses, which surprises folks new to Haskell. You can still express it (as I mentioned), it just takes a different form (and precisely which form is best can vary with other considerations).




I don't know what you consider reasonable, but you can express this in Haskell with type classes and existential types:

   data P = forall a . Parent a => P a
   type PList = [P]
where Parent is a type class.

You have to be a bit more explicit when using a Child in the position of a Parent when adding to the list and you have to use (fully polymorphic!) pattern matches to extract elements from the list, but personally I consider this a good thing as it's more explicit.

Aside: Existential types is what OOP interfaces are -- and interfaces are often encoded as such in language semantics; see e.g. Types and Programming Languages (Pierce).

EDIT: Typos in code -- unfortunately my Haskell is a little rusty :(.


Yes, "Haskell can't reasonably express this with just typeclasses" is probably what I should have said. With the right extensions, Haskell can do anything, but it's not always going to be a good idea...


What's with the weasel words? Existential types aren't even remotely controverisial or dangerous.


In Haskell you don't need to express this with type classes as it is trivially covered by sum types. Type classes are mostly syntactic sugar providing for ad-hoc polymorphism. The real power is provided by the underlying algebraic data types.


Sum types only cover this trivially when the type is closed or when those adding new subtypes can be expected to modify all uses of the type. It's still not the same thing.


The need for an open sum tends to be vanishingly rare in my experience.


It occurs moderately frequently in libraries where a user should be able to define domain specific types along with how the library should treat those types. A classic example would be a raytracer where the user might be adding new kinds of scene elements. It probably shouldn't occur in application code.

For what it's worth, I do think people underestimate the applicability of closed sum types.


I'm aware of the raytracer library example. The solution to that is to not use types to represent shapes. Instead, classify shapes by primitive types (triangles, quads, bezier curves, NURBS, etc.) and use a closed sum for those. It's far less common for a user to want to create a new primitive type and you can always use an escape hatch in the closed sum that allows the user to define their own primitive along with a function to draw it in terms of one of the other primitives.


This thread got silly a while back. I'm abandoning it.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: