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
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都是同一类
有一种使用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)
所有这些不同设计之间的权衡取决于您计划用它们做什么.