Haskell - 在另一个中使用一种数据类型

nmd*_*mdr 0 haskell types

Haskell新手; 我希望能够宣布Val它可以是IntVal, StringVal FloatValList它可以是StringList, IntList, FloatList,它的元素是(相应的)StringVal, IntVal and FloatVal.我到目前为止的尝试:

data Val = IntVal Int
         | FloatVal Float
         | StringVal String deriving Show

data List = IntList [(IntVal Int)]
          | FloatList [(FloatVal Float)]
          | StringList [(StringVal String)] deriving Show
Run Code Online (Sandbox Code Playgroud)

失败并出现错误:

    Not in scope: type constructor or class ‘IntVal’
    A data constructor of that name is in scope; did you mean DataKinds?

   data List = IntList [(IntVal Int)]

... (similarly for StringVal, FloatVal..)
Run Code Online (Sandbox Code Playgroud)

什么是实现这一目标的正确方法?

PS:声明List data List = List [Val]最终允许列表如下:l = [(IntVal 10),(StringVal"Hello")],我不想允许.

我希望列表中的每个元素Value都是同一类

luq*_*qui 6

有一种使用GADT的解决方案.问题是IntValetc实际上不是类型,它们只是单一类型的构造函数(基本上也支持模式匹配的函数)Val.因此,一旦你创建了一个Val,关于它的类型的信息在类型级别(即编译时间)完全丢失.

诀窍是标记Val它包含的类型.

data Val a where
    IntVal :: Int -> Val Int
    FloatVal :: Float -> Val Float
    StringVal :: String -> Val String
Run Code Online (Sandbox Code Playgroud)

然后,如果你有一个简单的列表[Val a],它将已经是同质的.如果你必须:

data List = IntList [Val Int]
          | FloatList [Val Float]
          ...
Run Code Online (Sandbox Code Playgroud)

这有点不同之处在于它"擦除"了列表的类型,并且它可以区分一个空的int列表和一个空的浮点列表.您也可以使用与List相同的GADT技巧

data List a where
    IntList :: [Val Int] -> List Int
    FloatList :: [Val Float] -> List Float
    ...
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,我认为更好的设计可能更简单

newtype List a = List [Val a]
Run Code Online (Sandbox Code Playgroud)

所有这些不同设计之间的权衡取决于您计划用它们做什么.