在Maybe类型上应用函数?

Som*_*aXD 5 haskell function maybe

Haskell的新手,我无法弄清楚如何将函数(a - > b)应用到列表中[也许是]并得到[也许b]

maybeX:: (a -> b) -> [Maybe a] -> [Maybe b]
Run Code Online (Sandbox Code Playgroud)

该函数应该与map执行完全相同的操作,将函数f应用于Maybe语句列表,如果它只是它返回我af只是如果它只是一个没什么.像下面的例子一样,我想在以下列表的每个元素上添加+5:

[Just 1,Just 2,Nothing,Just 3]
Run Code Online (Sandbox Code Playgroud)

得到

[Just 6,Just 7,Nothing,Just 8]
Run Code Online (Sandbox Code Playgroud)

真的试图弄清楚这一点,我尝试了很多,但似乎总是我不知道这种Maybe数据类型的工作方式..感谢您的帮助!

Ben*_*son 11

让我们首先定义如何对单个行为进行操作Maybe,然后我们将其扩展到整个列表.

mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f Nothing = Nothing
mapMaybe f (Just x) = Just (f x)
Run Code Online (Sandbox Code Playgroud)

如果Maybe包含一个值,则mapMaybe应用于f它,如果它不包含值,那么我们只返回一个空值Maybe.

但是,我们有一个列表MaybeS,所以我们需要应用mapMaybe到每个人.

mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = [mapMaybe f m | m <- ms]
Run Code Online (Sandbox Code Playgroud)

在这里我使用列表理解来评估mapMaybe f m每个mms.


现在换一种更先进的技术.将函数应用于容器中的每个值的模式由Functor类型类捕获.

class Functor f where
    fmap :: (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

A型fFunctor如果你可以写一个函数,它接受一个函数从ab,而且功能适用于一个f充满as到获得一个f完整的b秒.例如,[]并且Maybe都是Functors:

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]
Run Code Online (Sandbox Code Playgroud)

Maybe的版本fmapmapMaybe我上面写的相同,并且[]实现使用列表推导来应用于列表f中的每个元素.

现在,写mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b],你需要使用列表中的每一项操作[]的版本fmap,然后在各个操作Maybe使用S Maybe的版本fmap.

mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = fmap (fmap f) ms
-- or:
mapMaybes = fmap . fmap
Run Code Online (Sandbox Code Playgroud)

请注意,我们实际上fmap在这里调用两个不同的实现.外一个是fmap :: (Maybe a -> Maybe b) -> [Maybe a] -> [Maybe b],其分派给[]Functor实例.内在的是(a -> b) -> Maybe a -> Maybe b.


还有一个附录 - 虽然这是非常深奥的,所以如果你不理解这里的一切,不要担心.我只想给你一些我觉得很酷的东西.

这种" fmaps"风格(fmap . fmap . fmap ...)是通过多层结构向下钻取的常见技巧.每种fmap都有一种类型(a -> b) -> (f a -> f b),所以当你(.)构建它们时,你正在构建一个更高阶的函数.

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)
Run Code Online (Sandbox Code Playgroud)

所以,如果你有仿函数的函数对象(仿函数...),然后ñ fmap旨意让你在映射级别的元素ñ结构.Conal Elliot将这种风格称为"语义编辑器组合器".

诀窍也适用traverse :: (Traversable t, Applicative f) => (a -> f b) -> (t a -> f (t b)),这是一种"有效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))
Run Code Online (Sandbox Code Playgroud)

(我省略了位前=>,因为我跑出来的水平空间.)所以,如果你有traversables的一笔画(的traversables ......),你可以对在一级元素的effectful计算ñ只是写traverse ñ倍.编写这样的遍历是lens库背后的基本思想.