[This question is motivated by Chapter 9 in “Real World Haskell”]
Here’s a simple function (cut down to essentials):
saferOpenFile path = handle (\_ -> return Nothing) $ do
h <- openFile path ReadMode
return (Just h)
Why do I need that $ ?
If the second argument to handle isn’t a do block, I don’t need it. The following works just fine:
handle (\_ -> putStrLn "Error calculating result") (print x)
When I tried removing the $ compilation failed. I can get it to work if I explicitly add parens, i.e.
saferOpenFile path = handle (\_ -> return Nothing) (do
h <- openFile path ReadMode
return (Just h))
I can understand that, but I guess I’m expecting that when Haskell hits the do it should think “I’m starting a block”, and we shouldn’t have to explicitly put the $ there to break things up.
I also thought about pushing the do block to the next line, like this:
saferOpenFile path = handle (\_ -> return Nothing)
do
h <- openFile path ReadMode
return (Just h)
but that doesn’t work without parens either. That confuses me, because the following works:
add a b = a + b
addSeven b = add 7
b
I’m sure I’ll just reach the point where I accept it as “that’s just how you write idiomatic Haskell”, but does anyone have any perspective to give? Thanks in advance.
According to the Haskell 2010 report,
dodesugars like this:do {e;stmts}=e >>= do {stmts}For the second case, it’s the same to write it desugaring like this (and easier for demonstrative purposes):
do {p <- e; stmts}=e >>= (\p -> do stmts)So suppose you write this:
It desugars to this:
Which is the same as this:
Even though you probably intended for it to mean this:
tl;dr – when do syntax is desugared, it does not parenthesize the entire statement like you would think it should (as of Haskell 2010).
This answer is only AFAIK and
Note: if you say to me “but the actual desugaring uses a ‘let’ statement that should group
e >>= ok, right?”, then I would answer the same as the tl;dr for do statements: it doesn’t parenthesize/group like you think it does.