嵌套工作流中的yield问题(Issue with yield in nested workflow)

编程入门 行业动态 更新时间:2024-10-25 04:19:22
嵌套工作流中的yield问题(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))

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

抛出异常:nunit.framework.dll中的'NUnit.Framework.AssertionException'使用嵌套工作流测试/返回成功:失败:预期: <Success 5>但是: <Failure>

我不明白。 x没有产生,为什么它影响我的父工​​作流程? 如果我动了! 以下产生测试通过。 我正在盯着我的Combine实现,它在我看来,对于Failure*Success对,参数的实际顺序不会影响结果,但它似乎确实如此

I'm trying to write my own Either builder as part of my quest to learn computation expressions in f#, but I have hit a wall with what I think is issue with Combine method. My code so far:

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()

With this code I test the Combine with two tests:

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))

The first test passes, as I would expect, but the second test fails with following message:

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

I don't get it. The x is not yielded, so why does it influence my parent workflow? If I move let! below yield the test passes. I'm staring at my Combine implementation and it looks for me that for Failure*Success pair the actual order of arguments would not influence the result, but yet it seems like it does

最满意答案

do! let! 表达式中的子句去掉了Bind调用。 这意味着当你let! x = ...时你的Bind被调用let! x = ... let! x = ...

更具体地说,你的第二个例子变得如下:

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

因此它甚至不会yield 5 - 计算停止在let! x = let! x = 。

为了使内部计算“永远不会成为外部计算的一部分”,只需使用let (没有爆炸):

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

这将正确返回Success 5 。

do! and let! clauses within the expression get desugared to Bind calls. This means that your Bind is called when you do let! x = ....

More specifically, your second example gets desugared into the following:

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

So it never even gets to yield 5 - the computation stops at let! x =.

In order for the inner computation to "never become part" of the outer one, just use let (without the bang):

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

This will correctly return Success 5.

更多推荐

本文发布于:2023-07-29 20:41:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1319583.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:工作流   嵌套   yield   workflow   nested

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!