Haskell construct analogous to Rust trait objects

Viewed 161

Haskell supports type classes, like equality:

class Eq a where 
  (==)                  :: a -> a -> Bool

Rust does the same with type traits:

pub trait Draw {
    fn draw(&self);
}

Now, it's possible to declare in Haskell a list whose elements must belong to the equality type class: Eq a => [a] (I believe a is called a constrained type in Haskell). However, the elements of the list still must all be the same type! Say, all Integer or all Float or something. In Rust, however, one can have a list (a vector) of values where each implements a given trait but they are not necessarily the same concrete type: Vec<Box<dyn Draw>>. Is there a way to do the same in Haskell? Like, I want a list of values but all I care about is that each belong to some type class but not necessarily the same concrete type.

1 Answers

In Haskell, you can use existential types to express "some unknown type of this typeclass". (In older versions of GHC, you will need a few standard extensions on.)

class Draw a where
   -- whatever the methods are

data SomeDraw where
   SD :: Draw a => a -> SomeDraw

type MyList = [SomeDraw]

However, note that this is often overkill, and leads to a known anti-pattern.

For instance, if we had a class as follows:

class Draw a where
   draw :: a -> String

then the type MyList above is isomorphic to [String] (or at least morally such). There is no advantage to store an unknown "drawable" object whose only method converts it to string compared to storing the string directly. Also note that Haskell is lazy, so you can "store a string which is not evaluated yet", so to speak.

Anyway, existential quantification on typeclasses can also be defined in a generic way:

import Data.Kind

-- Ex has the same role of "dyn" in Rust here
data Ex (c :: Type -> Constraint) where
    Ex :: c a => a -> Ex c

type MyList = [Ex Draw]
Related