[英]How to handle a Typeclass Instance requiring additional Type Constraints
我發現自己處於一種情況,我想要定義的類型類實例需要額外的類型約束。 具體來說,我想為一個類型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 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
現在,雖然這確實編譯,當我真的想在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
的實例中。 所以我的方法可能是錯的。
類型類實例定義所需的其他類型約束的正確解決方案是什么?
我假設你的程序中的toDataTree
守衛有一個錯誤,你的意思是:
| edges /= DM.empty = ...
但是,您不需要Eq
實例來檢查映射是否為空。 如果使用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 []
實際上,如果地圖為空,那么無論如何映射其元素都會產生一個空列表,因此您可以進一步簡化為:
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
實例是更好的做法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.