Haskell:类型类问题
Posted
技术标签:
【中文标题】Haskell:类型类问题【英文标题】:Haskell: type classes question 【发布时间】:2009-06-19 20:33:27 【问题描述】:我希望定义以下类型类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
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
现在我想创建一个类型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)
到目前为止一切顺利,
现在我还想定义Trie
的insert
和empty
,这就是我遇到问题的地方。
我将讨论empty
,因为它更简单,insert
无论如何都需要它。
如果我试试这个:
instance Mapping k (Trie m k v) m => Mapping [k] v (Trie m k) where
empty = Trie trValue = Nothing, trChildren = empty
...
这让我得到以下错误:
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
我已经尝试并试图解决它但失败了。
有人知道如何让它工作吗?有没有可能?
【问题讨论】:
顺便说一句,我建议从类型类定义中删除v
(但将其保留在方法的签名中)。你不需要它,至少对于你迄今为止给出的所有结构来说,因为它们都将采用任何包含的类型,它使一切变得更简单。
【参考方案1】:
添加functional dependency:
-# LANGUAGE ..., FunctionalDependencies #-
class Mapping k v m | m -> k where
...
您之前遇到的错误是因为程序对在某些地方使用哪种键类型不明确,因此出现了关于类型变量k1
的错误。函数依赖允许从映射类型推导出键类型(通过声明只有一个可能的答案),从而解决了这个问题。
【讨论】:
谢谢!我对它进行了编码(见下文),由于您关于从类型类定义中删除 v 的建议,结果非常简洁。【参考方案2】:演示 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)
顺便说一句:如果不存在函数依赖关系,我们可能需要在烦人的接口上妥协,并使用函数表而不是类型类。
【讨论】:
@Titou:关于您的编辑 - 请参阅上面与 @GaneshSittampalam 讨论的关于从类型类定义中删除v
的讨论。 ***.com/questions/1019928/…***.com/questions/1019928/…
感谢您花时间指出错误,因为这是一个错误的信念,如果没有您的信息,我不会质疑。以上是关于Haskell:类型类问题的主要内容,如果未能解决你的问题,请参考以下文章