Entropic Thoughts

Fizz Buzz Through Monoids

Fizz Buzz Through Monoids

Some decade ago I read a good implementation of fizzbuzz. What set it apart was its excellent modularity. The original article is no longer on the web1 Although I realised just now it is archived., but this is my reconstruction:

module Main where

import Control.Monad (guard)
import Data.Foldable (for_)
import Data.Maybe (fromMaybe)

fizzbuzz i =
  fromMaybe (show i) . mconcat $
    [ "fizz" <$ guard (rem i 3 == 0)
    , "buzz" <$ guard (rem i 5 == 0)
    ]

main =
  for_ [1..100] $
    putStrLn . fizzbuzz

The great thing about this implementation is that if we get the natural change in requirements – that we are supposed to print “zork” for multiples of seven – we can accomodate that change by simply adding the line that does so:

--- orig.hs          2025-05-23 13:11:29.929652707 +0200
+++ new.hs           2025-05-23 13:18:02.897544701 +0200
@@ -8,6 +8,7 @@
   fromMaybe (show i) . mconcat $
     [ "fizz" <$ guard (rem i 3 == 0)
     , "buzz" <$ guard (rem i 5 == 0)
+    , "zork" <$ guard (rem i 7 == 0)
     ]

 main =

This will combine perfectly well with the other printouts, and we don’t have to change any other place in the code.

This is an indication of good modularity, and there are surprisingly few implementations of fizzbuzz that allow this. I’m serious. Try for yourself!

Monoids are the magic

We have previously seen the guard-sequence pattern which sits at the core of this implementation. The expression

"fizz" <$ guard (rem i 3 == 0)

will evaluate to Just "fizz" whenever i is divisible with three, and in all other cases it evaluates to Nothing. Thus, if a number is not divisible by any of the three given, the list will evaluate to

[Nothing, Nothing, Nothing]

If a number is divisible by only five, but not three or seven, the list will be

[Nothing, Just "buzz", Nothing]

And if a number is divisible by, say, three and seven, but not five, the list will be

[Just "fizz", Nothing, Just "zork"]

These are smushed together by the mconcat from the Monoid interface, which uses the generic smushing operation <>. This behaves just as expected for our strings-that-might-not-exist: it concatenates them together if they do exist, otherwise it returns Nothing.

Whatever we get out of mconcat, we pass it to fromMaybe (show i) which replaces any Nothing values with the string representation of the number coming into the function, but passes through any actual values it receives intact. This is the full fizzbuzz function that converts a number to the correct textual representation.

To make it an actual program, we loop through all numbers [1..100], convert them with fizzbuzz, and print the result.

Why you should care about fizzbuzz with monoids

It’s not that we care particularly much about fizzbuzz as a problem, but this implementation is a great example of the sort of power that’s unlocked when the standard libraries contain the right generic interfaces (like Monoid and Alternative.)

We get this modularity for free when writing fizzbuzz – but not only when writing fizzbuzz! It happens also on greater scales.