如何处理需要附加类型约束的Typeclass实例

时间:2018-09-04 02:09:03

标签: haskell constraints typeclass

我发现自己要定义一个类型类实例需要附加的类型约束。具体来说,我想为类型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 aEq (Trie a),因为我使用toDataTreeTrie 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的实例中。所以我的方法可能是错误的。

类型类实例定义需要其他类型约束的正确解决方案是什么?

1 个答案:

答案 0 :(得分:3)

我认为您的toDataTree防护程序中存在错误,您的意思是:

| edges /= DM.empty = ...

但是,您不需要Eq实例来检查映射是否为空。如果改用DM.null,则可以从EqtoDataTree实例中删除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实例是更好的做法。