[英]Haskell: type classes question
I wish to define the following typeclass Mapping
: 我希望定义以下类型类Mapping
:
{-# LANGUAGE MultiParamTypeClasses #-}
class Mapping k v m where
empty :: m v
insert :: k -> v -> m v -> m v
search :: k -> m v -> Maybe v
delete :: k -> m v -> m v
One instance of Mapping
is Data.Map.Map
Mapping
一个实例是Data.Map.Map
{-# LANGUAGE ..., FlexibleInstances #-}
instance Ord k => Mapping k v (Map.Map k) where
empty = Map.empty
search = Map.lookup
insert = Map.insert
delete = Map.delete
And now I want to create a type Trie :: * -> * -> * -> *
such as 现在我想创建一个类型Trie :: * -> * -> * -> *
等
{-# LANGUAGE ..., UndecidableInstances #-}
data Trie m k v = Trie {
trValue :: Maybe v,
trChildren :: m (Trie m k v)
}
instance Mapping k (Trie m k v) m => Mapping [k] v (Trie m k) where
search [] tree = trValue tree
search (x:xs) tree =
search xs =<< search x (trChildren tree)
So far so good, now I also want to define Trie
's insert
and empty
, and that's where I get into problems. 到目前为止一切都很好,现在我也想定义Trie
的insert
和empty
,这就是我遇到问题的地方。
I will discuss empty
because it's simpler and insert
needs it anyhow.. If I try this: 我将讨论empty
因为它更简单, insert
需要它无论如何..如果我试试这个:
instance Mapping k (Trie m k v) m => Mapping [k] v (Trie m k) where
empty = Trie { trValue = Nothing, trChildren = empty }
...
and that makes me get the following error: 这让我得到以下错误:
Could not deduce (Mapping k (Trie m k1 v) (m k1))
from the context (Mapping [k1] v (Trie m k1),
Mapping k1 (Trie m k1 v) (m k1))
arising from a use of `empty' at test.hs:27:49-53
Possible fix:
add (Mapping k (Trie m k1 v) (m k1)) to the context of
the instance declaration
or add an instance declaration for (Mapping k (Trie m k1 v) (m k1))
In the `trChildren' field of a record
In the expression: Trie {trValue = Nothing, trChildren = empty}
In the definition of `empty':
empty = Trie {trValue = Nothing, trChildren = empty}
I've tried and tried to solve it but failed. 我试过并试图解决它但失败了。
Does anyone know how to make it work? 有谁知道如何使它工作? Is it even possible? 它甚至可能吗?
Add a functional dependency : 添加功能依赖 :
{-# LANGUAGE ..., FunctionalDependencies #-}
class Mapping k v m | m -> k where
...
The errors you got before were because the program was ambiguous about which key type to use in certain places, hence the errors about the type variable k1
. 之前得到的错误是因为程序对某些地方使用哪种密钥类型不明确,因此关于类型变量k1
的错误。 The functional dependency allows the key type to be deduced from the map type (by declaring that there is only one possible answer), which deals with this problem. 函数依赖性允许从地图类型推导出密钥类型(通过声明只有一个可能的答案),它处理这个问题。
Code to demonstrate Ganesh's answer: 用于演示Ganesh答案的代码:
{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, StandaloneDeriving, UndecidableInstances #-}
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
class Mapping k m | m -> k where
empty :: m v
insert :: k -> v -> m v -> m v
search :: k -> m v -> Maybe v
delete :: k -> m v -> m v
instance Ord k => Mapping k (Map.Map k) where
empty = Map.empty
search = Map.lookup
insert = Map.insert
delete = Map.delete
data Trie m v = Trie {
trValue :: Maybe v,
trChildren :: m (Trie m v)
}
deriving instance (Show v, Show (m (Trie m v))) => Show (Trie m v)
trieMod :: Mapping k m => Maybe v -> [k] -> Trie m v -> Trie m v
trieMod val [] trie = trie { trValue = val }
trieMod val (x:xs) trie =
trie { trChildren = insert x newChild children }
where
children = trChildren trie
newChild = trieMod val xs prevChild
prevChild = fromMaybe empty . search x $ children
instance Mapping k m => Mapping [k] (Trie m) where
empty = Trie { trValue = Nothing, trChildren = empty }
search [] trie = trValue trie
search (x:xs) trie =
search xs =<< search x (trChildren trie)
insert key val = trieMod (Just val) key
delete = trieMod Nothing
type TernarySearchTree a = Trie (Map.Map a)
Btw: Had functional dependencies not existed, we would probably need to compromise on an annoying interface and use function tables instead of type classes. 顺便说一句:如果没有功能依赖,我们可能需要在烦人的界面上妥协并使用函数表而不是类型类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.