[我可以看到这里有很多关于Maybe类型和成分的问题,但是我很困惑,如果说实话,阅读这些内容会让我头疼。
这是我的处境:
为例,我有一个功能:
addm a b = Just (a + b)
如何在不使用模式匹配来展开add :: Maybe Int -> Maybe Int -> Maybe Int
的情况下使用addm
函数创建函数Maybe
?
我尝试过类似的东西
add x y = (addm <$> x) <*> y
但是它的类型为Maybe Int -> Maybe Int -> Maybe (Maybe Int)
如果可能,我也想避免使用标准库之外的任何东西。
编辑:在我的特定情况下,我实际上只需要一个函数Maybe Int -> Int -> Maybe Int
所以我可以使用
add x y = x >>= addm y
做到了。不过,我对最初问题的答案感到好奇。
有多种写法。所有这些都涉及到Maybe
是Monad的事实。
也许最容易理解的方法是使用join函数,对于任何Monad,该函数都删除最外层的嵌套。它的类型为Maybe (Maybe a) -> Maybe a
,与标准的Applicative运算符结合使用,正是您要寻找的类型:
add ma mb = join $ addm <$> ma <*> mb
或者您可以使用do
表示法以更命令式的形式编写计算,看起来像变量赋值,其中Monad负责传播任何Nothing
值:
add ma mb = do
a <- ma
b <- mb
addm a b
或者您可以显式使用“ bind”(>>=
)运算符,这是除终止符上方的do
块所要执行的操作(但我发现它比其他两个选项不太明确和易于理解:]
add ma mb = ma >>= \a -> mb >>= \b -> addm a b
在顶部贴上join
。
Control.Monad.join :: Monad m => m (m a) -> m -- combine two ms into one, this is why, as the meme goes, monads are monoids in the category of endofunctors
add x y = join $ addm <$> x <*> y
我想指出的是,这样编写addm
(实际上,总是返回Just
的任何函数)都是不自然的。您实际上只写了add x y = (+) <$> x <*> y
或add = liftA2 (+)
,但是当您处理实际有趣的单子代码时,在应用风格的表达式顶部加上join
的一般模式非常有用。
使用force类型,卢克!
您的addm
具有类型Int -> Int -> Maybe Int
。您的目标是以一种可以给您Maybe Int -> Maybe Int -> Maybe Int
的方式包装它。为此,我们需要一个类型为(Int -> Int -> Maybe Int) -> Maybe Int -> Maybe Int -> Maybe Int
的函数。如果我们是search for that type on Hoogle,尽管base
中没有结果,但是第三方库中有一些结果。 liftJoin2
和liftJoin2
是等效的,两者都可以完全满足您的要求。如果您不希望为此引入新的依赖项,请检查其来源以查看其工作方式:
bind2
bind2
(我在这里对bind2 :: Monad m => (a -> b -> m c) -> m a -> m b -> m c
bind2 f x y = liftA2 (,) x y >>= uncurry f
进行了一些修改,仅直接使用liftJoin2 :: (Monad m) => (a -> b -> m c) -> m a -> m b -> m c
liftJoin2 f ma mb =
join (liftM2 f ma mb)
方法,而不是liftJoin2
的重命名包装器。)