问题描述
Functional Programming in C++,在第214页上,引用与Haskell的expected<T,E>
相同的Either
单子,
如果您在包含错误的
mbind
上调用>>=
[相当于Haskell的expected
],则mbind
甚至不会调用转换函数。 它只会将该错误转发给结果 。
似乎可以“调整”先前编写的内容。 (我很确定LYAH或RWH都在没有短路的地方强调;如果您记得在哪里,请提醒我。)
从Haskell的理解来看,确实是,在一串单子绑定中,所有绑定都是真实发生的。然后他们如何处理作为第二个参数传递给他们的函数,取决于特定的monad。
对于Maybe
和Either
,当绑定传递了nothing
或Left 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
代表某些a
,f
,g
和h
中的每一个都是函数,{ 1}}返回h
,但管道的中间类型可以是包裹在Maybe Int
中的任何内容。也许Maybe
,f :: String -> Maybe String
,g :: 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>
g
与h
值的行为类似,其他monad可能具有不同的行为。列表可以一次,多次或不多次调用每个函数。元组monad仅对每个函数调用一次,没有短路或其他多重功能。
您似乎对这些类型的Monad实例在Haskell中的工作方式有误解。你说:
事实上,从Haskell的观点来看,在单子函数链中,所有这些函数都被调用,
但是事实并非如此。确实,无论您何时计算
Nothing >>= f
其中f
是a -> Maybe b
类型的任何函数,然后根据>>=
对Maybe
monad的实现来计算它,即:
Just x >>= f = f x
Nothing >>= f = Nothing
因此,f
的确会在Just
的情况下被调用,而在Nothing
的情况下不会被调用。因此,我们看到确实存在“短路”。确实,由于Haskell是惰性的,因此默认情况下每个函数都会“短路”-除非需要产生结果,否则不会进行任何计算。
这是一个有关性能的有趣问题,而不是我个人知道如何回答的问题。当然,正如我刚刚解释的那样,一旦遇到Nothing
时,不会评估该链中的以下任何功能-但执行模式匹配以查看此功能不太可能是免费的。也许编译器可以通过推测一旦达到Nothing
就可以放弃整个计算,从而可以优化这一点。但是我不确定。