-- Shakespeare, Love's Labour's Lost
I was inspired a while back by the talk "Write the Other Half of Your Program" by Jason Hemann and Daniel Friedman.
What would an example of this look like? Let us look at one example with the FizzBuzz kata.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fizzbuzzer) | |
(defn divisible-by? [n] | |
#(= 0 (mod % n))) | |
(defn fizzbuzz [x] | |
(let [fizz? (divisible-by? 3) | |
not-fizz? (complement fizz?) | |
buzz? (divisible-by? 5) | |
not-buzz? (complement buzz?)] | |
(cond | |
(and | |
(fizz? x) | |
(not-buzz? x)) "Fizz" | |
(and | |
(buzz? x) | |
(not-fizz? x)) "Buzz" | |
(and | |
(buzz? x) | |
(fizz? x)) "FizzBuzz" | |
(and | |
(not-fizz? x) | |
(not-buzz? x)) (str x) | |
))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fizzbuzzer.tests | |
(require | |
[clojure.test :refer :all] | |
[fizzbuzzer :as sut])) | |
(deftest fizz-tests | |
(testing "Given a number divisible by 3 it must Fizz" | |
(is (= "Fizz" (sut/fizzbuzz 3))) | |
(is (= "Fizz" (sut/fizzbuzz 9))))) | |
(deftest buzz-tests | |
(testing "Given a number divisible by 5 if must Buzz" | |
(is (= "Buzz" (sut/fizzbuzz 5))) | |
(is (= "Buzz" (sut/fizzbuzz 10))))) | |
(deftest fizzbuzz-tests | |
(testing "Given a number divisible by 3 and 5 it must FizzBuzz" | |
(is (= "FizzBuzz" (sut/fizzbuzz 15))) | |
(is (= "FizzBuzz" (sut/fizzbuzz 30))))) | |
(deftest number-tests | |
(testing "Given a number not divisible by 3 or 5 it must return the number given" | |
(is (= "2" (sut/fizzbuzz 2))) | |
(is (= "4" (sut/fizzbuzz 4))))) | |
(run-tests 'fizzbuzzer.tests) |
We see in the code above that the conditional statements around when to Fizz, Buzz, FizzBuzz, or just return the value given are more complex than they normally are.
Here is the Fizz condition:
(and(fizz? x)(not-buzz? x))
We see that in the Fizz condition we are testing that it is a Fizz and that it is not a Buzz, this will allow us to relocate the this condition before the FizzBuzz condition if we want to, but only if the FizzBuzz condition will allow this (which it will).
Here is the Buzz condition:
(and(buzz? x)(not-fizz? x))
We see that it is the reverse of the Fizz.
Here is the FizzBuzz condition:
(and(buzz? x)(fizz? x))
We see that we are testing for both Fizz and Buzz conditions.
Finally here the else condition:
(and(not-fizz? x)(not-buzz? x))
We see that we are looking for Not Fizz and Not Buzz. In a sense we have came up with our own little DSL around the logic of FizzBuzz and at the same time have written our conditions in such a way as to allow for them to be completely rearranged!