Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Omit 'do!' in computation expression

Tags:

f#

Is it possible to put together a computation expression builder that can sequence two or more expressions without putting do! in front of each one?

If I've read the relevant section of the manual correctly, this should be possible through the builder's Combine method. However, my Combine method doesn't appear to be used; instead, I get a compiler warning suggesting that I use ignore to discard the result.

For instance, given an F# State monad, I'd like to be able to do this:

let hello who = State (fun lines -> lines @ [sprintf "hello %s" who])
let m = state {
    hello "world"
    hello "F#"
}
let l = Execute m []
// l should now contain ["hello world"; "hello F#"]
like image 431
Tim Robinson Avatar asked Jan 03 '11 19:01

Tim Robinson


2 Answers

In addition to what Dario wrote, you cannot redefine the usual sequencing to behave as monadic do! (even if you decided not to support non-monadic operations in your computations). The problem is that usual sequencing isn't handled in any special way by the translator, so there is no interception point that you could use. Your example will be translated like this:

let m = state.Delay(fun () ->
  hello "world"
  hello "F#"
  state.Zero())

The call to hello is not wrapped into any member call that could be redefined.

In an earlier version of F# sequencing used to be translated to calls to Let member like this:

let m = state.Delay(fun () ->
  state.Let(hello "world", fun _ ->
    state.Let(hello "F#", fun _ -> 
      state.Zero())))

So it was possible to do what you wanted, but it cannot be done in the current version (I believe one reason was that this adds too much wrapping which hurts the performance).

like image 114
Tomas Petricek Avatar answered Oct 05 '22 03:10

Tomas Petricek


No, because that's what regular do is for. If you want monadic do, i.e. do!, you have to be explicit. Example:

let m = state {
    printfn "World"
    hello "World"
    return 42
}
  • The printfn expression is of type unit, and there is a do printfn implicit.
  • And in the hello case? We'd need monadic binding!

Now how would the compiler know what to choose, do or do!? Maybe by types (Scala does this), but right now, it doesn't. So it just assumes a regular do anywhere.

like image 39
Dario Avatar answered Oct 05 '22 02:10

Dario