2013-03-01 75 views
3

欲定义一个类m提供与 一个算符肥胖型操作这样的类型签名:定义与算符肥胖型和非算符肥胖型函数的类

mapify ::(一 - > b) - > ma - > mb

尽管如此,我还需要一些其他非函子式的操作。我会 都喜欢写沿着线的东西:

class MyMap m where 
    type Key m 
    type Value m 
    keys :: m -> [Key m] 
    elems :: m -> [Value m] 
    mapify :: (a -> b) -> m a -> m b -- WON'T WORK!!! 

我明白为什么这是行不通的。我提出的解决方案是将它分成两个类,一个是“正常”的,另一个是仿Functor。

{-# LANGUAGE TypeFamilies #-} 

import qualified Data.Map.Lazy as M 

class MyMap m where 
    type Key m 
    type Value m 
    keys :: m -> [Key m] 
    elems :: m -> [Value m] 

class MyMapF m where 
    mapify :: (a -> b) -> m a -> m b 

instance MyMap (M.Map k v) where 
    type Key (M.Map k v) = k 
    type Value (M.Map k v) = v 
    keys = M.keys 
    elems = M.elems 

instance MyMapF (M.Map k) where 
    mapify = M.map 

这工作正常,但有没有更好的办法?


编辑:我真的很喜欢sabauma提出的解决方案。但是,当我尝试创建使用此类的函数时,我无法获得类型签名。

doSomething 
    :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) => -- line 22 
    (Value m1 -> Value m2) -> m1 -> m2     -- line 23 
doSomething f m = mapify f m        -- line 24 

我得到的错误是:

../Amy3.hs:22:6: 
    Couldn't match type `b0' with `Value (Container m0 b0)' 
     `b0' is untouchable 
      inside the constraints (MyMap m1, 
            MyMap m2, 
            Container m1 ~ Container m2) 
      bound at the type signature for 
         doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) => 
            (Value m1 -> Value m2) -> m1 -> m2 
    Expected type: a0 -> b0 
     Actual type: Value m1 -> Value m2 

../Amy3.hs:24:19: 
    Could not deduce (m2 ~ Container m0 b0) 
    from the context (MyMap m1, MyMap m2, Container m1 ~ Container m2) 
     bound by the type signature for 
       doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) => 
           (Value m1 -> Value m2) -> m1 -> m2 
     at ../Amy3.hs:(22,6)-(23,38) 
     `m2' is a rigid type variable bound by 
      the type signature for 
      doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) => 
          (Value m1 -> Value m2) -> m1 -> m2 
      at ../Amy3.hs:22:6 
    In the return type of a call of `mapify' 
    In the expression: mapify f m 
    In an equation for `doSomething': doSomething f m = mapify f m 

../Amy3.hs:24:28: 
    Could not deduce (m1 ~ Container m0 a0) 
    from the context (MyMap m1, MyMap m2, Container m1 ~ Container m2) 
     bound by the type signature for 
       doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) => 
           (Value m1 -> Value m2) -> m1 -> m2 
     at ../Amy3.hs:(22,6)-(23,38) 
     `m1' is a rigid type variable bound by 
      the type signature for 
      doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) => 
          (Value m1 -> Value m2) -> m1 -> m2 
      at ../Amy3.hs:22:6 
    In the second argument of `mapify', namely `m' 
    In the expression: mapify f m 
    In an equation for `doSomething': doSomething f m = mapify f m 
Failed, modules loaded: none. 
+0

我在想'class Functor m => MyMap m哪里......'不会有效?这样,您可以简单地重复使用“Functor”部件,而不必担心“m”与“m a”。 – Xeo 2014-05-06 06:29:52

回答

6

一种可能性是编码使用其他相关 类型的“集装箱”式的。

import qualified Data.Map.Lazy as M 

class MyMap m where 
    type Key m 
    type Value m 
    type Container m :: * -> * 
    keys :: m -> [Key m] 
    elems :: m -> [Value m] 
    mapify :: (a -> b) -> Container m a -> Container m b 

instance MyMap (M.Map k v) where 
    type Key (M.Map k v) = k 
    type Value (M.Map k v) = v 
    type Container (M.Map k v) = M.Map k 
    keys = M.keys 
    elems = M.elems 
    mapify = M.map 

的想法是,在ContainerMapMap k,所以你与其相关的密钥类型捆绑 Map。这样,您的mapify功能可将 您的功能提升到容器中。我猜想,不管是不是更好, ,但它减少了类型类的数量。您不应该使用 需要MyMapF类,因为MyMapF与 标准Functor类型类相同。

好的,那个错误可以通过略微修改mapify 的定义来解决。

class MyMap m where 
    type Key m 
    type Value m 
    type Container m :: * -> * 
    keys :: m -> [Key m] 
    elems :: m -> [Value m] 
    -- mapify :: (a -> b) -> Container m a -> Container m b 

    -- Make sure the type-checker knows that m2 is just the container of m with 
    -- a different value 
    mapify :: (MyMap m2, m2 ~ Container m (Value m2)) => (Value m -> Value m2) -> m -> m2 


instance MyMap (M.Map k v) where 
    type Key (M.Map k v) = k 
    type Value (M.Map k v) = v 
    type Container (M.Map k v) = M.Map k 
    keys = M.keys 
    elems = M.elems 
    mapify = M.map 

doSomething 
    :: (MyMap m1, MyMap m2, m2 ~ Container m1 (Value m2)) => 
    (Value m1 -> Value m2) -> m1 -> m2 
doSomething f m = mapify f m 

这将进行类型检查。我认为问题在于类型检查程序需要一个更强的提示,即您正在执行的操作是更改MyMap 实例的Value,而不更改基础容器。

+0

谢谢!这对我来说好得多,因为在我所使用的领域中,所有功能都“归属”在一起。顺便说一句,我不能使用'Functor',因为我需要提供几个“functor-ish”操作,比如'mapValueWithKey'。但是,现在我遇到了类型签名问题(请参阅上面的编辑)。 – mhwombat 2013-03-01 17:07:56

+0

@mhwombat我不得不稍微改变类的定义,但是这个新版本似乎进行了类型检查。也许别人知道一个更好的方式来编码? – sabauma 2013-03-01 17:38:27

2

我意识到这个问题有点旧,但我跑过去寻找一些不相关的东西,并有一个你可能会喜欢的想法,特别是因为它看起来比sabauma提出的更简单一些。

import qualified Data.Map.Lazy as M 

class MyMap m where 
    type Key m 
    keys :: m a -> [Key m] 
    elems :: m a -> [a] 
    mapify :: (a -> b) -> m a -> m b 

instance MyMap (M.Map k) where 
    type Key (M.Map k) = k 
    keys = M.keys 
    elems = M.elems 
    mapify = M.map 

另请参阅keys包。

相关问题