15

我熟悉在 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 因错误而失败,则仍会调用所有其他步骤。

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

4

4 回答 4

13

您在假设一切顺利的情况下编写函数。然后你打开快乐的盒子并继续快乐的盒子。

最后,您可以使用构建器使语法更漂亮。

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 }
于 2015-04-24T15:30:43.823 回答
5

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

只是为了提供另一种选择,值得注意的是,在 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 = ...
...
于 2015-04-24T21:54:31.853 回答
3

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

计算表达式提供了很好的语法糖来执行所谓的单子组合,其中在执行下一步之前自动检查先前的结果值。

我最近就这个概念做了一次演讲——它发布在 youtube 上https://www.youtube.com/watch?v=gNNTuN6wWVc;@scottwlaschin 在http://fsharpforfunandprofit.com/series/computation-expressions.html上有详细的介绍

如果您需要更多帮助,请在推特上联系我!

于 2015-04-24T14:49:20.707 回答
1

丹尼尔的答案是延续风格方法的语法糖。这里是脱糖版本:

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)))
于 2015-04-24T22:13:53.657 回答