A few years ago, I started learning . That took me to the path of building a full compiler for a pure functional language in using only a purely functional approach. F# F# After that, I have moved to Scala and never came back to the .NET Framework. However, sometimes I miss the simplicity and expressivity of . I have demonstrated so in some of the posts I have written before such as ( ). F# |> Operator in Scala , Higher order functions, what are they? and What Comes Next to Higher Order Functions Coming back to today just to miss some Scala constructs I really use a lot, the . F# Monad Writer Logging is an important part of our applications. However, we rarely do pure functional logging. I am not going to go over why or why not to do functional logging, but we are going to see how simple is to write and use pure functional logging in by using a . F# Monad Writer The first thing we need to do, it’s a way to create a . Specifically, we want to be able to get access to the initial writer that we are going to use for our next operations. Writer This should be as simple as: let writer = bind 5 "starting" The operation should return a which initial value is and the initial value to be logged is . bind Writer 5 starting In order to do this, we need to define how a looks like. Writer type Writer<'a, 'L> = AWriter of 'a * List<'L> A simple discriminated union is more than enough. We need a generic type and the generic which is our log. Of course, we are not constrained to log only . The elements of the log can be, in fact, of any type. For convenience and simplicity, we are going to use most of the time, but we could use any other type as part of the log. ‘a ‘L string string At this point, we only need the function so it creates the desired . bind Writer let bind = function| (v, itemLog) -> AWriter(v, [itemLog]) is a function that receives two args, a generic value and the first item to be logged, and then it returns our initial . bind v itemLog Writer Now, we need a way to map over the value of the , so we can change types of the boxed value without affecting the log. Let’s see how we can write Writer map let map fx = function| AWriter(a, log) -> AWriter(fx a, log) In here, takes another function that is applied over the value by creating a new . Notice we never mutate a Writer, we just create new . map fx Writer a Writer Writers With only these two, we can start defining other functions that return (doing logging) while keeping referential transparency. Writers Let’s define some functions we could use in our program. let sum x y = bind (x + y, "sum")let mul x y = bind (x * y, "mul")let mod1 x y = bind (x % y, "mod")let minus x y = bind (x - y, "minus") Notice all these functions only have one single responsibility, to sum, to multiply, to find the module, and calculate difference. They have no knowledge of global loggers and they don’t append anything to a shared state. They are very small, very simple to test; they are pure functions. As we can see, all of them return a through the function . Thanks to the advance type system and type inference, defining functions like this is a very easy task. Writer bind F# By the using of we can do transformations as follow. map let str a = a.ToString() let to_string a = a |> map str sum 5 5 |> to_string Let’s take a closer look at this part. First, we created a with value and log and then we call which basically calls so the result is a with value and the same log . We have modified the value of the via without touching the log. Writer 5 + 5 = 10 ["sum"] to_string map Writer "10" ["sum"] Writer map What about getting things out of the ? Writer Let’s define a simple way to extract the current value and the log we have been constructing so far. let run = function| AWriter(a, log) -> (a, log) is a function that receives a and returns in a tuple form the value and the log. Now we could take a look at the content of the . run Writer Writer let (v, log) = run (sum 5 5) or in more idiomatic F# let (v, log) = sum 5 5 |> run In here, is the value and is . v 10 log ["sum"] Previously, we have defined different functions where all of them return ( ), yet we don’t have a way to combine the defined operations so the result of each of them gets aggregated into a single log. Writers sum, mul, mod1, minus The missing operation is . flatMap let flatMap fx = function| AWriter(a, log) ->let (v, new_log) = fx a |> runAWriter(v, List.append log new_log) receives a function of type and returns a new . If we take a closer look, is the one in charge of aggregating the logs from multiple Writers. flatMap 'a -> Writer(b, List<`L> Writer flatMap Let’s look at an example which could be a little more enlightening. let result =sum 5 5|> flatMap (mul 2)|> flatMap (mod1 25)|> flatMap (minus 10) The final value, , is a where we can call . result Writer run let (v, log) = run result System.Console.WriteLine v for i in log doSystem.Console.WriteLine i This will print out: 5summulmodminus Where is the result of and the the log in the order operations were executed. 5 5 + 5 = 10 * 2 = 20; 25 % 20 = 5; 10 — 5 = 5 At this point, we have fully defined a in in it’s simplest way. We could actually add more functionality to it, but let’s keep it as simple as possible for this exercise. Monad Writer F# The entire code of looks like this Writer module MonadWriter = type Writer<'a, 'L> = AWriter of 'a \\* List<'L> \n\n let bind = function \n | (v, itemLog) -> AWriter(v, \\[itemLog\\]) \n\n let run = function \n | AWriter(a, log) -> (a, log) \n\n let map fx = function \n | AWriter(a, log) -> AWriter(fx a, log) \n\n let flatMap fx = function \n | AWriter(a, log) -> \n let (v, new\\_log) = run (fx a) \n AWriter(v, List.append log new\\_log) Conclusions The is a very elegant way to keep your functions pure while they have a single responsibility, such doing small operations like . Also, by following this p_attern,_ we avoid injecting loggers all around our code, or worse, accessing to global loggers which can be very dangerous when used in multithreading / parallel execution contexts. Monad Writer a + b Most of us when working on OO languages have used the loggers, but that only makes deeper dependencies in code while breaking the Single Responsibility Principle. bad It is or solely decision to move to a functional way of doing logging, especially in distributed systems. You can take a look how to do in Apache Spark by reading . How to log in Apache Spark, a functional approach Log safe, be functional.