如何在 F# 中实现 "return early" 逻辑
How to implement "return early" logic in F#
我很清楚 F# 中没有等效的 "return" 关键字。
然而,我们最近遇到了一个问题,我们需要一个包含许多步骤的工作流程,其中每个步骤都可以 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 因错误而失败,仍然会调用其他所有步骤。
是否有 "functional" 方法 "returning early" 而不是调用工作流中的每个函数,我们必须每次都检查错误?
这就是计算表达式的用途。
计算表达式提供了很好的语法糖来执行所谓的单子组合,在执行下一步之前会自动检查先前的结果值。
我最近就这个概念做了一次演讲 - 它发布在 youtube 上 https://www.youtube.com/watch?v=gNNTuN6wWVc; and @scottwlaschin has a detailed introduction to it at http://fsharpforfunandprofit.com/series/computation-expressions.html
如果需要更多帮助,请在 Twitter 上联系我!
你在假设下编写你的函数,一切都像你所做的那样顺利。然后你打开快乐的盒子并继续快乐的盒子。
最后,您可以使用生成器使语法更漂亮。
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# 代码结构中有几个特殊情况,可以减轻 "return-early" 的痛苦。
规范的基于缩进的格式可能是这样的:
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 = ...
...
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)))
我很清楚 F# 中没有等效的 "return" 关键字。
然而,我们最近遇到了一个问题,我们需要一个包含许多步骤的工作流程,其中每个步骤都可以 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 因错误而失败,仍然会调用其他所有步骤。
是否有 "functional" 方法 "returning early" 而不是调用工作流中的每个函数,我们必须每次都检查错误?
这就是计算表达式的用途。
计算表达式提供了很好的语法糖来执行所谓的单子组合,在执行下一步之前会自动检查先前的结果值。
我最近就这个概念做了一次演讲 - 它发布在 youtube 上 https://www.youtube.com/watch?v=gNNTuN6wWVc; and @scottwlaschin has a detailed introduction to it at http://fsharpforfunandprofit.com/series/computation-expressions.html
如果需要更多帮助,请在 Twitter 上联系我!
你在假设下编写你的函数,一切都像你所做的那样顺利。然后你打开快乐的盒子并继续快乐的盒子。
最后,您可以使用生成器使语法更漂亮。
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# 代码结构中有几个特殊情况,可以减轻 "return-early" 的痛苦。
规范的基于缩进的格式可能是这样的:
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 = ...
...
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)))