Take the function getLine.
it has a type
getLine :: IO String
How do I extract the String from this IO value. More generally, how do I
In Haskell, when you want to work with a value that is "trapped" in IO, you don't take the value out of IO. Instead, you put the operation you want to perform into IO, as well!
For example, suppose you want to check how many characters the getLine :: IO String will produce, using the length function from Prelude.
There exists a helper function called fmap which, when specialized to IO, has the type:
fmap :: (a -> b) -> IO a -> IO b
It takes a function that works on "pure" values not trapped in IO, and gives you a function that works with values that are trapped in IO. This means that the code
fmap length getLine :: IO Int
represents an IO action that reads a line from console and then gives you its length.
<$> is an infix synonym for fmap that can make things simpler. This is equivalent to the above code:
length <$> getLine
Now, sometimes the operation you want to perform with the IO-trapped value itself returns an IO-trapped value. Simple example: you wan to write back the string you have just read using putStrLn :: String -> IO ().
In that case, fmap is not enough. You need to use the (>>=) operator, which, when specialiced to IO, has the type IO a -> (a -> IO b) -> IO b. In out case:
getLine >>= putStrLn :: IO ()
Using (>>=) to chain IO actions has an imperative, sequential flavor. There is a kind of syntactic sugar called "do-notation" which helps to write sequential operation like these in a more natural way:
do line <- getLine
putStrLn line
Notice that the <- here is not an operator, but part of the syntactic sugar provided by the do notation.