如何在Haskell中创建ADT?

And*_*rew 17 haskell scala algebraic-data-types

在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
Run Code Online (Sandbox Code Playgroud)

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

data Foo = A Int | B String | C A B
Run Code Online (Sandbox Code Playgroud)

这是行不通的,因为A和B不是类型。我应该使用GHC扩展名吗?

chi*_*chi 26

在Scala中,你的ADT品牌ABC要的亚型Foo。在Haskell我们没有亚型,因此ABC是代替型的构造Foo

一些可能的解决方法:

  1. 重复这些字段。这是最基本的选择。

    data Foo = A Int | B String | C Int String
    
    Run Code Online (Sandbox Code Playgroud)
  2. 定义其他类型,以便我们可以多次重用它们。

    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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 利用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
    
    Run Code Online (Sandbox Code Playgroud)

    在这里,在最后一行中,我们可以A使用type 引用“使用” 构造的值Foo 'AT,因为tag AT仅由构造器使用A。请注意,这种方法在上添加了一个tag参数Foo,因此它会稍微改变接口:我们不再可以编写bar :: Foo -> ...,而必须编写bar :: Foo t -> ...(或使用存在性类型)。

  • 最后一个示例需要几个扩展,对吗?(我认为是DataKinds,GADTs和KindSignatures。) (4认同)