In Haskell, it's sometimes useful to turn off buffering when writing code that uses the standard input/output file handles. That can be done to the three commonly-used handles in one line like this:
import System.IO
mapM_ (flip hSetBuffering NoBuffering) [ stdout, stderr, stdin ]
It's kind of terse though, let's go through what's happening in it.
Setting NoBuffering
on each handle could be done with separate
calls to hSetBuffering
. It takes a handle and a value that
specifies how to buffer:
hSetBuffering stdout NoBuffering
hSetBuffering stderr NoBuffering
hSetBuffering stdin NoBuffering
But this repetition is kind of boring, it would be nice if we could
map a function over a list of handles. hSetBuffering
has type
hSetBuffering :: Handle -> BufferMode -> IO ()
so we can't just use it as the predicate for pure map
. We need
monadic map which is mapM
. The types are (some of these are really
abstracted for type classes Foldable or Traversable, but let's just
keep it simple in this document):
map :: (a -> b) -> [a] -> [b]
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
hSetBuffering
is also going to evaluate to a useless ()
value
each time it's called which mapM
will collect together. We can
use the mapM_
variant to throw the list of those away.
mapM_ :: (Monad m) => (a -> m b) -> [a] -> m ()
mapM_
needs a function of one argument. We can use a lambda to
get the handle into the right place for hSetBuffering
mapM_ (\handle -> hSetBuffering handle NoBuffering) [ stdout, stderr, stdin ]
This is shorter than the original three function applications, but
then that handle variable, kind of boring too. We can use flip
to move the handle argument to the end of the arg list:
mapM_ (\handle -> (flip hSetBuffering) NoBuffering handle) [ stdout, stderr, stdin ]
And then as with algebra, if you think of the -> like an equals sign, you can remove the handle term from both sides of this expression. BTW, this is called "eta reduction" * ( see below )
mapM_ ( (flip hSetBuffering) NoBuffering ) [ stdout, stderr, stdin ]
Get rid of some extra parens now and you have:
mapM_ (flip hSetBuffering NoBuffering) [ stdout, stderr, stdin ]
(flip hSetBuffering NoBuffering)
is a function that takes a handle
and turns off its buffering
These reductions come from lambda calculus and work like this:
beta reduction is when you substitute a value into a function, or "apply" it
(\x -> x + 1) The function
(\x -> x + 1) 7 Now we'll apply it to 7
(7 + 1) Replace x everywhere on the right side with 7. This is the beta reduction
(8) And then we can further reduce it
eta reduction is when you drop an abstraction of a function
(\x -> abs x)
( abs ) We can remove the term x from "both sides"
abs Which is just this
For completeness, alpha conversion is just the renaming of variables
(\x y -> 2 * x * x + y) This
(\a b -> 2 * a * a + b) is the same thing as this
(\y y -> 2 * y * y + y) But beware, THIS IS NOT THE SAME
see the Haskell Wiki: alpha conversion, beta reduction, eta conversion