3

我想定义一个类,该类m提供具有如下类型签名的仿函数操作:

映射 :: (a -> 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.
4

2 回答 2

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

这个想法是Containerfor aMap是,因此您将 the与其关联的键类型Map 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

这将进行类型检查。我认为问题只是类型检查器需要一个更强的提示,即您所做的只是更改实例ValueMyMap ,而不更改底层容器。

于 2013-03-01T16:31:15.380 回答
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

另请参阅密钥包。

于 2014-05-06T01:09:33.517 回答