我正在尝试做在 Haskell 中必须非常明显的事情,即 go fromJust [1]和Just [2]to Just [1, 2]。但是我在网上找不到任何东西,因为我一直在寻找相关但无用的页面。那么,您如何实现这一目标?
4 回答
您可以使用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 b。fmap(如果您熟悉 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 <*> x,Just 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在这种情况下,通过编写, ifa和b已经是变量来直接使用它甚至可能更惯用。(另一方面,如果你部分应用它——比如说,将它传递给一个高阶函数——那么liftA2 (++)最好。)
Every Monadis an Applicative,所以如果你发现自己试图将一个函数“提升”到一个上下文中,Applicative可能就是你要找的。
虽然@ehird 的回答很棒,但我会使用以下形式的 noobish 解决方案:
mergeJust a b = do
a' <- a
b' <- b
return (a' ++ b')
要将解决方案扩展到Justs 列表,您可以使用
fmap join $ sequence [Just[1],Just[2],Just[3]]
-- Just [1,2,3]
由于其他解决方案中没有提到,我就在这里说一下。在我看来,完成任务的最简单方法是使用<>(或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