A Tale of two WATs

Tags
Semigroup Monoid WAT JavaScript

A JavaScript WAT and what it has to do with monoids

We suppose all programming languages have the occasional watWe believe the origin of this use of the term is from this lightning talk by Gary Bernhardt.

, some function or behavior that is surprising and makes you think, “wait, what?” From time to time, one of these will go viral on Twitter, and we all have a good laugh-cry at how absurd our profession can be.

When this tweet first appeared in our timelines, we had a good chat about it, compared it to Haskell’s equivalents, and tried to figure out how another language might do better. We’re going to use this as an excuse here to talk about the crucial difference between semigroups and monoids and why you should care.

The WAT

The text of the tweet, in case it ever gets deleted is this:

Fortunately, JavaScript has always supported #alternativefacts

Math.min() < Math.max() => false

This may or may not surprise you, depending on how you think of these functions. In JavaScript, Math.minMath.min() documentation.

returns the smallest number passed to it, or NaN if any parameter isn’t a number. However, it also has the somewhat unexpected behavior of returning Infinity when applied to zero arguments. Math.maxMath.max() documentation.

returns the largest number passed to it but returns -Infinity when it is given no arguments. That’s how you get the perhaps unexpected behavior above.

In JS, it seems these functions are conceived as monoidal folds; they have an identity value, which is the empty list. Since the min of an empty list and the max of an empty list are each an empty list, it’s false that one is “less than” the other.

However, is this the behavior we want? Probably not.

In Haskell, the situation is different but hardly better:

Chris wrote this to make Haskell act like JavaScript: https://gist.github.com/chris-martin/a4a69d1f938ffcda0a738419311d4431

Ideally we’d want these functions (minimum and maximum) not to have an identity, but to be semigroup folds over nonempty lists, and to have them only accept NonEmpty arguments (to avoid throwing the exception). But at least in Haskell they are considered semigroupish, it’s just that then they are partial functions and don’t cover the empty list case.

Then what we might want is a Semigroup constrained variant of foldMap (how minimum and maximum are implemented in base) rather than a Monoid constrained one.

PureScript might have such a thing – it seems they’ve adopted more of Kmett’s program of semigroupoids than we have in their base package, so it might be worth checking out sometime.