
Overthinking Fizzbuzz with Monoids - KirinDave
http://dave.fayr.am/posts/2012-10-4-finding-fizzbuzz.html
======
paultopia
Python can produce extensible solutions! It can!

(Edit from sillier comment.)

    
    
      def fizzbuzz(n):
          outstring = ""
          outputs = {5: "buzz", 3: "fizz", 7: "bar"}
          for x in sorted(outputs.keys()):
              if n % x == 0:
                  outstring += outputs[x]
          if outstring:
              return outstring
          return n
    

Then all you gotta do to add more prime numbers is stick them in the dict (or
factorize composite numbers you want to put in). Which is more idiomatic in
python than endless if elif elif elif elif.

~~~
blixt
Hold up, "idiomatic Python" and no generator comprehensions?!

    
    
        def fizzbuzz(i):
          outputs = [(3, 'Fizz'), (5, 'Buzz'), (7, 'Bar')]
          return ''.join(word for x, word in outputs if i % x == 0) or str(i)

~~~
KirinDave
It was over 5 years ago and these are more accepted now.

~~~
blixt
Sorry it wasn't meant very seriously, just me expressing a caricature of
idiomatic Python :)

So disclaimer: good old `for` loops are often a better choice unless you're
doing a simple modification or filtering of another sequence.

~~~
svara
Really? I actually liked that solution, I think it's both readable and clever.
The rule priority is defined by the order of your list of tuples ('fizzbuzz'
vs. 'buzzfizz'), so easy to change. That's superior to the solution above with
sorted(outputs.keys()).

~~~
blixt
I would say my solution is borderline. It requires maybe one or two extra
reads to grok in its entirety due to multiple conditionals, one of which is in
a loop and the other outside. Here's a more readable version for someone
looking at it the first time, or who is not too used to Python:

    
    
        OUTPUTS = [(3, 'Fizz'), (5, 'Buzz'), (7, 'Bar')]
        def fizzbuzz(i):
          result = ''
          for x, word in OUTPUTS:
            if i % x == 0:
              result += word
          return result or str(i)
    

Even the final `or` could be broken out to an `if not`, but in this case it's
so simple I prefer putting it together, much like a coalescing operator.

And then for shits and giggles we can say screw it and put it all in one line:

    
    
        print '\n'.join(''.join(word for x, word in [(3, 'Fizz'), (5, 'Buzz'), (7, 'Bar')] if i % x == 0) or str(i) for i in xrange(1, 101))

------
pja
No where near compact (or possibly obtuse?) enough for a Haskell version,
surely? Here's one I found elsewhere on the net sometime ago:

    
    
        (~>) :: (Integral a) => a -> String -> a -> Maybe String
        (n ~> str) i = str <$ guard (i `mod` n == 0)
    
        fizzbuzz = 3~>"Fizz" <> 5~>"Buzz"
    
        main = mapM_ putStrLn $ catMaybes $ fizzbuzz <$> [1..100]
    

At least it generalises nicely to arbitrary fizzbuzz numbers! (edit: as noted
below this version as written fails to print the non fizzbuzz digits. An
exercise for the reader :) )

~~~
KirinDave
TBF, I wrote this article 5 years ago. Monad Comprehensions were in fashion at
the time.

Now every dang person wants to write an arrow with a tilde in it and exclaim,
'Naturality!' ;)

Gotta love the use of guard in this. Maybe I should revisit.

I suspect there is also a clever way to do this applicatively, but haven't
really noodled with it to see if it lines up. Any chance to use applicativeDo!

~~~
mbid

        main = traverse_ putStrLn $ Compose $ fizzbuzz <$> [1..100]

~~~
KirinDave
I did not know about compose and this is a neat find.

------
101km

      from functools import partial  
    
      def rem(i, n, message):
        return message if not (i % n) else ''
    
      [(''.join([
         partial(rem, message='fizz', n=3)(i),
         partial(rem, message='buzz', n=5)(i)
         ]) or i) for i in range(1,101)]
    
    

vs

    
    
      {-# LANGUAGE MonadComprehensions #-}
    
      module Main where
      import Data.Monoid (mappend)
      import Data.Maybe (fromMaybe, listToMaybe, maybe)
      import System.Environment (getArgs)
    
      fizzbuzz i = fromMaybe (show i) $ mappend ["fizz" | i `rem` 3 == 0]
                                                ["buzz" | i `rem` 5 == 0]
    
      -- mapM_ is our iterator, putStrLn writes to console.
      main = mapM_ putStrLn [ fizzbuzz i | i <- [1..100] ]

~~~
KirinDave
As the author of the article, I feel like you've missed the point of this
article if you're writing a "vs" post.

The important point here is that monoids make this easier and less full of
unique logic. The code you've posted here is clever and thoughtful, but still
commits to the same "we don't abstract over monoids" problem I identified. You
saved 4 lines over other versions and probably lost clarity in your quest for
that.

It's worth noting that you are leveraging a built monoid: strings. '' = mzero
and join is your + operation.

~~~
101km
Sorry, it is very early in the morning so I'm still waking up. I didn't mean
anything by the groggy 'vs' other than a rosetta syntax comparison to Python.

With that being said, I don't think my snippet lost clarity and is
functionally (har har) equivalent to the Haskell.

I believe where Haskell would shine is if the mappend operated on a more
complex type than a regular string, but honestly I've only started reading
learn you a Haskell two days ago so maybe I'm missing something deeper?

Edit: I guess you could say Python strings are monoids and maybe works on ''.

~~~
KirinDave
Fair enough. Sorry, last time this was posted here we had a lot of those.

------
nicolashahn
I took a course that focused on Haskell in college. I enjoyed it. I love
reading articles about how neat Haskell is. I've still never been convinced
that I should use Haskell over a more mundane language for any actual project.

~~~
hood_syntax
Depends on what the project is. If parsing is the biggest part of the project
then I would say Haskell is top-class in that respect. It has proven success
stories. However, due to the lack of control over GC it has latency issues
with certain kinds of work and it's not a great choice for writing demanding
games.

For me, one of the big holes in the Haskell story is a great, idiomatic GUI
toolkit. That makes it a little more difficult to use for standard desktop
applications, but not impossible (after all, there are binding to common
frameworks like GTK etc).

~~~
paulajohnson
On the GUI toolkit issue, I'd recommend combining gtk2hs (which is a thin
wrapper around GTK) with Reactive Banana. GTK signals are isomorphic with
Banana events and GTK attributes are isomorphic with Banana behaviors. So you
can connect up a button press signal from GTK to an Event, transform that
event with data from some other behavior, and hence generate a new behavior
which is connected to the GTK attribute of the text displayed in a text box.
Your application logic is then expressed as the plumbing that pipes data from
event sources to event and behavior sinks. This works well because the event
plumbing is composable in ways that callbacks and state variables of native
GTK aren't.

~~~
hood_syntax
Thanks for the advice! I'll look into that

------
ldd
Just for the fun of it, I tried going the complicated way in Javascript:

```

var createRange = n => Array.from(Array(n), (_, i) => i + 1);

var rem = (i, n, message) => (i % n ? '' : message);

var accumulator = (a, b) => a + b;

var insideReducer = (p, n) => accumulator(p,rem.apply(null,n));

var outsideReducer = (p, n, i) => n ? p + n: p + (i + 1);

var messageFilter = (n, filterArray) => createRange(n)

    
    
        .map(i => filterArray.map((el)=>[i,...el])
    
            .reduce(insideReducer,"")
                           
        )
    
        .reduce(outsideReducer,"")
    

var fizzbuzz = n => messageFilter(n, [[3, 'fizz'],[5, 'buzz']])

```

------
cdevs
Just pass in a associative array with numbers 3 and 5 for keys and what to say
if mod that key value == 0, extendable, done, clock out.

~~~
KirinDave
Associative arrays are monoids. Just saying.

------
kbenson
There's some interesting Perl 6 implementations here[1] (and in the discussion
of that), which shows some interesting capabilities of multiple dispatch, and
farther down using subtypes along with multiple dispatch.

I particularly like this terse one:

    
    
      sub fizzbuzz($n, $_ = [$n % 3, $n % 5]) {
          when [0, 0] { "fizzbuzz" }
          when [0, *] { "fizz" }
          when [*, 0] { "buzz" }
          default { $n }
      }
    

That's fairly tight, while remaining somewhat readable. There's a version that
adds a line or two by moving $_ into the body of the sub as well in case
that's more readable to some.

1:
[https://gist.github.com/masak/b9371625ad85cfe0faba](https://gist.github.com/masak/b9371625ad85cfe0faba)

~~~
laylomo
Nice, reminds me of the equivalent OCaml implementation:

    
    
        let fizzbuzz n =
          match n mod 3, n mod 5 with
          | 0, 0 -> "fizzbuzz"
          | 0, _ -> "fizz"
          | _, 0 -> "buzz"
          | _ -> string_of_int n

------
machiaweliczny
This praised Ruby code in article is crap. It abuses side effects(unnecessary
3 invokes of printing) and needs to resort to using helper (@printed =
"Fizz"). I would never let something like this through code review.

Like you can't simply first generate data and then print it...

~~~
KirinDave
For a high pressure situation like an interview, I think it's quite good code
to produce. Post fact we could separate concerns.

------
Zelizz
We can go a little shorter with C :)

    
    
        main(i){for(;i<101;puts(printf(i%3?"%d\r":"fizz",i)&&i++%5?"":"buzz"));}

~~~
AlexCoventry
How does the divisible-by-five case suppress the numeral output, when `i%3` is
nonzero?

~~~
vidarh
The "\r" in the format string for the number is carriage return. It ensures
that as long as you print to terminal rather than redirect to file etc., the
number gets overwritten if anything else gets printed on the same line.

~~~
AlexCoventry
Ah, thank you.

------
ibotty
Monoid is a concept that is older than category theory. And way simpler. I
don't get why people always say category theory when they mean basic algebra.
Maybe they don't know better (lack of math background, expository texts that
go straight to "category theory")?

Anyone an idea on why that's happening?

~~~
icen
Introduction to category theory via haskell, and not learning the right areas
of abstract algebra?

It's plausible to even do a degree in mathematics and never encounter the name
'monoid' \- you'll obviously encounter the structure, but my abstract algebra
classes just skipped ahead to groups, and then to rings and modules, instead
of spending any time on the simpler structures.

Other times, like when studying formal languages, because regular languages
are a monoid, the structure is introduced, via noting that you have an empty
language and a method to combine any two languages, but not given the name
monoid.

So you come across haskell, and it gives a name to things like monoids,
monads, functors, and so on, and says that monads and functors come from
category theory, and it seems like a valid inference to say that monoids also
come from category theory.

------
bitsofpancake
Translation to Python:

    
    
        for i in range(100):
          print((("fizz" if i % 3 == 0 else "") +
                 ("buzz" if i % 5 == 0 else ""))
                 or str(i))
    

The "+" makes it a monoid.

------
Deestan
Or you can underthink it. It's side-effect free, takes no arguments, and is
terminating, so it can be optimized perfectly for speed:

    
    
        #!/bin/cat
        1
        2
        fizz
        4
        buzz
        6
    

…

    
    
        98
        fizz
        buzz

~~~
philh
That prints #!/bin/cat at the beginning, though.

If I can return to overthinking it, you could use a silly tool[1] I wrote ages
ago and do something like (untested)

    
    
        #! /usr/bin/cmd tail -n +2
        1
        2
        fizz
        ...
    

[1] [http://reasonableapproximation.net/2010/07/27/silly-
things-t...](http://reasonableapproximation.net/2010/07/27/silly-things-to-do-
with-shebang-lines.html)

------
rowyourboat
Why is FizzBuzz a separate branch? To produce the desired output, all you have
to do is print Fizz and Buzz in the correct order.

------
Koshkin
tl;dr: concatenation of strings corresponds to concatenation of "optional
strings"; monoid homomorphism!

------
framebit
No discussion of overthinking FizzBuzz is complete without a shoutout to the
glorious EnterpriseFizzBuzz:
[https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...](https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition)

~~~
roblabla
My favorite fizzbuzz-related story will aways be
[http://joelgrus.com/2016/05/23/fizz-buzz-in-
tensorflow/](http://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/)

Similar to this story, a ridiculously overengineered fizzbuzz using machine
learning...

