← Previous: Prelude

Programming Functionally

Clojure and ClojureScript encourage you to think functionally. With a bit of practice, it will become natural to write functions through which data flows, not just cobble together jabbering objects.

Fizz-Buzz Folly

Here’s an easy exercise, often posed as an interview question (From Why Can’t Programmers.. Program?):

Write a program that prints the numbers from 1 through 16. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

What’s the usual (procedural) approach?

Lets start with Javascript, a language favoring a procedural, object-oriented coding style. The exercise starts with “print the numbers from 1 to 16”. So lets reach for a venerable for-loop and print the numbers and the Fizzes. Then …

for ( let i = 1; i <= 16; i++ ) {
  if ( i % 3 === 0 ) {
    console.log("Fizz")
  } else {
    console.log(i);
  }
}

So we’re generating the desired output, but …

for ( let i = 1; i <= 16; i++ ) {
  if ( i % 3 === 0  &&  i % 5 === 0 ) {
    console.log("FizzBuzz")
  } else if ( i % 5 === 0 ) {
    console.log("Buzz")
  } else if ( i % 3 === 0 ) {
    console.log("Fizz")
  } else {
    console.log(i);
  }
}

The functional approach

Above, our thought process began with the side effects we ultimately wished to perform. Instead, let’s begin with the data.

Yes, we want to print a list of numbers, but what if we first think about the list itself. We can make an infinite list of numbers.

(range 17)

What about the Fizz’s and Buzz’s? How about a list where every third item is “Fizz”, and we just make the others empty. The ClojureScript function cycle Makes a new infinite list by repeating the given one over and over:

(take 16 (cycle ["" "" "Fizz"]))

Now try out a custom function, pick-fz-bz-or-n, defined below. There are many ways to test.  cljs.test  is  are

; Maybe we could use function pick-fz-bz-or-n.
; Uncomment to see its doc-string:
;(my-doc #'pick-fz-bz-or-n)

#_         ; Note that #_(...) is another kind of comment.
(test/are
  [fz     bz     n   result] (= (pick-fz-bz-or-n fz bz n)
                                result)
  ""      ""     16  16
  "Fizz"  ""     3   "Fizz"
  ""      "Buzz" 5   "Buzz"
  "Fizz"  "Buzz" 15  "FizzBuzz"
  )
  

Now we have the sequence of numbers and strings we want.

(defn pick-fz-bz-or-n
  "Returns the concatenation of the given strings, or if empty
  returns the last argument."
  [fz bz n]
  (-> (str fz bz)
      not-empty
      (or n)))

(take 16 (map pick-fz-bz-or-n
              (cycle ["" "" "Fizz"])
              (cycle ["" "" "" "" "Buzz"])
              (rest (range))))

Fibonacci Cha-Cha

The Fibonacci sequence starts with 0 and 1, then every later number is simply the sum of the previous two. Let’s make a function to generate the nth (zero-based) Fibonacci number. An ordinary procedural approach might look like this:

function fib(n) {
  const fibs = [0, 1]
  for ( let i = 2; i < n; i++ ) {
    fibs.push( fibs[i - 2] + fibs[i - 1] )
  }
  return fibs
}

fib(16)

Again, we started with the action of looping over side effects, in this case, repeatedly mutating the array fibs.

Instead, let’s turn the “action” inside-out by starting with the data. We can define the data itself recursively, since the elements past the first two depend entirely on the two previous ones.

(def fibs
  (lazy-cat [0 1] (map + fibs (rest fibs))))

(test/is (= (nth fibs 10) 55))

(take 16 fibs)

Notice how we haven’t instructed the computer to do something. We’ve just declared what the data must look like. Where did the loop go?


Next: Conclusion →