嵌套工作流中的产量问题

Issue with yield in nested workflow

我正在尝试编写自己的 Either 构建器,作为我在 f# 中学习计算表达式的探索的一部分,但我遇到了我认为与 Combine 方法有关的问题。到目前为止我的代码:

type Result<'a> = 
    | Failure
    | Success of 'a

type EitherBuilder() = 
    member this.Bind(m,f) = 
        match m with
        | Failure -> Failure
        | Success(x) -> f x
    member this.Yield x =
        Success(x)
    member this.YieldFrom x = 
        x
    member this.Combine(a,b) = 
        match a with 
        | Success(_) -> a
        | Failure -> b()
    member this.Delay y = 
        fun () -> y()
    member this.Run(func) = 
        func()

使用这段代码,我用两个测试来测试 Combine:

let either = new EitherBuilder()
...
testCase "returns success from 3 yields" <|
    fun _ -> 
        let value = either {
            yield! Failure 
            yield 4
            yield! Failure
            }
        value |> should equal (Success(4))
testCase "returns success with nested workflows" <|
    fun _ -> 
        let value = either {
            let! x = either { 
                yield! Failure 
                } 
            yield 5
            }
        value |> should equal (Success(5))

如我所料,第一个测试通过了,但第二个测试失败并显示以下消息:

Exception thrown: 'NUnit.Framework.AssertionException' in nunit.framework.dll either tests/returns success with nested workflows: Failed: Expected: <Success 5> But was: <Failure>

我不明白。 x 未生成,那么为什么它会影响我的父工​​作流程?如果我移动让!下面产生测试通过。我正在盯着我的 Combine 实现,它为我寻找 Failure*Success 对,参数的实际顺序不会影响结果,但它似乎确实如此

do!let! 表达式中的子句被脱糖为 Bind 调用。这意味着您的 Bind 在您执行 let! x = ... 时被调用。

更具体地说,您的第二个示例被脱糖为以下内容:

let value = 
    either.Bind(
       either.YieldFrom Failure,   // yield! Failure
       fun x ->                    // let! x =
           either.Yield 5          // yield 5
    )

所以它永远不会到达 yield 5 - 计算停止在 let! x =

为了使内部计算成为外部计算的 "never become part",只需使用 let(没有爆炸):

let value = either {
     let x = either { 
         yield! Failure 
         } 
     yield 5
     }

这将正确 return Success 5