Logging from within a Functional Programming Paradigm

Jeff Maner picture Jeff Maner · Dec 7, 2012 · Viewed 8k times · Source

I prefer to stick as closely as possible to the functional paradigm, squeezing as close as I can get to the purely functional when my brain is up for the challenge. I use F# when possible. Usually, I'm stuck with either VB.NET or C# (or VBA, when I'm really unlucky). So my languages allow me to stray quite far from the functional approach.

Historically I've ignored logging and communicating with the user until I've got a result--just let the user wait. Now I'm trying to implement logging and/or updates of status bars. It's easy, because my languages allow me to write to standard output whenever I want. But from a purely functional stand-point, how does one go about leaking information about what's going on inside one's function to the outside world? Is logging or communicating with the user during computation simply contrary to the purely functional approach?

I'm sure in Haskell one would use a Monad. What about when using other languages?

Thanks.

Answer

Petr picture Petr · Dec 8, 2012

Let's have a look at Haskell's monadic solution. The idea behind logging is that our computations have an additional method that writes a message somewhere "out". There are many ways how to represent such computations, but one of the most general is to make a monad:

class (Monad m) => MonadWriter w m | m -> w where
    tell   :: w -> m ()

Type w represents the messages and function tell is what "sends" a message into a monadic (effect-full) computation.

Notes:

  • Haskell's MonadWriter is actually richer, it contains functions that allow to examine and modify w, but let's keep that aside for now.
  • The | m -> w part isn't really important for the explanation, it just means that w is fixed for a given m.

The most often used implementation is Writer, which is basically just a pair. One element of it is the result of a computation and the other element is a sequence of written messages. (Actually it's not really a sequence, it's more general - a monoid, which defines operations for combining multiple messages into one.) You can examine Haskell's solution by looking at the Writer module. However it's written more generally, using WriterT monad transformer, so if you're not a monad fan, it can be quite hard to read. The same thing can be done in other functional languages as well, for example see this example in Scala.

But there are other possible, more side-effect oriented (still functional) implementations of the above type class. We can define tell to emit messages to some outside sink, like to stdout, to a file, etc. For example:

{-# LANGUAGE FunctionalDependencies, TypeSynonymInstances, FlexibleInstances #-}

instance MonadWriter String IO where
    tell = putStrLn

Here we say that IO can be used as a logging facility that writes Strings into stdout. (This is just a simplified example, a full implementation would probably have a monad transformer that would add tell functionality to any IO-based monad.)