A common question is "I have a value of IO a
, but I want to do something to that a
value: how do I get access to it?" How can one operate on data that comes from the outside world (for example, incrementing a number typed by the user)?
The point is that if you use a pure function on data obtained impurely, then the result is still impure. It depends on what the user did! A value of type IO a
stands for a "side-effecting computation resulting in a value of type a
" which can only be run by (a) composing it into main
and (b) compiling and executing your program. For that reason, there is no way within pure Haskell world to "get the a
out".
Instead, we want to build a new computation, a new IO
value, which makes use of the a
value at runtime. This is another way of composing IO
values and so again we can use do
-notation:
-- assuming
myComputation :: IO Int
getMessage :: Int -> String
getMessage int = "My computation resulted in: " ++ show int
newComputation :: IO ()
newComputation = do
int <- myComputation -- we "bind" the result of myComputation to a name, 'int'
putStrLn $ getMessage int -- 'int' holds a value of type Int
Here we're using a pure function (getMessage
) to turn an Int
into a String
, but we're using do
notation to make it be applied to the result of an IO
computation myComputation
when (after) that computation runs. The result is a bigger IO
computation, newComputation
. This technique of using pure functions in an impure context is called lifting.