Haskell function rewrite without bind return

233 views Asked by At

On advice I received here, I am trying to rewrite a function without an extraneous bind assignment and return, but am getting stuck with an extra IO I can't seem to understand how to get out of it.

I have

good :: IO (Either Int String)

getit :: Either Int String -> Int

main :: IO ()
main = do
  x <- fmap getit good
  putStrLn $ show x

main works fine. But....

main2 :: IO ()
main2 = do
  putStrLn $ show $ fmap getit good

-- let's try totally without do
main3 :: IO ()
main3 = putStrLn $ fmap show $ fmap getit good

main2 fails with:

• No instance for (Show (IO Int)) arising from a use of ‘show’
• In the second argument of ‘($)’, namely ‘show $ fmap getit good’
  In a stmt of a 'do' block: putStrLn $ show $ fmap getit good
  In the expression: do { putStrLn $ show $ fmap getit good }

And main3 fails with:

• Couldn't match type ‘IO’ with ‘[]’
  Expected type: String
    Actual type: IO String

What's the correct way to rewrite this idiomatically?

(Sub-question: is "<-" this guy actually called a bind? via here: Are there pronounceable names for common Haskell operators? )

2

There are 2 answers

1
Benjamin Hodgson On BEST ANSWER

Variable-binding in do-notation desugars into calls to the bind combinator >>=:

do { x <- m ; rest }  =  m >>= \x -> do rest

So your example translates to:

main = fmap getit good >>= \x -> putStrLn $ show x

Or, in point-free style:

main = fmap getit good >>= putStrLn . show

Or, exploiting the relationship between fmap and >>=:

main = good >>= putStrLn . show . getit

For many monads this last form will be more efficient. fmap often has to rebuild the mapped structure (for example, lists' fmap runs in O(n) time) whereas function composition is always O(1).

0
Mittenchops On

Self, as you googled for how to pronounce "<-" you discovered that I think you were looking for:

main4 :: IO ()
main4 = (fmap show $ fmap getit good) >>= putStrLn

From here:

https://wiki.haskell.org/IO_inside

A more complex example involves the binding of variables using "<-":

main = do a <- readLn print a This code is desugared into:

main = readLn >>= (\a -> print a)

But @Benjamin Hodgson's answer is better, especially the version that requires no fmaps at all.

And to the subquestion, "<-" desugars to a bind, but is pronounced "is drawn from" via: https://wiki.haskell.org/Pronunciation