Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Computation expressions for a Haskell programmer

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.

like image 979
nimish Avatar asked Oct 25 '13 10:10

nimish


People also ask

What is an expression in Haskell?

An expression evaluates to a result (usually written (e rightsquigarrow r) but we'll use e -- > r ). Haskell uses a similar notation for numbers and operators as most languages: 2 -- > 2. 3+4 -- > 7. 3+4*5 {equivalent to 3+(4*5)} -- > 23.

How do you make a Monad in Haskell?

To create a monad, it is not enough just to declare a Haskell instance of the Monad class with the correct type signatures. To be a proper monad, the return and >>= functions must work together according to three laws: (return x) >>= f ==== f x.

What is a Monad in Haskell?

What is a Monad? A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.

Does F# have monads?

F#+ generic computation expressions provide a convenient syntax for writing monadic expressions.


3 Answers

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.

like image 66
Lee Avatar answered Oct 20 '22 03:10

Lee


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 [] }
like image 27
Tomas Petricek Avatar answered Oct 20 '22 04:10

Tomas Petricek


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.

like image 37
Ankur Avatar answered Oct 20 '22 02:10

Ankur