Search code examples
scalahaskellalgebraic-data-types

How to create ADT in Haskell?


In Scala I can describe such ADT:

sealed trait Foo
case class A(a: Int) extends Foo
case class B(b: String) extends Foo
case class C(a: A, b: B) extends Foo

How can I do the same in Haskell?

data Foo = A Int | B String | C A B

It doesn't work, because A and B are not types. Should I use GHC extensions to do it?


Solution

  • In Scala, your ADT makes A,B,C to be subtypes of Foo. In Haskell we do not have subtypes, so A,B,C are instead constructors of type Foo.

    A few possible workarounds:

    1. Repeat the fields. This is the most basic option.

      data Foo = A Int | B String | C Int String
      
    2. Define additional types, so that we can reuse them more than once.

      data AT = AT Int      -- can have many arguments
      data BT = BT String   -- can have many arguments
      data Foo = A AT | B BT | C AT BT
      
    3. Exploit a GADT

      data FooTag = AT | BT | CT
      
      data Foo (tag :: FooTag) where
         A :: Int -> Foo 'AT
         B :: String -> Foo 'BT
         C :: Foo 'AT -> Foo 'BT -> Foo 'CT
      

      Here, in the last line we are able to refer to "the values constructed using A" using the type Foo 'AT, since tag AT is only used by constructor A. Note that this approach adds a tag parameter to Foo, so it slightly changes the interface: we can no longer write bar :: Foo -> ..., but we have to write bar :: Foo t -> ... (or to use existential types).