[英]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 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
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
,则可以从toDataTree
和Show
实例中删除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.