【问题标题】:How to handle a Typeclass Instance requiring additional Type Constraints如何处理需要额外类型约束的类型类实例
【发布时间】:2018-09-06 06:15:39
【问题描述】:

我发现自己处于我想要定义的类型类实例需要额外类型约束的情况。具体来说,我想为Trie a 类型定义Show

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

虽然这确实可以编译,但当我真正想在 Trie a 上调用 print 时(在这种情况下,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 的实例中。所以我的方法可能是错误的。

对于类型类实例定义所需的附加类型约束,正确的解决方案是什么?

【问题讨论】:

    标签: haskell constraints typeclass


    【解决方案1】:

    我假设您的程序中的 toDataTree 守卫存在错误,您的意思是:

    | edges /= DM.empty = ...
    

    但是,您不需要 Eq 实例来检查地图是否为空。如果您改用DM.null,则可以从toDataTreeShow 实例中删除Eq 约束:

    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 []
    

    事实上,如果 map 是空的,那么映射其元素无论如何都会产生一个空列表,所以你可以进一步简化为:

    toDataTree :: Trie a -> DT.Tree a
    toDataTree (Node label edges isFinal)
      = DT.Node label (map toDataTree (DM.elems edges))
    

    无论如何,这应该可以解决您当前的问题。

    抛开所有这些,错误的原因是您没有为任何Trie 提供任何Eq 实例。您可以在Trie 的定义中添加deriving (Eq) 子句:

    data Trie a = Node {
        label :: a,
        edges :: DM.Map a (Trie a),
        isFinal :: Bool
    } deriving (Eq)
    

    这可能会给你一个关于脆弱内部绑定的可怕警告,但你可以从Show 实例和toDataTree 中删除Eq (Trie a) 约束(因为它们将被Eq a 约束暗示)警告消失。

    如前所述,您仍然不想这样做,因为使用 DM.null 并完全绕过 Eq 实例是更好的做法。

    【讨论】:

    • 谢谢。只是复制和粘贴错误通过太多版本工作。现在已经修好了。
    猜你喜欢
    • 2014-10-02
    • 1970-01-01
    • 1970-01-01
    • 2018-06-15
    • 2015-09-06
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    • 2017-01-27
    相关资源
    最近更新 更多