假设我有一个代表某种树结构的adt:
data Tree = ANode (Maybe Tree) (Maybe Tree) AValType
| BNode (Maybe Tree) (Maybe Tree) BValType
| CNode (Maybe Tree) (Maybe Tree) CValType
据我所知,没有办法对类型构造函数进行模式匹配(或者匹配函数本身不会有类型?)但是我仍然希望使用编译时类型系统来消除返回或解析Tree节点的错误“类型”。例如,可能是CNode只能是ANodes的父母。我可能有
parseANode :: Parser (Maybe Tree)
作为Parsec解析函数,它被用作我的CNode解析器的一部分:
parseCNode :: Parser (Maybe Tree)
parseCNode = try (
string "<CNode>" >>
parseANode >>= \maybeanodel ->
parseANode >>= \maybeanoder ->
parseCValType >>= \cval ->
string "</CNode>"
return (Just (CNode maybeanodel maybeanoder cval))
) <|> return Nothing
根据类型系统,parseANode可能最终返回Maybe CNode,Maybe BNode或Maybe ANode,但我真的想确保它只返回一个Maybe ANode。请注意,这不是我想要做的数据或运行时检查的模式值 - 我实际上只是想检查我为特定树模式编写的解析器的有效性。 IOW,我不是要检查解析数据的模式正确性,我真正想做的是检查我的解析器的模式正确性 - 我只是想确保我不要总有一天,parseANode会返回除了ANode值以外的东西。
我希望如果我匹配绑定变量中的值构造函数,那么类型推理会找出我的意思:
parseCNode :: Parser (Maybe Tree)
parseCNode = try (
string "<CNode>" >>
parseANode >>= \(Maybe (ANode left right avall)) ->
parseANode >>= \(Maybe (ANode left right avalr)) ->
parseCValType >>= \cval ->
string "</CNode>"
return (Just (CNode (Maybe (ANode left right avall)) (Maybe (ANode left right avalr)) cval))
) <|> return Nothing
但是这有很多问题,而parseANode不再可以自由地返回Nothing。并且它无论如何都不起作用 - 看起来绑定变量被视为模式匹配,当parseANode返回Nothing或Maybe BNode之类的时候,运行时会抱怨非详尽的模式匹配。
我可以沿着这些方向做点什么:
data ANode = ANode (Maybe BNode) (Maybe BNode) AValType
data BNode = BNode (Maybe CNode) (Maybe CNode) BValType
data CNode = CNode (Maybe ANode) (Maybe ANode) CValType
但是这种情况很糟糕,因为它假设约束适用于所有节点 - 我可能对这样做不感兴趣 - 事实上它可能只是CNode只能为父节点提供父节点。所以我想我可以做到这一点:
data AnyNode = AnyANode ANode | AnyBNode BNode | AnyCNode CNode
data ANode = ANode (Maybe AnyNode) (Maybe AnyNode) AValType
data BNode = BNode (Maybe AnyNode) (Maybe AnyNode) BValType
data CNode = CNode (Maybe ANode) (Maybe ANode) CValType
然后这使得与* Node的模式匹配变得更加困难 - 实际上它是不可能的,因为它们只是完全不同的类型。我可以在任何想要模式匹配的地方制作一个类型类我猜
class Node t where
matchingFunc :: t -> Bool
instance Node ANode where
matchingFunc (ANode left right val) = testA val
instance Node BNode where
matchingFunc (BNode left right val) = val == refBVal
instance Node CNode where
matchingFunc (CNode left right val) = doSomethingWithACValAndReturnABool val
无论如何,这似乎有些混乱。谁能想到更简洁的做法呢?
答案 0 :(得分:4)
我仍然希望使用编译时类型系统来消除返回或解析错误的“类型”树节点的可能性
这听起来像是GADT的用例。
{-# LANGUAGE GADTs, EmptyDataDecls #-}
data ATag
data BTag
data CTag
data Tree t where
ANode :: Maybe (Tree t) -> Maybe (Tree t) -> AValType -> Tree ATag
BNode :: Maybe (Tree t) -> Maybe (Tree t) -> BValType -> Tree BTag
CNode :: Maybe (Tree t) -> Maybe (Tree t) -> CValType -> Tree CTag
现在,您可以在不关心节点类型时使用Tree t
,或者在使用Tree ATag
时使用{{1}}。
答案 1 :(得分:4)
我不理解您对最终解决方案的反对意见。你仍然可以对AnyNode
进行模式匹配,如下所示:
f (AnyANode (ANode x y z)) = ...
它有点冗长,但我认为它具有您想要的工程属性。
答案 2 :(得分:1)
keegan答案的扩展:编码红/黑树的正确属性是一种规范的例子。该线程的代码显示了GADT和嵌套数据类型解决方案:http://www.reddit.com/r/programming/comments/w1oz/how_are_gadts_useful_in_practical_programming/cw3i9