如何在 F# 中实现"return early"逻辑



我熟悉F#中没有等效的"return"关键字这一事实。

然而,我们最近遇到了一个问题,我们需要一个由多个步骤组成的工作流,每个步骤都可能返回好的或坏的结果。如果在任何一个步骤中发现了不好的结果,我们希望退出工作流,并提前退出!

我们通过有效地检查每个步骤(即函数)中的错误来解决这个问题,但我觉得这不是正确的方法——效率低下,我们不会提前退出。

工作流中的示例函数如下:

let StepB stepAResult someParameters =
match stepAResult with
| Good(validResourceData) ->
// Do current step processing
// Return current step result
| Error(error) -> error |> Result.Error

工作流程本身如下所示:

let Workflow someParameters =
let stepAResult = StepA someParameters
let stepBResult = StepB stepAResult someParameters
let stepCResult = StepC stepBResult someParameters
let stepDResult = StepD stepCResult someParameters
stepDResult

因此,每个示例函数都会接受上一个函数的结果,并且只有在没有错误的情况下才执行当前步骤!

我遇到的问题是,如果StepA因错误而失败,那么每隔一步都会被调用。

是否有一种"函数式"的"提前返回"方式,而不是调用工作流中的每个函数,即我们每次都必须检查是否有错误?

您在这样的假设下编写函数,即一切顺利。然后你打开这个快乐案例,继续这个快乐案例。

最后,您可以使用构建器使语法更加美观。

type Result<'TSuccess, 'TError> = 
| Success of 'TSuccess
| Error of 'TError
type ResultBuilder() =
member this.Bind(v, f) =
match v with
| Success v -> f v
| Error e -> Error e
member this.Return value = Success value
let result = ResultBuilder()
let bla<'a> = result {
let! successOne = Success 1
let! successTwo = Success 2
let! failure = Error "after this, the computation exited"
failwith "Boom, won't occurr"
return successOne + successTwo }

其他答案都很好,计算表达式非常适合。

只是为了提供另一种选择,值得注意的是,F#代码结构中有几个特殊情况,允许不那么痛苦的"提前返回"故事。

规范的基于缩进的格式可能是这样的混乱:

let step1 = ...
if failed step1 then
() // bail out
else
let step2 = ...
if failed step2 then
()
else
let step3 = ...
if failed step3 then
()
else 
let step4 = ...
...

下面是两种可供选择的格式。它们看起来很奇怪,但实际上很方便。

let step1 = ...
if failed step1 then
() // bail out
else
let step2 = ...
if failed step2 then
()
else
let step3 = ...
if failed step3 then
()
else 
let step4 = ...
...

let step1 = ...
if failed step1 then () else
let step2 = ...
if failed step2 then () else
let step3 = ...
if failed step3 then () else 
let step4 = ...
...

这就是计算表达式的作用。

计算表达式为进行所谓的一元合成提供了很好的语法糖,在执行下一步之前,会自动检查上一个结果值。

我最近做了一个关于这个概念的演讲,它发布在youtube上https://www.youtube.com/watch?v=gNNTuN6wWVc;@scottwlaschin在http://fsharpforfunandprofit.com/series/computation-expressions.html

如果你想得到更多帮助,请在推特上给我打电话!

Daniel的答案是语法糖到延续风格的方法。这里的脱糖版本:

let step1 parm cont =
if true then cont 42 else None
let step2 parm cont =
if false then cont 69 else None
let conti parm =
step1 parm (fun result1 -> 
step2 parm (fun result2 -> 
Some(result1 + result2)))

最新更新