Skip to content

Instantly share code, notes, and snippets.

Last active August 18, 2016 18:16
Show Gist options
  • Save dino-/6d250a8f271d7717f7e39db2427c8d1d to your computer and use it in GitHub Desktop.
Save dino-/6d250a8f271d7717f7e39db2427c8d1d to your computer and use it in GitHub Desktop.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment