Logging from within a Functional Programming Paradigm

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.)


I'm new to functional programming, but here's an attempt in Scala:

object FunctionalLogging {

  type Result = Int

  class ResultWithLogging(val log: List[String], val result: Result) {}

  def functionWithLogging(log: List[String], arg: String): ResultWithLogging = {
    def function(arg: String): Result = arg.length

    new ResultWithLogging(log :+ ("Calling function(" + arg +")"), function(arg))
  }

  val result = functionWithLogging(List(), "Hello world!")

  // -- Pure functional code ends here --
  println("Result = " + result.result)
  println("Log = " + result.log)
}

It's functional in that there are no side effects, but obviously the log is part of the function arguments and return, so it's not very elegant or practical.

It seems to me that logging is a desirable side-effect by definition, so if you go along with my definition the question is how to isolate the non-functional from the functional code. In practice I would probably start with a Scala object (possibly too much like a singleton - a trait is probably better Scala), or an actor to accumulate the logging messages and do whatever needs to be done with them.

This is a more pragmatic view: Logging in Scala

Edit

This question talks about Haskell Monads and IO: What other ways can state be handled in a pure functional language besides with Monads?