[英]apply function on Maybe types?
New to Haskell and I can't figure out how apply a function (a -> b) into a list [Maybe a] and get [Maybe b] Haskell的新手,我无法弄清楚如何将函数(a - > b)应用到列表中[也许是]并得到[也许b]
maybeX:: (a -> b) -> [Maybe a] -> [Maybe b]
The function is supposed to do the exact same thing as map, apply the function f on a list of Maybe statements and if it Just it returns me af Just and if it's a Nothing just a Nothing. 该函数应该与map执行完全相同的操作,将函数f应用于Maybe语句列表,如果它只是它返回我af只是如果它只是一个没什么。 Like the following example I want to add +5 on every Element of the following List : 像下面的例子一样,我想在以下列表的每个元素上添加+5:
[Just 1,Just 2,Nothing,Just 3]
and get 得到
[Just 6,Just 7,Nothing,Just 8]
Really trying to figure this and I tried a lot but it always seems like it is something that I'm not aware of in the way of which this Maybe datatype works.. Thanks for your help! 真的试图弄清楚这一点,我尝试了很多,但似乎总是我不知道这种Maybe数据类型的工作方式..感谢您的帮助!
Let's start by defining how to act on a single Maybe
, and then we'll scale it up to a whole list. 让我们首先定义如何对单个Maybe
,然后我们将其扩展到整个列表。
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f Nothing = Nothing
mapMaybe f (Just x) = Just (f x)
If the Maybe
contains a value, mapMaybe
applies f
to it, and if it doesn't contain a value then we just return an empty Maybe
. 如果Maybe
包含一个值, mapMaybe
将f
应用于它,如果它不包含值,那么我们只返回一个空的Maybe
。
But we have a list of Maybe
s, so we need to apply mapMaybe
to each of them. 但是我们有一个Maybe
s 列表 ,所以我们需要将mapMaybe
应用于每个。
mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = [mapMaybe f m | m <- ms]
Here I'm using a list comprehension to evaluate mapMaybe fm
for each m
in ms
. 在这里我使用列表理解来评估mapMaybe fm
为每m
在ms
。
Now for a more advanced technique. 现在换一种更先进的技术。 The pattern of applying a function to every value in a container is captured by the Functor
type class. 将函数应用于容器中的每个值的模式由Functor
类型类捕获。
class Functor f where
fmap :: (a -> b) -> f a -> f b
A type f
is a Functor
if you can write a function which takes a function from a
to b
, and applies that function to an f
full of a
s to get an f
full of b
s. A型f
是一个Functor
,如果你可以写一个函数,它接受一个函数从a
到b
,和功能适用于f
充满了a
s到得到f
充满b
秒。 For example, []
and Maybe
are both Functor
s: 例如, []
和Maybe
都是Functor
s:
instance Functor Maybe where
fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)
instance Functor [] where
fmap f xs = [f x | x <- xs]
Maybe
's version of fmap
is the same as the mapMaybe
I wrote above, and []
's implementation uses a list comprehension to apply f
to every element in the list. Maybe
fmap
的版本与我上面写的mapMaybe
相同, []
的实现使用列表mapMaybe
将f
应用于列表中的每个元素。
Now, to write mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
, you need to operate on each item in the list using []
's version of fmap
, and then operate on the individual Maybe
s using Maybe
's version of fmap
. 现在,要编写mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
,你需要使用[]
的fmap
版本操作列表中的每个项目,然后对个人Maybe
是使用Maybe
的fmap
版本。
mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = fmap (fmap f) ms
-- or:
mapMaybes = fmap . fmap
Note that we're actually calling two different fmap
implementations here. 请注意,我们实际上在这里调用两个不同的fmap
实现。 The outer one is fmap :: (Maybe a -> Maybe b) -> [Maybe a] -> [Maybe b]
, which dispatches to []
's Functor
instance. 外部的一个是fmap :: (Maybe a -> Maybe b) -> [Maybe a] -> [Maybe b]
,它会调度到[]
的Functor
实例。 The inner one is (a -> b) -> Maybe a -> Maybe b
. 内在的是(a -> b) -> Maybe a -> Maybe b
。
One more addendum - though this is quite esoteric, so don't worry if you don't understand everything here. 还有一个附录 - 虽然这是非常深奥的,所以如果你不理解这里的一切,不要担心。 I just want to give you a taste of something I think is pretty cool. 我只想给你一些我觉得很酷的东西。
This "chain of fmap
s" style ( fmap . fmap . fmap ...
) is quite a common trick for drilling down through multiple layers of a structure. 这种“ fmap
链”样式( fmap . fmap . fmap ...
)是向下钻取结构的多个层的常见技巧。 Each fmap
has a type of (a -> b) -> (fa -> fb)
, so when you compose them with (.)
you're building a higher-order function. 每个fmap
都有一个(a -> b) -> (fa -> fb)
,所以当你用(.)
组成它们时,你就构建了一个高阶函数。
fmap :: Functor g => (f a -> f b) -> (g (f a) -> g (f b))
fmap :: Functor f => (a -> b) -> (f a -> f b)
-- so...
fmap . fmap :: (Functor f, Functor g) => (a -> b) -> g (f a) -> g (f b)
So if you have a functor of functors (of functors...), then n fmap
s will let you map the elements at level n of the structure. 因此,如果你有一个fmap
函数( fmap
函数......)的fmap
函数,那么n fmap
将允许你映射结构的n级元素。 Conal Elliot calls this style "semantic editor combinators" . Conal Elliot将这种风格称为“语义编辑器组合器” 。
The trick also works with traverse :: (Traversable t, Applicative f) => (a -> fb) -> (ta -> f (tb))
, which is a kind of "effectful fmap
". 这个技巧也适用于traverse :: (Traversable t, Applicative f) => (a -> fb) -> (ta -> f (tb))
,这是一种“有效的fmap
”。
traverse :: (...) => (t a -> f (t b)) -> (s (t a) -> f (s (t b)))
traverse :: (...) => (a -> f b) -> (t a -> f (t b))
-- so...
traverse . traverse :: (...) => (a -> f b) -> s (t a) -> f (s (t b))
(I omitted the bits before the =>
because I ran out of horizontal space.) So if you have a traversable of traversables (of traversables...), you can perform an effectful computation on the elements at level n just by writing traverse
n times. (我省略了=>
之前的位,因为我用完了水平空间。)因此,如果你有一个可遍历的遍历(可遍历...),你可以通过编写traverse
n级元素执行有效的计算n次。 Composing traversals like this is the basic idea behind the lens
library. 编写这样的遍历是lens
库背后的基本思想。
[Note: this assumes familiarity with functors.] [注意:这假设熟悉仿函数。]
Another approach is to use type-level composition as defined in Data.Functor.Compose
. 另一种方法是使用Data.Functor.Compose
定义的类型级组合。
>>> getCompose (fmap (+5) (Compose [Just 1, Just 2, Nothing, Just 3]))
[Just 6,Just 7,Nothing,Just 8]
You can abstract this into a defintion for your maybeX
: 你可以将它抽象为你的maybeX
的定义:
-- Wrap, map, and unwrap
maybeX :: (a -> b) -> [Maybe a] -> [Maybe b]
maybeX f = getCompose . fmap f . Compose
(In fact, nothing in the definition assumes Maybe
or []
, just the restricted type annotation. If you enable the FlexibleContexts
extension, you can infer the type (Functor g, Functor f) => (a1 -> a) -> f (g a1) -> f (ga)
and use it on arbitrary nested functors. (实际上,定义中没有任何内容假定是Maybe
或[]
,只是限制类型注释。如果启用FlexibleContexts
扩展,则可以推断出类型(Functor g, Functor f) => (a1 -> a) -> f (g a1) -> f (ga)
并在任意嵌套仿函数上使用它。
>>> 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`
) )
The Compose
constructor composes the []
and Maybe
type constructors into a new type constructor: Compose
构造函数将[]
和Maybe
类型构造函数组合成一个新类型构造函数:
>>> :k Compose
Compose :: (k1 -> *) -> (k -> k1) -> k -> *
Compare: 相比:
Compose :: (k1 -> *) -> (k -> k1) -> k -> *
(.) :: (b -> c) -> (a -> b) -> a -> c
(The main difference is that (.)
can compose any two functions; it appears that Compose
and its associated instances require the last higher-kinded value to be applied to a concrete type, one of kind *
.) (主要区别在于(.)
可以组成任何两个函数;看起来Compose
及其相关实例需要将最后一个较高的值应用于具体类型,类型*
。)
Applying the data constructor Compose
to a list-of-Maybes produces a wrapped value 应用数据构造函数Compose
到Maybes列表会生成包装值
>>> :t Compose [Nothing]
Compose [Nothing] :: Compose [] Maybe a
As long as the arguments to Compose
are themselves instances of Functor
(as []
and Maybe
are), Compose fg
is also a functor, so you can use fmap
: 只要Compose
的参数本身就是Functor
实例(as []
和Maybe
are), Compose fg
也是一个fmap
函数,所以你可以使用fmap
:
>>> fmap (+5) (Compose [Just 1,Nothing,Just 2,Just 3])
Compose [Just 6,Nothing,Just 7,Just 8]
The Compose
value is just a wrapper around the original value: Compose
值只是原始值的包装:
>>> getCompose $ fmap (+5) (Compose [Just 1,Nothing,Just 2,Just 3])
[Just 6,Nothing,Just 7,Just 8]
In the end, this isn't much different than simply composing fmap
directly: 最后,这与简单直接编写fmap
不同:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose x) = Compose (fmap (fmap f) x)
The difference is that you can define the type with a function and get its accompanying Functor
instance for free, instead of having to hand-code both. 不同之处在于您可以使用函数定义类型并免费获取其附带的Functor
实例,而不必手动编码。
You're likely already aware of 你可能已经意识到了
map :: (a -> b) -> [a] -> [b]
...which is really just a special case of ......这实际上只是一个特例
fmap :: Functor f => (a -> b) -> f a -> f b
The latter works both on lists (where it behaves exactly like map
) and on Maybe
, because both are functors. 后者在列表(它的行为与map
完全相同)和Maybe
,因为它们都是仿函数。 Ie, both of the following signatures are valid specialisations: 即,以下两个签名都是有效的专业化:
fmap :: (a -> b) -> [a] -> [b]
fmap :: (a -> b) -> Maybe a -> Maybe b
Now, your use case looks similar, but unfortunately [Maybe a]
is not by itself a specialisation of fa
, rather it has the form f (ga)
. 现在,你的用例看起来很相似,但遗憾的是[Maybe a]
本身并不是fa
,而是它的形式为f (ga)
。 But note that we can just substitute the variable α
for ga
, ie for Maybe a
, then we can use 但请注意,我们可以将变量α
替换为ga
,即对于Maybe a
,然后我们可以使用
fmap :: (α -> β) -> [α] -> [β]
ie 即
fmap :: (Maybe a -> Maybe b) -> [Maybe a] -> [Maybe b]
That looks even more like the signature you want! 这看起来更像是你想要的签名! However we still need a function with signature Maybe a -> Maybe b
. 但是我们仍然需要一个带签名的函数, Maybe a -> Maybe b
。 Well, look above... I repeat: 好吧,看看上面......我再说一遍:
fmap :: (a -> b) -> Maybe a -> Maybe b
This can be partially applied , ie when you have a function φ :: a -> b
, you can easily obtain the function fmap φ :: Maybe a -> Maybe b
. 这可以部分应用 ,即当你有一个函数φ :: a -> b
,你可以很容易地得到函数fmap φ :: Maybe a -> Maybe b
。 And that solves your problem: 这解决了你的问题:
maybeX :: (a -> b) -> [Maybe a] -> [Maybe b]
maybeX φ = fmap (fmap φ)
...or, if you want to get extra fancy, ......或者,如果你想得到额外的幻想,
maybeX = fmap . fmap
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.