Как реализовать раннюю логику возврата в F#

Я знаком с тем фактом, что в 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 завершается с ошибкой, все остальные шаги все еще вызываются.

Есть ли «функциональный» способ «досрочного возврата» вместо вызова каждой функции в рабочем процессе, где мы должны каждый раз проверять наличие ошибки?


person bstack    schedule 24.04.2015    source источник
comment
Прочтите это: fsharpforfunandprofit.com/posts/recipe-part2   -  person Tomasz Jaskuλa    schedule 24.04.2015


Ответы (4)


Вы пишете свои функции в предположении, что все прошло так, как вы сделали. Затем вы разворачиваете счастливый случай и продолжаете счастливый случай.

И, наконец, вы можете использовать конструктор, чтобы сделать синтаксис красивым.

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 }
person Daniel Fabian    schedule 24.04.2015
comment
Это предложение более или менее то, с чем мы согласились, однако оно означает, что вы не вернетесь раньше. Каждая функция в рабочем процессе должна эффективно проверять наличие ошибки. Спасибо, что разъяснили это! - person bstack; 28.04.2015
comment
Здесь следует иметь в виду, что все этапы должны быть выполнены, поскольку каждый этап может изменить тип успеха или ошибки, и в конце вы должны получить детерминированный тип результата. - person melston; 19.04.2021

Другие ответы великолепны, для этого идеально подходят вычислительные выражения.

Просто чтобы предоставить еще один вариант, стоит отметить, что в структуре кода 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 = ...
...

or

let step1 = ...
if failed step1 then () else

let step2 = ...
if failed step2 then () else

let step3 = ...
if failed step3 then () else 

let step4 = ...
...
person latkin    schedule 24.04.2015

Для этого и нужны вычислительные выражения.

Вычислительные выражения предоставляют хороший синтаксический сахар для того, что известно как монадическая композиция, где предыдущее результирующее значение проверяется автоматически перед выполнением следующего шага.

Недавно я говорил об этой концепции — он размещен на YouTube по адресу https://www.youtube.com/watch?v=gNNTuN6wWVc; и у @scottwlaschin есть подробное введение в него по адресу http://fsharpforfunandprofit.com/series/computation-expressions.html

Напишите мне в твиттере, если вам нужна дополнительная помощь!

person John Azariah    schedule 24.04.2015
comment
Но значит ли это, что мы должны проверять каждый шаг? Если шаг А завершится с ошибкой, я хочу выйти, я не хочу проверять это на шагах В, С и D? - person bstack; 24.04.2015
comment
Что вы имеете в виду под выходом? * Если это означает, что вы не выполняете какие-либо из последующих функций в рабочем процессе, именно это и происходит в рабочем процессе. * Если это означает, что вы больше не проверяете значение результата, это на самом деле невозможно сделать в общем виде. используя понятие значения, состоящего из безопасного объединения нескольких функций... - person John Azariah; 24.04.2015
comment
Кроме того, вам не нужно самостоятельно писать код проверки — вы предоставляете только те функции, которые участвуют в рабочем процессе в случае успеха, а функциональная механика поднимает эти функции, чтобы применить код проверки для вас... так что вы не действительно нужно беспокоиться о раздувании кода или проблемах с обслуживанием, которые вы получаете в коде императивного стиля! - person John Azariah; 24.04.2015

Ответ Дэниела - это синтаксический сахар для подхода к стилю продолжения. Вот версия без сахара:

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)))
person BitTickler    schedule 24.04.2015