## Semigroups?

So, what’s a semigroup? There are multiple types we’ll be talking about but the basic semigroup is:

```
import cats.kernel.laws._
trait Semigroup[T] {
def combine(a: T, b: T): T
}
trait SemigroupLaws[T] {
def S: Semigroup[T]
def associative(a: T, b: T, c: T) =
.combine(S.combine(a, b), c) <-> S.combine(a, S.combine(b, c))
S}
```

There are a few more laws in cats, but `associative`

is the one we care about, you can see it wants `((a, b), c)`

to be equivalent to `(a, (b, c))`

.

This allows us to merge results in any order, which is a nice property to have for a distributed system if we want to perform intermediate merges during a map/reduce style operation.

## Commutative Semigroups?

Our next step up is when we add `commuatative`

:

```
trait CommutativeSemigroup[T] extends Semigroup[T] {}
trait CommutativeSemigroupLaws[T] extends SemigroupLaws[T] {
override def S: CommutativeSemigroup[T]
def commutative(a: T, b: T) =
.combine(a, b) <-> S.combine(b, a)
S}
```

This now allows us to merge in any order, we don’t need to keep track of ordering at all during reduce.

## Semilattice?

Next we can add `idempotent`

:

```
trait Semilattice[T] extends CommutativeSemigroup[T] {}
trait SemilatticeLaws[T] extends CommutativeSemigroupLaws[T] {
override def S: Semilattice[T]
def idempotent(a: T, b: T) =
.combine(a, b) <-> S.combine(a, S.combine(a, b))
S}
```

This then allows us to this then allows us to consume the same item twice without changing the result, this is mostly needed when you can’t guarantee `exactly-once`

delivery.