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