F # выйти из раннего выражения вычисления?

Пытаясь узнать больше о том, как работают вычислительные выражения, я пытаюсь закодировать построитель, который пропускает оставшуюся часть выражения после оценки блока then оператора if, после чего сам рабочий процесс оценивается как true. Рабочий процесс должен возвращать false, если ни один из операторов if не оценивается как true.

Например:

let mutable x = 0

let result =
    earlyExit {
        if false then x <- 99
        if true then x <- 33
        if true then x <- 11
    }

Здесь result должно быть true, а x должно быть 33.

Самое близкое, что я получил, это:

type EarlyExitBuilder () =
    member this.Combine (a, b) = a || b ()
    member this.Delay fn = fn
    member this.Run fn = fn ()
    member this.Zero () = false

... что приводит к тому, что рабочий процесс оценивается как false, а x как 11.

Это выполнимо, используя синтаксис в моем примере?


person MiloDC    schedule 13.07.2016    source источник
comment
Связано: stackoverflow.com/questions /13710700/   -  person bytebuster    schedule 13.07.2016


Ответы (2)


Наименьшим изменением, которое даст вам поведение, которое вы ищете, является, вероятно, добавление return к вычислению - конструкция return может вернуть true и досрочно завершить вычисление:

let mutable x = 0

let result =
    earlyExit {
        if false then return x <- 99
        if true then return x <- 33
        if true then return x <- 11
    }

Это оценивается как true, а значение x будет 33. Построитель вычислений такой же, как у вас, с дополнительным членом Return, возвращающим true:

type EarlyExitBuilder () =
    member this.Combine (a, b) = a || b ()
    member this.Delay fn = fn
    member this.Run fn = fn ()
    member this.Zero () = false
    member this.Return( () ) = true

Как упоминалось в одном из упомянутых ответов, это в некоторой степени связано с моим построителем императивных вычислений который позволяет использовать return в императивном стиле и расширенную версию с прерыванием и продолжением.

person Tomas Petricek    schedule 13.07.2016
comment
Отлично, спасибо, Томаш! Вы всегда помогаете нам, начинающим энтузиастам F#. :) Приятно осознавать, что я был настолько близок к реализации решения. - person MiloDC; 13.07.2016
comment
@MiloDC Это был забавный вопрос :) вычислительные выражения всегда скрывают некоторые неожиданные сюрпризы (даже для меня!) - person Tomas Petricek; 13.07.2016

Я не думаю, что есть хороший способ сделать это, используя предложенный вами синтаксис; внутри вычислительного выражения, что-то вроде

if c then e

будет скомпилирован во что-то вроде

if c then 
    e
    builder.Zero() 
else 
    builder.Zero()

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

person kvb    schedule 13.07.2016