我发现自己处于一种情况,我想要定义的类型类实例需要额外的类型约束。具体来说,我想为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
的实例中。所以我的方法可能是错的。
类型类实例定义所需的其他类型约束的正确解决方案是什么?
我认为你的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
实例是更好的做法。