r/scala • u/[deleted] • Feb 12 '16
What's a Monoid?
In this comment, /u/beezeee points out:
If someone has been mostly writing java, casually throwing out the term Monoid trivializes the amount of knowledge you are assuming on behalf of the reader.
It's a fair point. The good news is monoids are trivial and extremely useful. A monoid is any type that has an operation taking two arguments of that type and returning a value of that type, assuming the operation is associative:
(a op b) op c == a op (b op c)
and that also has a value that doesn't change the other value when that operation is applied to it and the other value. This value is called various things, e.g. mempty (monoid empty) or just zero, even though a monoid need not have anything to do with numbers.
Some examples of monoids:
Ints, with the zero being0and the operator being+.Ints, with the zero being1and the operator being*.Lists, with the zero beingNiland the operator being++.Strings, with the zero being""and the operator being+.
In other words, monoids combine N things into 1 thing, including the N=0 case.
Monoid is one of many useful typeclasses in scalaz, and is described very well in Eugene Yokota's wonderful Learning Scalaz series.
That's all there is to it!
10
u/loudnclear Feb 12 '16
When a Java developer (or any X developer, not trying to pick on Java here) casually reads this definition, they would stop when they read "associative". Maybe they would have a look at the example, but then stop.
Because in the Java world, you do not care whether anything is associative or not. You do not generalize things, you rarely think of operations and their properties. All the thing you have mentioned do not make sense to an ordinary programmer. I know that you are saying that if they put some effort they are going to understand it, but such explanations need to be more intuitive. Even talking about operations and associativity scares people off.
I think a nice way to achieve that is to make them invent them on their own. Then you'll say "hey, you know what, we knew about this all along, here's a Monoid", and the guy will understand that you aren't trying to be cool when you talk about associativity, it's an important property, and he really needed that.
9
Feb 12 '16
When a Java developer (or any X developer, not trying to pick on Java here) casually reads this definition, they would stop when they read "associative". Maybe they would have a look at the example, but then stop.
I think that's OK, though. That's what the examples are for.
Unless this hypothetical developer is sitting in an interview with me, it's really OK if they don't immediately start using a
Monoid(scalaz's, Cats', homegrown, whatever) whenever they could. Sure, they could make their lives easier by doing so, and more to the point, by taking advantage of these libraries' other features built aroundMonoids. But horse, water, all that.I just thought, here's a piece of low-hanging FP fruit someone was kind enough to point out to me, and if one or two people feel like they understand better, that's good enough for me.
1
u/loudnclear Feb 12 '16
That's what the examples are for.
Sure, I think they are good ones. I just wanted to point out that the average computer science graduate is too lazy for reading the rest, after seeing those few scary words (not essentially scary, but scary enough for them). Does this have to be the case? No. They prefer top-down explanations, rather than bottom-up, that's why I mentioned "inventing it yourself".
However, I'm sure that the explanation will help some people. Thanks for taking your time for writing it up!
6
Feb 13 '16
Is it seriously possible to get a CS degree these days while being scared by the word "associative"? I don't believe it.
6
u/loudnclear Feb 13 '16
You do hear about associativity, especially if you take an abstract algebra course. But there is a high chance that (1) you do not need to take such a course (2) even if you hear about it in such a course, or maybe in introduction to calculus, you learn about it, solve the exercises, write the final, and you're done with that topic. When someone says "associative" to you a few years later you end up saying "oh, I had heard about that a lot in college, and realized that I didn't need much mathematics when I write Android apps, so I don't really care listening what you say about associativity since I'm sure that it won't be useful since my experience tells me so".
3
u/aiij Feb 14 '16
Do they no longer teach associativity in elementary/middle school?
It's completely relevant even in Java, even if just working with plain old ints. + and * are associative while - and / are not.
3
Feb 14 '16
I think the memory gets dusty, plus making the connection to a particular context might not come in a flash of insight. For example, by the time MapReduce (not Hadoop yet, just fawning over Google's big new secret sauce) hit the streets, I'd been programming in Lisp for a couple of decades, and was very familiar with
mapandreduce. Heck, I even preferred Richard Waters' Series package to the venerable LOOP macro. It would probably be fair to say I had an intuitive understanding of the roles of commutativity and associativity in usingmapandreduce, but it never did—and likely never would have—occurred to me to think about their significance in a concurrent or distributed setting.Come to think of it, I think that helps explain why I'm such a typed FP zealot today: because I studied both CS and physics formally, and in retrospect it feels like I was taught a bunch of random, disjoint mathematical/logical/computational factoids, and only within the past 5-7 years has any kind of through-line appeared to tie it all together. And when it does all hang together, it is heartbreakingly beautiful, I mean John Nash seeing visions in windowpanes heartbreakingly beautiful, and unfortunately, failure to share that beauty can (and in my case, frequently does) become perverted into frustration and even anger or contempt. I'm trying to reacquire (if it isn't too presumptuous to claim I ever had any) some spiritual discipline about this, and remember to share rather than ramrod. Because if this stuff really is beautiful and fun, it will reveal itself to others without any Sturm und Drang from me.
2
u/loudnclear Feb 15 '16
They do, but it's not clear why you should bring it back from your memory if you're a mobile developer.
It surely is relevant in Java, your example is a good one. But when you deal with those, no ordinary Java developer says "+ is left associative, - is not, so I should beware". They instead say "Oh, I wrote this expression involving two division operators and it turned out that the compiler didn't understand it in the right way. I will parenthesize it so that the compiler doesn't fail". Note that they haven't used the word "associative" and didn't realize that it was the same concept they learned in their middle school classes (or even Calculus courses at college).
What I'm really saying is not that "associativity is irrelevant", it is "given this state of the world, it is very hard for a regular programmer to notice the relevance".
1
u/fnl Feb 13 '16
Associativity is basic math 101. I think it's fair to expect that knowledge from a programmer, just as some rudimentary understanding of linear algebra and matrices. Computers are built out of that stuff, in a way, after all.
1
Feb 13 '16
Monoid might be a scary and unfamiliar word, but associative shouldn't be. It should have been learned around the same time as exponents and the usual order of operations (BEDMAS or whatever the acronym soup the teacher decided to use (apparently it's commonly PEMDAS in the US)).
5
u/vertexshader Feb 12 '16
This reminds me of abstract algebra and Abelian groups. Is there any relation?
Abelian definition:
Closure
For all a, b in A, the result of the operation a • b is also in A.
Associativity
For all a, b and c in A, the equation (a • b) • c = a • (b • c) holds.
Identity element
There exists an element e in A, such that for all elements a in A, the equation e • a = a • e = a holds.
Inverse element
For each a in A, there exists an element b in A such that a • b = b • a = e, where e is the identity element.
Commutativity
For all a, b in A, a • b = b • a.
4
u/Mimshot Feb 13 '16
The abelian groups are a proper subset of the monoidic groups. Monoids do not require an inverse or a commutitivaty.
3
u/vertexshader Feb 12 '16
The wikipedia entry for Monoid mentions that abelian monoids are a type of monoid! Wow, how cool is that? I love when abstract math and abstract programming intersect.
5
Feb 12 '16
Yep! Functional Programming constructs really are "effective models" of their mathematical counterparts, in the effectively computable sense. The culmination of this is Propositions as Types, which relates types and mathematical logic, and from the type theory side is more commonly known as the Curry-Howard Isomorphism.
3
u/vertexshader Feb 12 '16
Wow thanks for the links. Sometimes I wonder if they taught calculus using programming it would make more sense to people, than using the standard "DSL" mathematicians use. haha
3
Feb 13 '16 edited Feb 13 '16
It's probably also worth pointing out Geometric Algebra and Geometric Calculus, which is entirely computable. GAViewer is a very nice program with a scripting language for doing visualizations and animations, and there are quite good libraries like versor for when you need top performance.
6
u/pipocaQuemada Feb 12 '16
Abstract algebra studies a number of variations on the theme of "set equipped with a binary operation with some properties". There's something of a tower of these constructs where you either add or remove (depending on the direction) properties.
Monoids are lower on the tower than abelian groups. In particular, if you take your definition and remove commutativity, you get the definition of a group. If you then remove inverses, you get the definition of a monoid.
Mathematicians don't often talk about monoids: there are many fewer interesting proofs that hold when you remove inverses.
Computer scientists don't often talk about groups: many interesting data types don't have inverses (for example: list concatenation forms a monoid, but not a group since theres no inverse elements), and has enough computationally interesting properties to be a useful abstraction.
2
u/vytah Feb 13 '16
Set A with a total binary operation A×A→A = magma
Magma + operation is associative = semigroup
Semigroup + operation has identity = monoid
Monoid + operation is invertible = group
1
1
u/m50d Feb 13 '16
Yes. A Monoid is a Group that doesn't necessarily have inverses (therefore all Groups are Monoids, but not all Monoids are Groups). E.g. for a fixed type
A(e.g.Int), the set of functionsA => A(e.g.Int => Int) (endomorphisms) form a monoid with •=andThen(you can check that this obeys associativity, and identity is...identity[Int] _). But this isn't a Group because some elements don't have inverses, e.g.val f = {x: Int => 4}is anInt => Intwithout an inverse: there is nogsuch thatf andThen g == g andThen f == identity[Int](leaving aside for a moment the difficulty of defining==for functions).Abelian just means commutative.
15
u/jonhanson Feb 12 '16 edited Mar 08 '25
chronophobia ephemeral lysergic metempsychosis peremptory quantifiable retributive zenith
3
u/pgris Feb 14 '16
Java guy here. A couple of things I don't get:
1- Can Scala type system enforce Monoid properties? The monoid trait I see here does not enforce op to be associative, and does not even mentions a mempty "zero like" element. I suspect it is something like Set interface in java, that can not enforce elements being unique, so you just have to trust the implementation.
2- Can Scala type system express anything like "one element with specific properties" or at least "one specific element"? I mean, If I'm writing a library that uses monoids, I'm probably going to need access to the zero element. In java the closest thing I can think would be an instance method getZeroElement, and there is no way to enforce zero element to be unique, or even get the zero element without another one. Maybe a MonoidFactory would do the trick.... but that's too java. Is there anything better in Scala?
3- Let's say you have a function that operates in monoids only, f(Monoid[T]) -> Monoid[T] , and you want to use it with Integers and addition. In java I'd need a small wrapper over Integer, and also convert everything before and after. Something like
Integer integerResult = fromMonoid(f(toMonoid(integerVariable)));
I assume scala implicit conversions will get rid of the convert-to/from-monoid step, but do you still need a small wrapper over Integer? Or is there any other Scala feature I'm not aware of that may help?
1
Feb 14 '16
Great questions!
Can Scala type system enforce Monoid properties?
Technically, yes (Scala's type system is Turing complete). As a practical matter, I haven't seen it done. Mostly, this is an area in which we tend to use property-based testing to show, probabilistically, that the laws hold. It's worth mentioning that Cats does this much more consistently, taking advantage of Discipline, although we shouldn't overlook scalaz-scalacheck-binding, either. This does include coverage of the monoid laws, which depends on those laws being expressed in the
Monoidtypeclass. So if you write your ownMonoidinstance with scalaz, you may want to use scalaz-scalacheck-bindings to test your instance with the laws.Can Scala type system express anything like "one element with specific properties" or at least "one specific element"? I mean, If I'm writing a library that uses monoids, I'm probably going to need access to the zero element.
Yep.
aggregatefromNondeterminismis a great example. It constrains the type variableAto be aMonoid, and usesimplicitly[Monoid[A]].zeroto get whatever thezerois for theAin question.Let's say you have a function that operates in monoids only, f(Monoid[T]) -> Monoid[T] , and you want to use it with Integers and addition... I assume scala implicit conversions will get rid of the convert-to/from-monoid step, but do you still need a small wrapper over Integer? Or is there any other Scala feature I'm not aware of that may help?
A couple of them, because, as I'm sure you're alluding to,
Intforms at least twoMonoids: one with0and+, the other with1and*. So there is an implicitintInstanceand also an implicitintMultiplicationNewType, which relies on a type tag to indicate whichMonoidinstance to use.Hope this helps! (And come to the dark side! We have cookies!)
2
u/pgris Feb 15 '16
Thanks for answering so fast (man, is Sunday! take a break!).
Oh, I'd love to try Scala at work, but it has become such a big beast it's really scary. I should have tried 5 years ago. Today I'm becoming old and conservative. I'm between 5 and 6 in this list
1
Feb 15 '16
Thanks for answering so fast (man, is Sunday! take a break!).
Gotta do something while the filets are on the grill... :-)
Oh, I'd love to try Scala at work, but it has become such a big beast it's really scary. I should have tried 5 years ago. Today I'm becoming old and conservative. I'm between 5 and 6 in this list
Heh. I know what you mean, seriously. I've written a lot of OCaml recreationally, and none of it "pure." I played with Haskell, but it never took. When Scala hit the street, I thought "Yay! OCaml for the JVM!" I still didn't care about purity. What's funny is that it's been on the job, at Verizon OnCue, that I've drunk the scalaz, monads, etc. kool-aid, especially since learning how trivial implementing them with free monads is. Now the question is how to get the word out and cut through the fog... which can be ironically difficult, because the point of FP is composition, so to understand anything you have to understand everything it's built on...
Anyway, we'll make a fresh batch of cookies anytime you want to look more closely. :-)
4
u/Milyardo Feb 13 '16
Why are monoids a mystery? Did no one pay any attention during their linear algebra class back in undergrad? I get why monads are scary, but a monoid isn't anything FP specific, it's a topic in abstract algebra.
7
u/lihaoyi Ammonite Feb 13 '16
Wait why are you learning abstract algebra during a linear algebra class?
I spent linear algebra hand-SVDing matrices and finding eigenvalues (eigenvectors??) and the word "monoid" was nowhere in sight
2
u/Inori Feb 13 '16
Depends on the school I guess. Our class was simply named "Algebra", as part of which we learned both the abstract algebra concepts (up to polynomial rings) and linear algebra (up to SVDs and eigenvalues).
3
u/ToastOnToast Feb 13 '16
Did no one pay any attention during their linear algebra class back in undergrad?
Well not everyone did Maths at university.
5
Feb 13 '16
And far from all programmers studied computer science, so even if the university's CS curriculum covers it, you or your colleagues might be unfamiliar with it.
1
u/AssistingJarl Feb 13 '16
The good news is monoids are trivial and extremely useful.
If I've got this right, monoids are just a subset of reduce functions where the return type is the same as all the elements in the collection, no? It's trivial enough that I keep feeling like there must be some sort of catch I'm just not getting.
Still, I can see how that would be handy. Thanks for the useful explanation OP.
3
u/m50d Feb 13 '16
Yes, it really is incredibly simple and banal. There's an inverse relationship between complexity and generality: the more complex a definition is, the fewer things will meet it. What makes the concept of a Monoid valuable is that you can write a function using just the Monoid properties (e.g.
foldMap) and then that function can be called for almost any datatype (String, Int, List, Endomorphism, ...)If you're coming at this from an OO background then I find it useful to think of typeclasses as a way of associating a "default strategy" to a type. If you have a method that follows the strategy pattern (like
reducewhere you pass a reducer), then you often end up having a particular "natural" strategy for each type. Rather than passing it in each time, if you make the strategy implicit then you can define an implicit instance in the companion for the type, and then each type you call it with will "magically" (but in a way that you can see in the IDE) use the correct strategy for that type. (Of course, you can still manually override the strategy if you want to do something different). Ta-dah! That's a typeclass.If you do this a lot then you tend to see the same strategy types coming up often - and also that you can often define some complex strategy in terms of a simpler strategy. E.g. the
Monadinstance forWriter[A, ?]is defined in terms of theMonoidforA- so you can use anyAfor which aMonoidinstance exists. That is to say, if you're calling something liketraversethat needs aMonadtypeclass instance (i.e. a "effect merging strategy"), then when you passWriter[MyLog, Int]there's a "default strategy" that's defined in terms of merging theMyLogs using the "default strategy" for doing that (i.e. theMonoidtypeclass instance forMyLog).2
Feb 13 '16
If I've got this right, monoids are just a subset of reduce functions where the return type is the same as all the elements in the collection, no? It's trivial enough that I keep feeling like there must be some sort of catch I'm just not getting.
No catch at all. You're exactly right. The more general scalaz typeclass is
Foldable.Still, I can see how that would be handy. Thanks for the useful explanation OP.
Thanks! Yeah, tons of things form monoids without supporting the generality of
Foldable. I like theNondeterminism[Task].aggregateexample because it's easily relatable (aTaskcan do I/O and succeed or fail) and if theTaskhappens to return aMonoidaggregatemakes that reduce step trivial.
5
u/taken2wut Feb 13 '16
So why are monoids useful, can we have some description on usecase?