MiloDC
MiloDC

Reputation: 2415

F# "exit early" computation expression?

In attempting to learn more about how computation expressions work, I'm attempting to code a builder that skips the remainder of the expression after evaluating the then block of an if statement, whereupon the workflow itself would evaluate to true. The workflow should return false if none of the if statements evaluated to true.

For example:

let mutable x = 0

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

Here, result should be true, and x should be 33.

The closest I've gotten is:

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

... which results in the workflow evaluating to false, and x to 11.

Is this doable using the syntax in my example?

Upvotes: 2

Views: 617

Answers (2)

Tomas Petricek
Tomas Petricek

Reputation: 243061

The smallest change that would give you the behaviour that you are looking for is probably to add return to the computation - the return construct can return true and terminate the evaluation early:

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
    }

This evaluates to true and the value of x will be 33. The computation builder is the same as yours, with additional Return member returning 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

As mentioned in one of the referenced answers, this is somewhat related to my imperative computation builder which lets you use imperative-style return and an extended version with break and continue.

Upvotes: 3

kvb
kvb

Reputation: 55184

I don't think there's any good way to do this using the syntax you're proposing; inside of a computation expression, something like

if c then e

is going to get compiled to something like

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

so there's no way for the context to differentiate which branch was taken.

Upvotes: 3

Related Questions