Maybe 和 Either 单子,short-circuiting,以及性能
Maybe and Either monads, short-circuiting, and performance
Functional Programming in C++,在第 214 页,参考与 Haskell 的 Either
相同的 expected<T,E>
单子,读取
[...] as soon as any of the functions you're binding to returns an error, the execution will stop and return that error to the caller.
然后,在下面的标题中,它显示为
If you call mbind
[equivalent to Haskell's >>=
] on an expected
that contains an error,, mbind
won't even invoke the transformation function; it will just forward that error to the result.
这似乎是在“调整”之前写的内容。 (我很确定 LYAH or RWH 在某个地方强调没有 short-circuiting;如果你记得在哪里,请提醒我。)
确实,根据 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
中的每一个都是函数,具有h
返回 Maybe Int
但管道的中间类型可以是任何包裹在 Maybe
中的东西。也许 f :: String -> Maybe String
, g :: String -> Maybe Char
, h :: Char -> Maybe Int
.
让我们也明确关联性:
result :: Maybe Int
result = ((x >>= f) >>= g) >>= h
要计算表达式,实际上必须调用每个 bind (>>=
),但不一定调用函数 f
、g
,或h
。最终绑定到 h
需要检查它的 left-hand 参数来决定它是 Nothing
还是 Just something
;为了确定我们需要调用对 g
的绑定,并决定我们需要调用对 f
的绑定,这必须至少查看 x
。但是一旦这些绑定中的任何一个产生 Nothing
,我们只需为每一步检查 Nothing
付费(非常便宜),而不是为调用(可能昂贵的)下游函数付费。
假设x = Nothing
。然后 f
的绑定会检查它,看到 Nothing
,并且根本不会调用 f
。但是我们仍然需要绑定它的结果,以便知道它是否是 Nothing
。这继续沿着链条向下直到最后我们得到 result = Nothing
,调用了 >>=
三次但是 none 函数 f
、g
或 [=16] =].
Either
与 Left
值的行为相似,其他 monad 可能有不同的行为。列表可以调用每个函数一次、多次或不调用;元组 monad 只调用每个函数一次,没有 short-circuiting 或其他多重性特征。
您似乎对这些类型的 Monad 实例在 Haskell 中的工作方式存在误解。你说:
Indeed, my understanding, from Haskell, is that in a chain of monadic functions, all of the functions are called,
但事实显然并非如此。事实上任何时候你计算
Nothing >>= f
其中 f
是 a -> Maybe b
类型的任何函数,然后根据 Maybe
monad 的 >>=
的实现进行计算,即:
Just x >>= f = f x
Nothing >>= f = Nothing
所以 f
确实会在 Just
的情况下被调用,但不会在 Nothing
的情况下被调用。所以我们看到确实存在“短路”。事实上,由于 Haskell 是惰性的,默认情况下每个函数都“短路”——除非需要产生结果,否则不会计算任何东西。
这是一个关于性能的有趣问题 - 而不是我个人知道如何回答的问题。当然,正如我刚刚解释的那样,一旦遇到 Nothing
,链中的以下函数的 none 将被评估 - 但执行模式匹配以查看它不太可能是免费的。或许编译器能够通过推断它可以在遇到 Nothing
时放弃整个计算来优化它。但我不确定。
Functional Programming in C++,在第 214 页,参考与 Haskell 的 Either
相同的 expected<T,E>
单子,读取
[...] as soon as any of the functions you're binding to returns an error, the execution will stop and return that error to the caller.
然后,在下面的标题中,它显示为
If you call
mbind
[equivalent to Haskell's>>=
] on anexpected
that contains an error,,mbind
won't even invoke the transformation function; it will just forward that error to the result.
这似乎是在“调整”之前写的内容。 (我很确定 LYAH or RWH 在某个地方强调没有 short-circuiting;如果你记得在哪里,请提醒我。)
确实,根据 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
中的每一个都是函数,具有h
返回 Maybe Int
但管道的中间类型可以是任何包裹在 Maybe
中的东西。也许 f :: String -> Maybe String
, g :: String -> Maybe Char
, h :: Char -> Maybe Int
.
让我们也明确关联性:
result :: Maybe Int
result = ((x >>= f) >>= g) >>= h
要计算表达式,实际上必须调用每个 bind (>>=
),但不一定调用函数 f
、g
,或h
。最终绑定到 h
需要检查它的 left-hand 参数来决定它是 Nothing
还是 Just something
;为了确定我们需要调用对 g
的绑定,并决定我们需要调用对 f
的绑定,这必须至少查看 x
。但是一旦这些绑定中的任何一个产生 Nothing
,我们只需为每一步检查 Nothing
付费(非常便宜),而不是为调用(可能昂贵的)下游函数付费。
假设x = Nothing
。然后 f
的绑定会检查它,看到 Nothing
,并且根本不会调用 f
。但是我们仍然需要绑定它的结果,以便知道它是否是 Nothing
。这继续沿着链条向下直到最后我们得到 result = Nothing
,调用了 >>=
三次但是 none 函数 f
、g
或 [=16] =].
Either
与 Left
值的行为相似,其他 monad 可能有不同的行为。列表可以调用每个函数一次、多次或不调用;元组 monad 只调用每个函数一次,没有 short-circuiting 或其他多重性特征。
您似乎对这些类型的 Monad 实例在 Haskell 中的工作方式存在误解。你说:
Indeed, my understanding, from Haskell, is that in a chain of monadic functions, all of the functions are called,
但事实显然并非如此。事实上任何时候你计算
Nothing >>= f
其中 f
是 a -> Maybe b
类型的任何函数,然后根据 Maybe
monad 的 >>=
的实现进行计算,即:
Just x >>= f = f x
Nothing >>= f = Nothing
所以 f
确实会在 Just
的情况下被调用,但不会在 Nothing
的情况下被调用。所以我们看到确实存在“短路”。事实上,由于 Haskell 是惰性的,默认情况下每个函数都“短路”——除非需要产生结果,否则不会计算任何东西。
这是一个关于性能的有趣问题 - 而不是我个人知道如何回答的问题。当然,正如我刚刚解释的那样,一旦遇到 Nothing
,链中的以下函数的 none 将被评估 - 但执行模式匹配以查看它不太可能是免费的。或许编译器能够通过推断它可以在遇到 Nothing
时放弃整个计算来优化它。但我不确定。