Question

I'm looking to learn F#, but one thing that's confusing to me is the computation expression (do-notation??) syntax and desugaring.

In haskell, you have a very simple Monad typeclass and rules for desugaring do-notation into bind and return. There's no magic involved in adding keywords; the only thing must match up are the types.

In F# there's a bunch of builders, and keywords, and complexity.

Is there a good explanation of how to map one concept to the other?

I basically want to know how I map

do
  x <- monadicComputation
  foo x
  someOtherMonadicComputation
  let y = somePureComputation x
  return $ bar y

to F#.

The only keywords in the haskell are do, (<-) and let.

Was it helpful?

Solution

You can't write generic monadic code in F#, instead you have to specify the monad you're working in by naming the builder associated with the expression. Your example code would look like:

let example = async {
    let! a = someAsyncComputation
    foo a
    do! someOtherAsyncComputation
    let y = somePureComputation a
    return (bar y)
}

for the async computation expression type. The 'bang' pattern (do!, let! etc.) is used when binding monadic values, while the regular keywords are used for non-monadic values.

let! corresponds to bind (>>=) while let corresponds to let in do notation. return corresponds to return, while return! is used to yield an existing monadic value. do! is similar to (>>) which executes a monadic value for its effects, while do is for non-monadic effects, which has no parallel in Haskell.

OTHER TIPS

If you come from the Haskell background than you might be interested in an academic article that I wrote about F# computation expressions recently.

It links the computation expression syntax (which is quite flexible) to standard type classes that are used in Haskell. As already mentioned, F# does not easily let you write code generic over monad (it can be done, but it is not idiomatic), but on the other hand it lets you choose the most appropriate syntax and you can even get nice syntax for MonadPlus or for monad transformers.

In addition to the async monad that was mentioned by Lee, here is an example of MonadPlus (using the sequence expression - a list monad - as an example):

let duplicate list = seq { 
  for n in list do 
    yield n 
    yield n ∗ 10 }

Or a computation expression for parsers:

let rec zeroOrMore p = parse {
  return! oneOrMore p 
  return [] }

The haskell do notation has only one special syntax i.e <- which is mapped to bind function, everything else inside do is just normal function application for which the result is the monad type, for example: return, putStr etc.

Similarly in F# you have let! to represent bind operation and return keyword syntactic sugar (not normal function call like in haskell but this keyword maps to the Return function that you define). Now there are many other keywords that your computation expressions can support (you can omit them easily if not required), they all are documented here. These additional operations sort of give you the syntactic sugar to use F# keywords instead of normal functions that return the monadic value. You can see that all the keyword that you can overload in F# computation expressions have monadic return value.

So basically, you don't need to be worried about all these keywords, just think of them as normal monad returning functions (with a specific type signature that you can find in documentation) that you can call using F# keywords inside the computation expression syntax.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top