FizzBuzz: How To Overengineer the Coding Interview

If you’re ever looking for a job as a Developer, you will without a doubt encounter the “Coding Interview”.

That terrifying moment when someone tells you a problem you haven’t heard of since your second semester of college and expects you to solve it quickly and correctly without autocomplete, Stack Overflow or a compiler.

Scaaary… I know.

But the main problem with the Coding Interview are not the conditions in which you have to work, or the judgment of your peers or any of that which we associate with stress, rejection and failure during an interview.

Stolen/Borrowed from @The Practical Developer

It’s that since we are getting basic problems, we tend to offer basic solutions that are dirty, complex and built to only work for these exact use cases.

And I would argue that in order to truly assess how good an engineer someone is, you’d have to see how good they can overengineer their solution to a problem.

Because, if you’re not already solving convoluted imaginary issues regarding reusability, maintenance, and scale in your head; are you even solving the problem? Am I right?

So please join me as I do exactly that! For fun! (Yes, I’m that bored.)

FizzBuzz

So let’s take a look at one of the most simple problems: “FizzBuzz”. The idea goes as follows: if a number is divisible by 3 then you print out “Fizz”, if it’s divisible by 5 “Buzz”, if it’s divisible by both: you guessed it “FizzBuzz”. If none of these apply simply print out the number.

So a basic game would go:

    1
    2
    Fizz
    4
    Buzz
    Fizz
    7
    8
    Fizz
    Buzz
    11
    Fizz
    13
    14
    FizzBuzz
    16

Most programmers faced with this problem will write something along the lines of this (in Java of course):

for (int i = 1; i <= n; i++) {
    String output = "";
    if (i % 3 == 0) output += "Fizz";
    if (i % 5 == 0) output += "Buzz";
    if (output == "") output += i;
    System.out.println(output);
}

But let’s say you’re like me and hate Java. Well, I personally have a moral objection against loops and decided to use a functional programming language instead.

So from now on, I will be using OCaml: a French functional programming language, known for being Haskell’s less famous but more charming cousin.

Modeling our Problem

Since we’re going to be overengineering this problem, we won’t directly work with the rules of FizzBuzz until the very end. For now, we will be dealing with an abstract set of rules that can be fed to a magic function that will tell us the output.

What rules?

Imagine a rule as an if statement in the Java code above. The rules will tell our program what to write given which numbers. We will define them in the Form of Tuples of Conditions and Strings.

If a condition is met we will append the string to the output of the function. For example, a Rule in FizzBuzz would be:

let rule = (fun n -> n % 3 = 0), "Fizz";;

Which translates to if the input is divisible by 3 append “Fizz” to the output.

Got it? Ok. Here we go!

Implementation Time!

First! Since I don’t want to import any modules into my code I will implement two quite common functions: map and reduce.

let rec reduce l f a = match l with
    | x::xs -> reduce xs f (f a x)
    | [] -> a;;

let rec map l f = match l with
    | x::xs -> (f x)::(map xs f)
    | [] -> [];;

Next! We have to think about that last empty String check.

Since we are working with abstract rules, we don’t know if one of them wants to output an empty String. So we can’t rely on empty String meaning no rule applied. Therefore it would be best to abstract this case as an Optional instead. Which is already defined in OCaml as:

type 'a optional = Some of 'a | None

Now to correctly deal with Optionals we will need the following functions:

1. Elvis Operator:

let ($) a b = match a with
    | Some(a) -> a
    | None -> b;;

This function simply checks if a is defined. If that’s the case it returns a. Otherwise, it will return the default value b.

Note: The Elvis Operator is usually written with (?:). However because of OCaml’s constraints on infix operators using (?:) is not possible. So I decided to use ($). Just because!

2. Map on Optional:

let mapOptional i f = match i with
    | Some(i) -> Some(f i)
    | None -> None;;

This will pass the value of i to f if it’s present and otherwise return None.

3. Concatenating a String on top of an Optional String:

let (^?) a b = (mapOptional a (fun a -> a ^ b)) $ b;;

This last one has a very simple purpose. If a has a value return ab. Otherwise output b.

Now that we have the optionals out of the way, we can start writing our magic function.

Let’s start with a helping function:

let helper a n (f, s) =
    if (f n) then
        Some(a ^? s)         
    else
        a;;

Which will check for the condition f on n and if it’s met return the accumulator a concatenated with the String s. Otherwise only a.

And now we can call it:

let output n r f =
    (reduce r
        (fun a x -> helper a n x)
    None) $ (f n);;

We will reduce all the rules r using our helper: starting with a None value and collect all the Strings. If no conditions are met the result will be None. If this is the case we will return the default value given by calling f on n. (In the case of fizzbuzz f will be converting the integer n into a String.)

And that’s all we need. Now we only have to feed these functions the right inputs.

For that let’s start by being able to generate a range of numbers:

let rec range a b =
    if a < b then
        a::(range (a + 1) b)
    else
        [b];;

And being able to calculate the *mod *of an integer a given a base b (since it’s not defined in OCaml):

let rec (%) a b =
    if a < b then
        a
    else
        (a - b) % b;;

Now we need the rules we will feed our function:

let rules = map [3, "Fizz"; 5, "Buzz"] (fun (a, b) ->
    (fun n -> n % a = 0), b
);;

And finally we define the function fizzbuzz which will output all the results until n:

let fizzbuzz n =
    map (range 1 n) (fun x -> output x rules string_of_int);;

And we’re done!

If you call fizzbuzz with 15 you will get the output:

["1"; "2"; "Fizz"; "4"; "Buzz"; "Fizz"; "7"; "8"; "Fizz"; "Buzz"; "11"; "Fizz"; "13"; "14"; "FizzBuzz"]

So? What have we learned?

Well, not much. There aren’t any lessons about style or system design here.

Some of you would hire me after reading this. Others (the majority, quite frankly) would probably want to kill me for even daring to write code like that. But I will say this:

You have a much better-informed opinion about how I code based on that than from a little Java snippet anyone can write. And is that not more useful than any “Coding Interview”?

So I will leave you with this. If you’re ever interviewing someone. Maybe, just ask them to overengineer their solution, and see what’s really going on under the hood.

You might be surprised…

Fin~

Written by:

Mathias Quintero

02 Aug 2017
Comments: