Reputation: 173
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?
Upvotes: 17
Views: 1547
Reputation: 116139
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:
Repeat the fields. This is the most basic option.
data Foo = A Int | B String | C Int String
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
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).
Upvotes: 27