Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monadic do notation inside let, is it possible?

Consider the following valid Haskell code

module Main where

main :: IO ()
main = do
  let x = f
  print x

f :: Maybe (Int, Int)
f =
  Just 3 >>= (\a ->
    Just 5 >>= (\b ->
      return (a, b)))

where the function f can be rewritten equivalently with do-notation like this

f :: Maybe (Int, Int)
f = do
  a <- Just 3
  b <- Just 5
  return (a, b)

What annoys me, the do notation won't work when I put the contents of f inline. The following code does not even parse:

main :: IO ()
main = do
  let x = do
    a <- Just 3
    b <- Just 5
    return (a, b)
  print x

Am I correct that inside let I am forced to resort to (>>=)?

While I'm at it, the following code does not parse, either:

module Main where

main :: IO ()
main = do
  let x =
    Just 3 >>= (\a ->
      Just 5 >>= (\b ->
        return (a, b)))
  print x

I don't see an obvious reason other than an unnecessary limited power of let. Is there an elegant way to use bind inside let?

like image 844
ruben.moor Avatar asked Nov 27 '15 18:11

ruben.moor


People also ask

What is the syntax for do notation 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 is return Haskell?

return is actually just a simple function in Haskell. It does not return something. It wraps a value into a monad.


1 Answers

Am I correct that inside let I am forced to resort to (>>=)?

No:

main :: IO ()
main = do
  let x = do
      a <- Just 3
      b <- Just 5
      return (a, b)
  print x

Haskell's layout rule dictates that the body of the binding e in p = e mus be intended at least as much as the beginning of p (or the first binding, if you're using multiple at once). Since let in do follows (almost) the same rules as let … in, you can verify this with the following function:

f :: Int
f = 
  let x =
    3 + 5
  in x

This doesn't work, since 3 + 5 doesn't have the same or greater indentation level as x. However,

f :: Int
f =
  let x =
       3 + 5
  in x

works. Also, while the main above works, it doesn't really convey that a and b are things in x's do block, so it's a little bit better to indent them slightly more:

main :: IO ()
main = do
  let x = do
        a <- Just 3
        b <- Just 5
        return (a, b)
  print x
like image 145
Zeta Avatar answered Oct 11 '22 17:10

Zeta