也许与单子,短路和性能有关

问题描述

Functional Programming in C++,在第214页上,引用与Haskell的expected<T,E>相同的Either单子,

[...]一旦绑定到任何函数,就会返回错误 执行将停止 ,并将该错误返回给调用者。

然后在下面的标题显示

如果您在包含错误mbind调用>>= [相当于Haskell的expected],则mbind甚至不会调用转换函数 它只会将该错误转发给结果

似乎可以“调整”先前编写的内容。 (我很确定LYAHRWH都在没有短路的地方强调;如果您记得在哪里,请提醒我。)

从Haskell的理解来看,确实是,在一串单子绑定中,所有绑定都是真实发生的。然后他们如何处理作为第二个参数传递给他们的函数,取决于特定的monad。

对于MaybeEither,当绑定传递了nothingLeft x参数时,第二个参数将被忽略。

在这两种情况下,我仍然想知道这样做是否会降低性能

justPlus1 = Just . (+1)
turnTonothing = const nothing
Just 3 >>= turnTonothing >>= justPlus1 >>= justPlus1 >>= justPlus1 >>= justPlus1 >>= justPlus1

在这种情况下,鉴于这种情况,链条只能做其他事情

nothing >>= _ = nothing
Left l >>= _ = Left l

解决方法

考虑以下表达式:

result :: Maybe Int
result = x >>= f >>= g >>= h

在该表达式中,当然x :: Maybe a代表某些afgh中的每一个都是函数,{ 1}}返回h,但管道的中间类型可以是包裹在Maybe Int中的任何内容。也许Maybef :: String -> Maybe Stringg :: String -> Maybe Char

让我们也明确关联性:

h :: Char -> Maybe Int

要计算表达式,实际上必须调用每个 bind result :: Maybe Int result = ((x >>= f) >>= g) >>= h ),但不一定必须调用函数>>=f或{{ 1}}。最终,与g的绑定需要检查其左手参数,以确定它是h还是h;为了确定我们需要调用到Nothing的绑定,并确定我们需要调用到Just something的绑定,它必须至少查看g。但是一旦这些绑定中的任何一个产生了f,我们只需为在每一步上检查x(非常便宜)而支付,而不是为调用(可能是昂贵的)下游函数付费。

假设Nothing。然后,绑定到Nothing的对象将进行检查,看到x = Nothing,并且根本不会打扰f。但是我们仍然需要绑定该结果,以了解它是否为Nothing。这一直沿链式进行,直到最后我们得到f,已经调用了Nothing三次,但没有使用任何函数result = Nothing>>=f。 / p>

gh值的行为类似,其他monad可能具有不同的行为。列表可以一次,多次或不多次调用每个函数。元组monad仅对每个函数调用一次,没有短路或其他多重功能。

,

您似乎对这些类型的Monad实例在Haskell中的工作方式有误解。你说:

事实上,从Haskell的观点来看,在单子函数链中,所有这些函数都被调用,

但是事实并非如此。确实,无论您何时计算

Nothing >>= f

其中fa -> Maybe b类型的任何函数,然后根据>>=Maybe monad的实现来计算它,即:

Just x >>= f = f x
Nothing >>= f = Nothing

因此,f的确会在Just的情况下被调用,而在Nothing的情况下不会被调用。因此,我们看到确实存在“短路”。确实,由于Haskell是惰性的,因此默认情况下每个函数都会“短路”-除非需要产生结果,否则不会进行任何计算。

这是一个有关性能的有趣问题,而不是我个人知道如何回答的问题。当然,正如我刚刚解释的那样,一旦遇到Nothing时,不会评估该链中的以下任何功能-但执行模式匹配以查看此功能不太可能是免费的。也许编译器可以通过推测一旦达到Nothing就可以放弃整个计算,从而可以优化这一点。但是我不确定。