简体   繁体   English

Haskell:类型类问题

[英]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. 到目前为止一切都很好,现在我也想定义Trieinsertempty ,这就是我遇到问题的地方。

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.

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