简体   繁体   English

在Maybe类型上应用函数?

[英]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包含一个值, mapMaybef应用于它,如果它不包含值,那么我们只返回一个空的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为每mms


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 ,如果你可以写一个函数,它接受一个函数从ab ,和功能适用于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相同, []的实现使用列表mapMaybef应用于列表中的每个元素。

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是使用Maybefmap版本。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM