Search code examples
haskellpointfree

Haskell function rewrite without bind return


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? )


Solution

  • 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).