如何在Haskell中创建ADT?

17

在Scala中,我可以描述这样的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

我该如何在Haskell中做同样的事情?

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

它不能正常工作,因为A和B不是类型。我应该使用GHC扩展来做到这一点吗?

1个回答

27
在Scala中,您的ADT使得ABC成为Foo的子类型。在Haskell中,我们没有子类型,因此ABC代替成为Foo类型的构造函数。
一些可能的解决方法:
  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).


5
最后一个例子需要进行几个扩展,是吗?(我想是 DataKindsGADTsKindSignatures 扩展。) - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接