我想定义一个类m
,它提供了一个functor-ish操作
像这样的类型签名:
mapify ::(a - > b) - > m a - > m b
不过,我还需要一些其他的非仿函数操作。我会 我喜欢写下以下内容: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
工作正常,但是有更好的方法吗?
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 :(得分: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
Container
Map
的{{1}}是Map k
,所以你捆绑了
Map
及其关联的密钥类型。这样,您的mapify
功能就会提升
你的功能进入容器。是否“更好”取决于你,
我猜,但它确实减少了类型类的数量。你不应该
需要MyMapF
类与您的示例,因为MyMapF
与。{1}}相同
标准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
这将进行类型检查。我认为问题只是类型检查器需要
一个更强的暗示,你正在做的就是改变Value
的{{1}}
例如,不更改底层容器。
答案 1 :(得分: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包。