9

我正在尝试做在 Haskell 中必须非常明显的事情,即 go fromJust [1]Just [2]to Just [1, 2]。但是我在网上找不到任何东西,因为我一直在寻找相关但无用的页面。那么,您如何实现这一目标?

4

4 回答 4

16

您可以使用liftA2 (++)

liftA2 (++) :: Maybe [a] -> Maybe [a] -> Maybe [a]

liftA2只是将二进制函数提升为Applicative. Applicatives 是为在上下文中提升任意参数的函数而设计的,因此它们非常适合。在这种情况下,Applicative我们使用的是Maybe. 要了解这是如何工作的,我们可以查看定义:

liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b

(<$>)只是将纯值上的任何函数提升到一个在内部运行的函数f: (a -> b) -> f a -> f bfmap(如果您熟悉 s,它只是 的别名Functor。)对于Maybe

_ <$> Nothing = Nothing
f <$> Just x = Just (f x)

(<*>)有点棘手:它将内部的函数应用于内部f的值f: f (a -> b) -> f a -> f b。对于Maybe

Just f <*> Just x = Just (f x)
_ <*> _ = Nothing

(实际上,与 ,f <$> x是一样的pure f <*> xJust f <*> x用于Maybe。)

因此,我们可以扩展 的定义liftA2 (++)

liftA2 (++) a b = (++) <$> a <*> b

-- expand (<$>)
liftA2 (++) (Just xs) b = Just (xs ++) <*> b
liftA2 (++) _ _ = Nothing

-- expand (<*>)
liftA2 (++) (Just xs) (Just ys) = Just (xs ++ ys)
liftA2 (++) _ _ = Nothing

事实上,我们可以使用这些运算符将任意数量的参数的函数提升为 any Applicative,只需遵循 的模式liftA2。这称为applicative style,在惯用的 Haskell 代码中很常见。(++) <$> a <*> b在这种情况下,通过编写, ifab已经是变量来直接使用它甚至可能更惯用。(另一方面,如果你部分应用它——比如说,将它传递给一个高阶函数——那么liftA2 (++)最好。)

Every Monadis an Applicative,所以如果你发现自己试图将一个函数“提升”到一个上下文中,Applicative可能就是你要找的。

于 2012-01-24T18:36:33.417 回答
3

虽然@ehird 的回答很棒,但我会使用以下形式的 noobish 解决方案:

mergeJust a b = do
    a' <- a
    b' <- b
    return (a' ++ b')
于 2012-01-24T19:22:54.450 回答
3

要将解决方案扩展到Justs 列表,您可以使用

fmap join $ sequence [Just[1],Just[2],Just[3]]
-- Just [1,2,3]
于 2012-01-24T21:19:10.537 回答
1

由于其他解决方案中没有提到,我就在这里说一下。在我看来,完成任务的最简单方法是使用<>(或mappend) from Data.Monoid

import Data.Monoid

Just [1,2] <> Just [7,8] == Just [1,2,7,8]

但是,请注意,与 ehird 的应用解决方案不同,此解决方案不会短路Nothing值。

Just [1,2] <> Nothing ---> Just [1,2]
--However
(++) <$> Just [1,2] <*> Nothing ---> Nothing
于 2012-08-11T20:17:26.093 回答