What is the practical difference between a semigro...
# arrow
r
What is the practical difference between a semigroup and a monoid? Both seem to be mechanisms for combining things together
i
r
So what is a case where a monoid can accomplish something that a semigroup can’t? Where does that identity element come in to play? I’ve seen monoid example for things like summing lists, concatenating words, etc, but those all seem like operations a semigroup can achieve just as well.
i
Whenever you want to have a more specific description of the overall behavior of your Data type you can ,in fact should , provide those instances.
r
🤔
i
well semigroup can’t do what a monoid can
Think of this when you filter over a List the end result can be an emptyList right
r
With you so far 🙂
i
List has there by an empty instance and thus has an Monoid instance. We need these kind of reasonings for bigger combinators like FunctorFilter or other Typeclasses which assume specific predicates, just like those I described earlier.
That article I referenced is pretty good if you want to take a look at it, theoretically.
@Bob Glamm already sums up what you need to know practically.
m
As a simple example: if you have list of elements that only have a semigroup instance you can only combine a non-empty list into a single element.
r
@mikkom Ahh that makes a lot of sense. So the identity just acts as a sort of default for any type of combination?
m
Whereas with monoid you have the identity element to return in case of an empty list.
👌🏽 1
Yeah, and it's such a default that can be combined with another element so that the outcome is that same element unchanged.
0 + 1 == 1
,
"foo" + "" == "foo"
etc
b
☝️
I find it harder to come up examples of semigroups that are not monoids for my day-to-day work
r
so that the outcome is that same element unchanged.
Is this a requirement of a monoid’s identity?
b
identity law is that a + 0 == 0 + a == a
m
Yep
b
for whatever "+" and "0" are
well, strictly speaking that is both left- and right-identity but that is required by monoid
I do get mildly irritated when I stumble across a function that returns
null
instead of
listOf()
for List type
m
I would remove the word "mildly"
b
☝️
because
null
violates monoid laws
and then it takes a lot of extra code to handle that case
m
Yeah, and typically for no good reason
b
👆 , Mikko is on a roll
😄 1
r
Ok cool I think this is really clicking for me now. The example that I was using that made me bring this up was using
Validated.applicative
. It requires a non-empty list semigroup, which makes sense because how could a non-empty list have a valid identity? Thanks everyone 🙂
👍 2
r
Semigroup contains just A.combine(): A whereas a Monoid is a Semigroup that also provides an empty identity value
empty(): A
b
One probably really newbie question based on that, if I'm creating a Monad and I provide only a combine api and not an empty identity api I'm actually misunderstand Monads and not implementing one since it's not a Monoid?
r
In practice when you hear the meme ... A monad is a Monoid in the category of endofunctors it's really a bit irrelevant to what we do since the category we care about is the category of Kotlin types where the morphisms are functions that take us from a type to another
In most of those cases the Monoid in question is of A and the Monad is of F for a value of F a which in kotlin we say is Kind<F, A>
But rarely I think of Monads as Monoids when applied to a type system. I'm sure others think differently but that also seem to be the experience of most people I talked to about this topic when applied to computing
b
Normally I use Monads thinking in a sequential and a composable solution, but maybe sometimes I didn't care about provide a default identity value... a
getOrElse {}
from a Monad implementation could be consider a default identity value api?
r
Those are instances of FunctorFilter which are usually monads that can provide an empty identity for example emptyList for List
👍 1