Haskell 避免在 return 可能的函数中进行双重包装
Haskell avoiding double-wrapped maybes in functions that return maybes
我看到这里有很多关于 Maybe 类型和组合的问题,但我很困惑,老实说,读这些让我头疼。
这是我的情况:
例如,我有一个函数:
addm a b = Just (a + b)
如何使用 addm
函数创建函数 add :: Maybe Int -> Maybe Int -> Maybe Int
,而不使用模式匹配来展开 Maybe
s?
我试过
add x y = (addm <$> x) <*> y
但这有一个类型 Maybe Int -> Maybe Int -> Maybe (Maybe Int)
我还想尽可能避免使用标准库之外的任何东西。
编辑:在我的具体情况下,我实际上只需要一个函数 Maybe Int -> Int -> Maybe Int
所以我可以使用
add x y = x >>= addm y
成功了。不过,我仍然对原始问题的答案感到好奇。
在上面贴一个 join
。
Control.Monad.join :: Monad m => m (m a) -> m a -- 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
(真的,任何总是 returns Just
的函数)是不自然的。您实际上只写 add x y = (+) <$> x <*> y
或 add = liftA2 (+)
,但是当您处理真正有趣的单子代码时,将 join
放在应用样式表达式之上的一般模式很有用.
有多种写法。所有这些都涉及 Maybe
是 Monad 的事实。
也许最容易理解的方法是使用 join 函数,对于任何 Monad,该函数都会删除最外层的嵌套。这里它有类型 Maybe (Maybe a) -> Maybe a
,这正是您正在寻找的,结合标准的应用运算符:
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
使用 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, although there's no results in base
, there are a few results in third-party libraries. liftJoin2
and 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
以仅直接使用 base
方法,而不是 utility-ht
重命名的包装器。)
我看到这里有很多关于 Maybe 类型和组合的问题,但我很困惑,老实说,读这些让我头疼。
这是我的情况:
例如,我有一个函数:
addm a b = Just (a + b)
如何使用 addm
函数创建函数 add :: Maybe Int -> Maybe Int -> Maybe Int
,而不使用模式匹配来展开 Maybe
s?
我试过
add x y = (addm <$> x) <*> y
但这有一个类型 Maybe Int -> Maybe Int -> Maybe (Maybe Int)
我还想尽可能避免使用标准库之外的任何东西。
编辑:在我的具体情况下,我实际上只需要一个函数 Maybe Int -> Int -> Maybe Int
所以我可以使用
add x y = x >>= addm y
成功了。不过,我仍然对原始问题的答案感到好奇。
在上面贴一个 join
。
Control.Monad.join :: Monad m => m (m a) -> m a -- 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
(真的,任何总是 returns Just
的函数)是不自然的。您实际上只写 add x y = (+) <$> x <*> y
或 add = liftA2 (+)
,但是当您处理真正有趣的单子代码时,将 join
放在应用样式表达式之上的一般模式很有用.
有多种写法。所有这些都涉及 Maybe
是 Monad 的事实。
也许最容易理解的方法是使用 join 函数,对于任何 Monad,该函数都会删除最外层的嵌套。这里它有类型 Maybe (Maybe a) -> Maybe a
,这正是您正在寻找的,结合标准的应用运算符:
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
使用 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, although there's no results in base
, there are a few results in third-party libraries. liftJoin2
and 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
以仅直接使用 base
方法,而不是 utility-ht
重命名的包装器。)