简体   繁体   English

如何处理需要额外类型约束的类型类实例

[英]How to handle a Typeclass Instance requiring additional Type Constraints

I found myself in a situation where a typeclass instance I want to define requires additional type constraints. 我发现自己处于一种情况,我想要定义的类型类实例需要额外的类型约束。 Specifically I want to define Show for a type Trie a : 具体来说,我想为一个类型Trie a定义Show

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

while the instance for Show is: 而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)

I required Eq a and Eq (Trie a) here, as I use toDataTree which converts a Trie a to a DT.Tree a and entails these type constraints: 我在这里需要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

Now while this does compile, when I actually want to call print on a Trie a (where a = Char in this case) I get 现在,虽然这确实编译,当我真的想在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

This must be because I needed to add these additional type constraints to the instance of Show . 这一定是因为我需要将这些额外的类型约束添加到Show的实例中。 So my approach is probably wrong. 所以我的方法可能是错的。

What is the correct solution for additional type constraints being required for a type class instance definition? 类型类实例定义所需的其他类型约束的正确解决方案是什么?

I assume there's a bug in your program in the toDataTree guard and you meant: 我假设你的程序中的toDataTree守卫有一个错误,你的意思是:

| edges /= DM.empty = ...

However, you don't need Eq instances to check that a map is empty. 但是,您不需要Eq实例来检查映射是否为空。 If you use DM.null instead, you can drop the Eq constraints from toDataTree and from the Show instance: 如果使用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 []

In fact, if the map is empty, then mapping over its elements will produce an empty list anyway, so you can further simplify this to: 实际上,如果地图为空,那么无论如何映射其元素都会产生一个空列表,因此您可以进一步简化为:

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

Anyway, that should solve your immediate problem. 无论如何,这应该可以解决您当前的问题。

Setting all that aside, the reason for the error is that you haven't provided any Eq instance for any Trie . 将所有这些设置在一边,错误的原因是您没有为任何Trie提供任何Eq实例。 You could add a deriving (Eq) clause to the definition for Trie : 您可以在Trie的定义中添加一个deriving (Eq)子句:

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

This might give you a scary warning about fragile inner bindings, but you can drop the Eq (Trie a) constraints (because they will be implied by the Eq a constraint) from both the Show instance and toDataTree to make the warning go away. 这可能会给你一个关于脆弱的内部绑定的可怕警告,但你可以从Show实例和toDataTree删除Eq (Trie a)约束(因为它们将由Eq a约束暗示)以使警告消失。

Still, as mentioned, you don't really want to do this, because using DM.null and bypassing the Eq instances entirely is better practice. 尽管如此,如上所述,你真的不想这样做,因为使用DM.null并完全绕过Eq实例是更好的做法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM