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
答案 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]
所有这些不同设计之间的权衡实际上取决于您打算如何处理它们。