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

时间:2018-08-14 00:26:09

标签: haskell types

Haskell新手;我希望能够声明Val可以是IntVal, StringVal FloatVal,也可以声明List可以是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

失败,并显示错误:

    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..)

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

PS: 将列表声明为data List = List [Val]最终允许使用列表,如下所示: l = [(IntVal 10),(StringVal“ Hello”)],我不想允许。

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

1 个答案:

答案 0 :(得分:6)

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

诀窍是使用其包含的类型标记Val

data Val a where
    IntVal :: Int -> Val Int
    FloatVal :: Float -> Val Float
    StringVal :: String -> Val String

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

data List = IntList [Val Int]
          | FloatList [Val Float]
          ...

稍有不同,例如,它“擦除”列表的类型,并且可以区分空白的int列表和空白的floats列表。您还可以在List上使用相同的GADT技巧

data List a where
    IntList :: [Val Int] -> List Int
    FloatList :: [Val Float] -> List Float
    ...

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

newtype List a = List [Val a]

所有这些不同设计之间的权衡实际上取决于您打算如何处理它们。