## 1. Introduction

Monads are one of the most important building blocks of functional programming. They help us to compose various computations in a better way by structuring the program generically and avoiding boilerplate code.

The *Writer* monad is one of the sub-categories of the Monad type. In this tutorial, we’ll look at the *Writer* monad in detail using the Cats library.

## 2. What Is a *Writer* Monad?

**The Writer monad represents a computation that produces a value along with a description of the computation**. It’s represented as type

*Writer[L, A]*, where

*L*represents the computation description, sometimes called the log side of the

*Writer.*Type

*A*represents the actual value of the computation.

**We can accumulate the descriptions of computation along with the computed value using Writer monad**. This is very useful in cases where we want to track and reason about a chain of computations.

We should note that ** Writer[L,A] is a type alias for WriterT[Id, L, A]** where

*WriterT*is a monad transformer. However, for the scope of this article, it’s not necessary to have knowledge about monad transformers.

## 3. Setup

Let’s add the Cats library dependency in *build.sbt*:

`libraryDependencies += "org.typelevel" %% "cats-core" % "2.9.0"`

## 4. Instantiating Writer Monads

Let’s create an instance of a *Writer* monad:

`val writer: Writer[String, Int] = Writer("Multiplication", 5 * 5)`

The first argument of the *Writer* monad is the description of the value/computation.Â The second argument is the actual computation that will be performed. We can get the description and value out from a *Writer* monad using the *run()* method, which returns a tuple:

```
val (desc, value) = writer.run
value shouldBe 25
desc shouldBe "Multiplication"
```

## 5. Operations

In this section, we’ll look at various operations that can be performed on instances of the *Writer* monad.

### 5.1. *map()* and *flatMap()*

Since *Writer* is a monad, we can apply *map()* and *flatMap() *methods just like any other monad. For instance, we can use *map()* on an existing *Writer* instance, that operates only on the value side:

```
val writer = Writer("Number", 5)
val (log, value) = writer.map(_ * 2).run
value shouldBe 10
log shouldBe "Number"
```

Since *map()* and *flatMap()* are available, we can use *for*-comprehension as well:

```
val combined: Writer[String, Int] = for {
writer1 <- Writer("Init Value,", 10)
writer2 <- Writer("Multiplication", 5)
} yield writer1 * writer2
combined.run shouldBe ("Init Value,Multiplication", 50)
```

**When we compose multiple Writer instances, the log side of each writer is combined automatically using a SemiGroup type-class for the String type.** We can notice that the log messages combined together into a single string, without any explicit operation.

### 5.2. Log Side Operations

The *Writer* type provides additional methods to operate only on the log side. Let’s explore them with the help of some examples.

**The tell() method appends a value to the left side of the Writer** using the available SemiGroup type-class instance:

```
val writer = Writer("Init Value", 100)
val writer2 = writer.tell(",Starting manipulations")
writer2.run._1 shouldBe "Init Value,Starting manipulations"
```

Similarly, **we can clear the left side data using the method ****reset()**:

```
val writer = Writer("Init Value", 100)
val resetWriter = writer.reset
val (log, value) = resetWriter.run
value shouldBe 100
log shouldBe empty
```

### 5.3. Other Operations

*Writer* also provides more operators to transform the data within the instance. For instance, **we can extract only the value side from the Writer instance using the method value()**:

```
val writer = Writer("Log Side", 100)
val value: Int = writer.value
value shouldBe 100
```

Additionally, we can swap the sides using the *swap()* method:

```
val writer = Writer("Log Side", 100)
val (log, value) = writer.swap.run
log shouldBe 100
value shouldBe "Log Side"
```

## 6. Pros and Cons of *Writer* Monads

In this section, let’s look at some of the pros and cons of using *Writer* monad.

Let’s see some of their benefits:

- ability to describe the operation along with the final values
- utilize the power of monads for easier composition

There are a few cons when using the *Writer* monad. Even though the left side is called the log side, it shouldn’t be confused with the log statements in the traditional logging sense. **The reason is that the left side of the Writer is meant to hold the accumulated description of operations, rather than log statements**. Moreover, the log side will keep on accumulating until the

*run()*method is executed. As a result, it doesn’t actually log these statements in real time.

**To perform referentially transparent logging, it’s better to use logging libraries such as log4cats.**

## 7. Conclusion

In this article, we looked at the *Writer* monad in Cats. We also discussed how we can utilize the *Writer* monad to describe chained operations. Additionally, we touched on some of the benefits and challenges of using it. The *Writer* monad is extremely useful in documenting and troubleshooting a chain of operations with ease.

As always, the sample code used in this article is available over on GitHub.