← Back to list

Creating a custom Monoid with Scala and Cats

In this article, I will demonstrate how a monoid (not yet a monad!) is created using Scala and Cats. The motivation for this was this old…

Florian Baierl · 2021-09-10 21:50 · 3 claps · 2.3 min read
#scala #monoids #functional-programming #cats
Open on Medium ↗
Wiki topics: PSY · Psychology 💻 · Programming 🚀 · Self Improvement

Creating a custom Monoid with Scala and Cats

Image completely unrelated, compared to hiking in the alps, monoids are a piece of cake!

Image completely unrelated, compared to hiking in the alps, monoids are a piece of cake!

In this article, I will demonstrate how a monoid (not yet a monad!) is created using Scala and Cats. The motivation for this was this old Stackoverflow question of mine.

First of all, some definitions:

What we want to create is something similar to Scala’s Either in a sense that we want to have have a type that can show the result of a computation. Let’s call it Result. Result will differ just a bit from a regular Either in the way that it can be combined. Cats provides syntax for the combine method in the form of the |+| operator.

Here are some example use cases for Result and how it differs from Either when combining multiple instances of it:

val e1: Either[String, Int] = Right(1)
val e2: Either[String, Int] = Right(2)
val e3: Either[String, Int] = Left("request failed")
val combined1 = e1 |+| e2 |+| e3
assert(combined1 == Left("request failed"))

val r1: Result[Int] = Result(1)
val r2: Result[Int] = Result(2)
val r3: Result[Int] = Failed("request failed")
val combined2 = r1 |+| r2 |+| r3
assert(combined2 == Succeeded(NonEmptyList.of(1, 2)))

With our goal set, let’s jump to the implementation of Result!

The Result class

The basic Result definition looks like this:

object Result extends ResultInstances {
  def apply[A](values: A*): Result[A] =
    NonEmptyList.fromList(values.toList)
      .map(nel => Succeeded(nel)).getOrElse(Failed())
}
sealed trait Result[+A] {
  def vs: Seq[A] = this match {
    case Succeeded(xs) => xs.toList
    case _ => Nil
  }
}
case class Succeeded[A](values: NonEmptyList[A]) extends Result[A]
case class Failed(reason: String = "") extends Result[Nothing]

As we can see there is already a lot going on. We have created a trait called Result with two classes that extend that type: Succeeded and Failed . Succeeded has a non-empty list of result values, while Failed can contain the failure reason as a String (obviously this could be extended to e.g. a Throwable or a generic type, but for the sake of simplicity, we will use a String for now).

Additionally, I added a small helper method vs to the trait that returns all values stored in that Result instance.

The companion object for Result serves two purposes:

  1. Offer a convenient apply method, so that we can create Results like this:
Result(1) // equals Succedded(NonEmptyList.one(1))
Result()  // equals Failed("")
  1. Bring ResultInstances into scope, whenever Result is used.

Writing the Monoid Type-class for Result

The implementation for ResultInstances is quite straight-forward:

sealed abstract class ResultInstances {

  implicit def resultInstance[A]: Monoid[Result[A]] = 
    new Monoid[Result[A]] {

    override def empty: Result[A] = Failed()

    override def combine(x: Result[A], y: Result[A]): Result[A] =
      Result.apply(x.vs ++ y.vs: _*)
  }
}

As we discussed in the first part of this article, the Monoid of Result needs to implement two methods: empty and combine. For the empty value, we need to use Failed, since Failed |+| x (where x is a Result) is equal to just x. combine creates a new Result with combined inner values. Notice that combine(Failed, Failed) returns Failed (in our case, we would loose both failure messages when combining two Failed instances, though).

And that’s it! We succesfully implemented our new Monoid! The code can be found in my github repository.


메타데이터
post_id
5f01bd4f4a14
slug
creating-a-custom-monoid-with-scala-and-cats-5f01bd4f4a14
url
https://medium.com/@fbaierl1/creating-a-custom-monoid-with-scala-and-cats-5f01bd4f4a14
canonical_url
https://medium.com/@fbaierl1/creating-a-custom-monoid-with-scala-and-cats-5f01bd4f4a14
author_url
https://medium.com/@fbaierl1
status
ok
fetched_at
2026-06-20 20:29:01