简体   繁体   中英

F# Computation Expressions and Return statement

On the Try F# website they give an example of a computation expression:

type Age =
| PossiblyAlive of int
| NotAlive

type AgeBuilder() =
    member this.Bind(x, f) =
        match x with
        | PossiblyAlive(x) when x >= 0 && x <= 120 -> f(x)
        | _ -> NotAlive
    member this.Delay(f) = f()
    member this.Return(x) = PossiblyAlive x

let age = new AgeBuilder()

let willBeThere (a:int) (y:int) =
  age { 
    let! current = PossiblyAlive a
    let! future = PossiblyAlive (current + y)

    return future
  }

which seems a bit like the standard Maybe monad found in Haskell.

However, in true Haskell form I would want to use return for the two lines:

let! current = PossiblyAlive a
let! future = PossiblyAlive (current + y)

to be:

let! current = return a
let! future = return (current + y)

however it doesn't work. The closest I get to is:

let! current = age.Return a
let! future = age.Return (current + y)

but this looks dirty. Is there any way to use return without using the computation builders function explicitly?

You can create a nested expression:

let! current = age { return a }
let! future = age { return (current + y) }

although you could just use let instead:

let current = a
let future = current + y

Be aware that this builder breaks the monad laws since

return 150 >>= return is not the same as return 150

I have had a look at this problem in more detail and I think I have found a reasonable alternative to using the age { return <expr> } syntax that Lee shows in his answer.

My main beef with this syntax is that we are already in the age monad therefore any return statement within the body ought to resolve automatically to age.Return . However, fixing this is probably of very low priority for the F# team, because the workaround is extremely simple.

My alternative is to overload the Bind method with a function which takes a value which it then lifts; then sends this lifted value to the other Bind function:

type Age =
| PossiblyAlive of int
| NotAlive

type AgeBuilder() =
    let reasonableAge (x:int) = x >= 0 && x <= 120

    member __.Return x = 
        if reasonableAge x then PossiblyAlive x else NotAlive

    member __.Bind(x:Age, f) =
        match x with
        | PossiblyAlive x when reasonableAge x -> f x
        | _ -> NotAlive

    member this.Bind(x:int, f) =
        this.Bind(this.Return(x), f)

let age = new AgeBuilder()

let willBeThere (a:int) (y:int) =
    age { 
        let! current = a
        let! future = (current + y)
        return future
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM