Haskell IO Without the M-word
There was a recent suggestion that introducing new Haskell users to the M-word too early may not be beneficial. I decided to try and give a brief introduction to IO in Haskell without it. I hope it helps and doesn’t confuse people further. Feel free to send a pull request.
You’re probably aware that IO functions in Haskell have a type something like this:
IO by itself is not a concrete type you can use in a function, it’s
a type constructor. This means it needs to be given another type as an
argument to make it a concrete type.
IO String is an IO action that
returns a string,
IO () is an IO action that returns no useful
() is the unit type (a bit like
void in C).
For example, the function that prints a string and returns nothing interesting:
You can sequence two IO actions using
>>, which is a bit like using
a semicolon in in your shell:
putStrLn "Hello" >> putStrLn "World"
is similar to
echo Hello ; echo World in Bash.
It executes the first action, does nothing with the result, and then executes the second action.
If you want to pipe the result of one action into another one, you can
>>= (called bind), which is a bit (but not entirely) like
cat filename | echo in your shell:
readFile "filename" >>= putStrLn
Using this can get a bit messy when you have a lot of actions, so
is the same as
is the same as
If you just want to know about IO, stop here. Read further if you want another hint on the mystery of the M-word.
If you have a dictionary lookup function which takes a key and returns a result if one exists:
We don’t specify the dictionary with this function, we just assume it’s in the environment somewhere for simplicity.
Maybe a is a type with two potential values: it’s either
Just a, where
a is a type we haven’t specified. In this example
if the first
String argument is in the dictionary, it will return a
Just String, and if it’s not it’ll return
Maybe is also a type constructor like
IO, and if you squint…
… it looks kinda similar, and you can ‘pipe’ the result back in to
another lookup using the same syntax we used above (including the
lookup "test" >>= lookup
This looks up “test” in the dictionary, and if it fails it returns
Nothing. If it succeeds it ‘pipes’ the result into another lookup,
returning either a
Just String or a
This ‘piping’ stuff is a useful pattern! Those boffins at Haskell HQ should give it a name. Maybe they can borrow some terminology from category theory or something.