【问题标题】:Haskell avoiding double-wrapped maybes in functions that return maybesHaskell 在返回可能的函数中避免双重包装的可能
【发布时间】:2020-01-01 11:19:31
【问题描述】:

我可以看到这里有很多关于 Maybe 类型和构图的问题,但我很困惑,如果我很诚实的话,读到这些让我头疼。

这是我的情况:

比方说,我有一个函数:

addm a b = Just (a + b)

如何使用addm 函数创建函数add :: Maybe Int -> Maybe Int -> Maybe Int,而不使用模式匹配来解开Maybes?

我尝试过类似的东西

add x y = (addm <$> x) <*> y

但是这个类型是Maybe Int -&gt; Maybe Int -&gt; Maybe (Maybe Int)

如果可能的话,我还想避免使用标准库之外的任何东西。

编辑:在我的具体情况下,我实际上只需要一个函数Maybe Int -&gt; Int -&gt; Maybe Int 所以我能够使用

add x y = x >>= addm y

成功了。不过,我仍然对原始问题的答案感到好奇。

【问题讨论】:

  • 您在寻找的不是应用函子吗?
  • 也许吧?我对 Haskell 还是很陌生,所以我真的不明白这如何适用于这种情况。我认为如果 addm 本身不返回 Maybe 会正常工作。
  • 我已经很长时间没有使用 Haskell 了,但也许这会对你有所帮助:learnyouahaskell.com/…。它不一样,但也许它会引导到正确的轨道
  • @GrayCat 我很确定您需要 Monad 实例来执行此操作,而不仅仅是 Applicative 方法
  • @GrayCat 好资源,谢谢;但它并没有真正向我展示我在查找 &lt;*&gt;&lt;$&gt; 时在 Hoogle 上没有看到的任何内容。我还是很困。

标签: haskell monads functor maybe


【解决方案1】:

有很多方法可以写这个。所有这些都涉及Maybe 是一个 Monad 的事实。

也许最容易理解的方法是使用join 函数,对于任何Monad,它都会删除最外层的嵌套。在这里,它的类型为 Maybe (Maybe a) -&gt; Maybe a,这正是您正在寻找的,与标准 Applicative 运算符相结合:

add ma mb = join $ addm <$> ma <*> mb

或者您可以使用 do 表示法以更命令式的方式编写计算,看起来像变量赋值,其中 Monad 负责传播任何 Nothing 值:

add ma mb = do
    a <- ma
    b <- mb
    addm a b

或者您可以显式使用“绑定”(&gt;&gt;=) 运算符,这就是上面的 do 块的作用(但我发现这比其他两个选项更明确和更容易理解):

add ma mb = ma >>= \a -> mb >>= \b -> addm a b

【讨论】:

  • 很好的答案!这正是我一直在寻找的东西,也感谢您从多个角度接近它!我会保留这个以供参考!
【解决方案2】:

在顶部贴上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(真的,任何总是返回Just 的函数)都是不自然的。你真的只需要写add x y = (+) &lt;$&gt; x &lt;*&gt; yadd = liftA2 (+),但是当你处理真正有趣的一元代码时,将join 放在applicative 样式的表达式之上的一般模式很有用。

【讨论】:

  • 我几乎在 cmets 中指出 addm 的定义根本不需要或不想使用 Maybe,但后来我反映 OP 说“为了示例”,所以我假设真正的功能更复杂,并且有一定的机会产生Nothing
  • addm 函数只是为了举例,我知道编写一个总是返回 Just 的函数是不自然的,但感谢您提供替代方案(以及问题的解决方案!)跨度>
【解决方案3】:

使用 force 类型,Luke!

您的addm 的类型为Int -&gt; Int -&gt; Maybe Int。您的目标是以一种可以给您Maybe Int -&gt; Maybe Int -&gt; Maybe Int 的方式包装它。为此,我们需要一个类型为(Int -&gt; Int -&gt; Maybe Int) -&gt; Maybe Int -&gt; Maybe Int -&gt; Maybe Int 的函数。如果我们search for that type on Hoogle,虽然base没有结果,但是在第三方库里有一些结果。 liftJoin2bind2 是等价的,两者都完全按照您的意愿行事。如果您不想为此引入新的依赖项,请检查它们的来源以了解它们是如何做到的:

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重命名的包装器。)

【讨论】:

    猜你喜欢
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 2020-09-19
    相关资源
    最近更新 更多