Semigroup

beginner

A semigroup for some given type A has a single operation (which we will call combine), which takes two values of type A, and returns a value of type A. This operation must be guaranteed to be associative. That is to say that:

(a.combine(b)).combine(c)

must be the same as

a.combine(b.combine(c))

for all possible values of a, b ,c.

There are instances of Semigroup defined for many types found in Arrow and the Kotlin std lib. For example, Int values are combined using addition by default but multiplication is also associative and forms another Semigroup.

Examples

Now that you’ve learned about the Semigroup instance for Int try to guess how it works in the following examples:

import arrow.*
import arrow.typeclasses.*
import arrow.instances.*

ForInt extensions { 1.combine(2) }
// 3
import arrow.data.*
import arrow.instances.listk.semigroup.*

ListK.semigroup<Int>().run { listOf(1, 2, 3).k().combine(listOf(4, 5, 6).k()) }
// ListK(list=[1, 2, 3, 4, 5, 6])
import arrow.core.*
import arrow.instances.option.monoid.*

Option.monoid<Int>(Int.semigroup()).run { Option(1).combine(Option(2)) }
// Some(3)
Option.monoid<Int>(Int.semigroup()).run { Option(1).combine(None) }
// Some(1)

Many of these types have methods defined directly on them, which allow for such combining, e.g. + on List, but the value of having a Semigroup typeclass available is that these compose.

Additionaly Semigroup adds + syntax to all types for which a Semigroup instance exists:

Option.monoid<Int>(Int.semigroup()).run {
  Option(1) + Option(2)
}
// Some(3)

Contents partially adapted from Scala Exercises Cat’s Semigroup Tutorial

Data types

Module Data types
arrow.core Either, Option, Try
arrow.data ListK, MapK, NonEmptyList, SequenceK, SetK
arrow.typeclasses Const

Type Class Hierarchy