我正在尝试做一些在Haskell中显而易见的事情,它从Just [1]
和Just [2]
到Just [1, 2]
。但是我在网上找不到任何东西,因为我一直在寻找相关但无益的页面。那么,你是如何实现这一目标的呢?
答案 0 :(得分:16)
您可以使用liftA2 (++)
:
liftA2 (++) :: Maybe [a] -> Maybe [a] -> Maybe [a]
liftA2
只是将二进制函数提升为Applicative
。 Applicative
被设计用于在上下文中提升任意参数的函数,因此它们是完美的。在这种情况下,我们使用的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
。 (如果您熟悉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
:
Applicative
实际上,我们可以使用这些运算符将任意个参数的函数提升到任何liftA2
,只需遵循(++) <$> a <*> b
的模式。这称为 applicative style ,在惯用的Haskell代码中非常常见。在这种情况下,如果a
和b
已经是变量,那么通过撰写liftA2 (++)
直接使用它可能更为惯用。 (另一方面,如果你部分应用它 - 比如说,将它传递给更高阶函数 - 那么Monad
更可取。)
每个Applicative
都是Applicative
,因此,如果您发现自己试图将某个功能“提升”到某个环境中,{{1}}可能就是您正在寻找的内容。
答案 1 :(得分:3)
虽然@ ehird的答案很棒,但我会在表单中使用一个noobish解决方案:
mergeJust a b = do
a' <- a
b' <- b
return (a' ++ b')
答案 2 :(得分:3)
要将解决方案扩展为Just
列表,您可以使用
fmap join $ sequence [Just[1],Just[2],Just[3]]
-- Just [1,2,3]
答案 3 :(得分:1)
由于其他解决方案中没有提及,我会在这里说。在我看来,完成任务的最简单方法是使用<>
中的mappend
(或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