嵌套工作流中的产量问题
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
。
我正在尝试编写自己的 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
。