我发现自己要定义一个类型类实例需要附加的类型约束。具体来说,我想为类型Show
定义Trie a
:
data Trie a = Node {
label :: a,
edges :: DM.Map a (Trie a),
isFinal :: Bool
}
而Show的实例是:
import qualified Data.Tree as DT
instance (Show a, Eq a, Eq (Trie a)) => Show (Trie a) where
show trie@(Node label edges _) = DT.drawTree (mapTree show $ toDataTree trie)
我在这里需要Eq a
和Eq (Trie a)
,因为我使用toDataTree
将Trie a
转换为DT.Tree a
并带来以下类型约束:
import qualified Data.Map as DM
toDataTree :: (Eq a, Eq (Trie a)) => Trie a -> DT.Tree a
toDataTree (Node label edges isFinal)
| edges == DM.empty = DT.Node label (map toDataTree (DM.elems edges))
| otherwise = DT.Node label []
mapTree :: (a -> b) -> DT.Tree a -> DT.Tree b
mapTree f (DT.Node rootLabel subForest) = DT.Node (f rootLabel) $ map (mapTree f) subForest
现在虽然可以编译,但实际上我想在print
上调用Trie a
(在这种情况下,其中a
= Char
)
• No instance for (Eq (Trie Char)) arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
这一定是因为我需要将这些附加的类型约束添加到Show
的实例中。所以我的方法可能是错误的。
类型类实例定义需要其他类型约束的正确解决方案是什么?
答案 0 :(得分:3)
我认为您的toDataTree
防护程序中存在错误,您的意思是:
| edges /= DM.empty = ...
但是,您不需要Eq
实例来检查映射是否为空。如果改用DM.null
,则可以从Eq
和toDataTree
实例中删除Show
约束:
toDataTree :: Trie a -> DT.Tree a
toDataTree (Node label edges isFinal)
| not (DM.null edges) = DT.Node label
(map toDataTree (DM.elems edges))
| otherwise = DT.Node label []
实际上,如果地图为空,则在其元素上进行映射始终会产生一个空列表,因此您可以将其进一步简化为:
toDataTree :: Trie a -> DT.Tree a
toDataTree (Node label edges isFinal)
= DT.Node label (map toDataTree (DM.elems edges))
无论如何,那应该可以解决您眼前的问题。
将所有这些放在一边,导致错误的原因是您没有为任何Eq
提供任何Trie
实例。您可以在deriving (Eq)
的定义中添加Trie
子句:
data Trie a = Node {
label :: a,
edges :: DM.Map a (Trie a),
isFinal :: Bool
} deriving (Eq)
这可能会给您关于内部绑定脆弱的可怕警告,但是您可以从两个Eq (Trie a)
实例中删除Eq a
约束(因为Show
约束将暗示它们)和toDataTree
使警告消失。
还是,如上所述,您实际上并不想这样做,因为使用DM.null
并完全绕过Eq
实例是更好的做法。