Проблема с выходом во вложенном рабочем процессе
Я пытаюсь написать свой собственный конструктор 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.AssertionException в nunit.framework.dll либо проверяет / возвращает успех с вложенными рабочими процессами: Ошибка: Ожидается:
<Success 5>
Но было:<Failure>
Я не понимаю x
не уступает, так почему это влияет на мой родительский рабочий процесс? Если я перееду, пусть! ниже сдачи теста проходит. Я смотрю на мою реализацию Combine, и мне кажется, что для Failure*Success
пара фактический порядок аргументов не повлияет на результат, но все же кажется, что это делает
1 ответ
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 =
,
Для того, чтобы внутреннее вычисление "никогда не стало частью" внешнего, просто используйте let
(без взрыва):
let value = either {
let x = either {
yield! Failure
}
yield 5
}
Это будет правильно вернуться Success 5
,