[注意:这假设您熟悉函子。]
另一种方法是使用Data.Functor.Compose 中定义的类型级组合。
>>> getCompose (fmap (+5) (Compose [Just 1, Just 2, Nothing, Just 3]))
[Just 6,Just 7,Nothing,Just 8]
您可以将其抽象为您的maybeX 的定义:
-- Wrap, map, and unwrap
maybeX :: (a -> b) -> [Maybe a] -> [Maybe b]
maybeX f = getCompose . fmap f . Compose
(事实上,定义中没有假设Maybe或[],只是限制类型注释。如果启用FlexibleContexts扩展,则可以推断类型(Functor g, Functor f) => (a1 -> a) -> f (g a1) -> f (g a)并将其用于任意嵌套仿函数.
>>> maybeX (+1) (Just [1,2])
Just [2,3]
>>> maybeX (+1) [[1,2]]
[[2,3]]
>>> maybeX (+1) Nothing -- type (Num a, Functor g) => Maybe (g a), since `Nothing :: Maybe a`
)
Compose 构造函数将[] 和Maybe 类型构造函数组合成一个新的类型构造函数:
>>> :k Compose
Compose :: (k1 -> *) -> (k -> k1) -> k -> *
比较:
Compose :: (k1 -> *) -> (k -> k1) -> k -> *
(.) :: (b -> c) -> (a -> b) -> a -> c
(主要区别在于(.) 可以组合任何两个函数;似乎Compose 及其关联实例需要将最后一个更高种类的值应用于具体类型,一种*。 )
将数据构造函数 Compose 应用于可能的列表会产生一个包装值
>>> :t Compose [Nothing]
Compose [Nothing] :: Compose [] Maybe a
只要Compose 的参数本身是Functor 的实例(就像[] 和Maybe 一样),Compose f g 也是一个仿函数,所以你可以使用fmap:
>>> fmap (+5) (Compose [Just 1,Nothing,Just 2,Just 3])
Compose [Just 6,Nothing,Just 7,Just 8]
Compose 值只是原始值的包装:
>>> getCompose $ fmap (+5) (Compose [Just 1,Nothing,Just 2,Just 3])
[Just 6,Nothing,Just 7,Just 8]
最后,这与直接编写 fmap 并没有太大区别:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose x) = Compose (fmap (fmap f) x)
不同之处在于您可以使用函数定义 type 并免费获取其随附的 Functor 实例,而不必手动编写两者。