Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"<-" bindings in do notation

I have a hard time grasping this. When writing in do notation, how are the following two lines different?

1. let x = expression
2. x <- expression

I can't see it. Sometimes one works, some times the other. But rarely both. "Learn you a haskell" says that <- binds the right side to the symbol on the left. But how is that different from simply defining x with let?

like image 716
Undreren Avatar asked Mar 29 '12 20:03

Undreren


People also ask

What does >> mean in Haskell?

Essentially, a >> b can be read like "do a then do b , and return the result of b ". It's similar to the more common bind operator >>= .

Do statements in Haskell?

As a syntactical convenience, do notation does not add anything essential, but it is often preferable for clarity and style. However, do is not needed for a single action, at all. The Haskell "Hello world" is simply: main = putStrLn "Hello world!"

What are monads 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.


3 Answers

The <- statement will extract the value from a monad, and the let statement will not.

import Data.Typeable

readInt :: String -> IO Int
readInt s = do
  putStrLn $ "Enter value for " ++ s ++ ": "
  readLn

main = do
  x <- readInt "x"
  let y = readInt "y"
  putStrLn $ "x :: " ++ show (typeOf x)
  putStrLn $ "y :: " ++ show (typeOf y)

When run, the program will ask for the value of x, because the monadic action readInt "x" is executed by the <- statement. It will not ask for the value of y, because readInt "y" is evaluated but the resulting monadic action is not executed.

Enter value for x: 
123
x :: Int
y :: IO Int

Since x :: Int, you can do normal Int things with it.

putStrLn $ "x = " ++ show x
putStrLn $ "x * 2 = " ++ show (x * 2)

Since y :: IO Int, you can't pretend that it's a regular Int.

putStrLn $ "y = " ++ show y -- ERROR
putStrLn $ "y * 2 = " ++ show (y * 2) -- ERROR
like image 91
Dietrich Epp Avatar answered Oct 11 '22 23:10

Dietrich Epp


In a let binding, the expression can have any type, and all you're doing is giving it a name (or pattern matching on its internal structure).

In the <- version, the expression must have type m a, where m is whatever monad the do block is in. So in the IO monad, for instance, bindings of this form must have some value of type IO a on the right-hand side. The a part (inside the monadic value) is what is bound to the pattern on the left-hand side. This lets you extract the "contents" of the monad within the limited scope of the do block.

The do notation is, as you may have read, just syntactic sugar over the monadic binding operators (>>= and >>). x <- expression de-sugars to expression >>= \x -> and expression (by itself, without the <-) de-sugars to expression >>. This just gives a more convenient syntax for defining long chains of monadic computations, which otherwise tend to build up a rather impressive mass of nested lambdas.

let bindings don't de-sugar at all, really. The only difference between let in a do block and let outside of a do block is that the do version doesn't require the in keyword to follow it; the names it binds are implicitly in scope for the rest of the do block.

like image 30
bitbucket Avatar answered Oct 11 '22 23:10

bitbucket


In the let form, the expression is a non-monadic value, while the right side of a <- is a monadic expression. For example, you can only have an I/O operation (of type IO t) in the second kind of binding. In detail, the two forms can be roughly translated as (where ==> shows the translation):

do {let x = expression; rest} ==> let x = expression in do {rest}

and

do {x <- operation; rest} ==> operation >>= (\ x -> do {rest})
like image 9
Jeremiah Willcock Avatar answered Oct 11 '22 21:10

Jeremiah Willcock