Koans to Katas

Codeidoscope
6 min readAug 9, 2018

--

Clojure is the first language I’ve had to learn in a while, and it’s been an interesting experience to delve into the depth of the resources available and try and figure out what would work best for me.

Clojure Koans are a very popular way to get started with Clojure, and I was no exception to the rule. However, I very quickly got frustrated and a little bored of them, and moved on to Codewars katas instead. Here’s why:

I found the koans to be nice and easy at first, but as they grew in difficulty (which I think is mostly dictated by the topic of the koans), they became too restrictive for my liking. I suspect it is my lack of experience with functional programming and learning languages that made it harder for me to work out what was expected of me in the koans.

For a bit of background, here are some of the koans on equality (the first set the user goes through):

“We shall contemplate truth by testing reality, via equality” 
(= __ true)
“To understand reality, we must compare our expectations against reality”
(= __ (+ 1 1))
“You can test equality of many things”
(= (+ 3 4) 7 (+ 2 __))

And here are some from later sets:

From “creating functions”:

"Don't forget: first things first"  
(= [__ __ __ __]
(let [ab-adder (partial concat [:a :b])]
(ab-adder [__ __])))

From “sequence comprehension”:

"Sequence comprehensions can bind each element in turn to a symbol"
(= __
(for [x (range 6)]
x))

From “recursion”:

(defn recursive-reverse [coll]  
__)
(defn factorial [n]
__)
"Reversing directions is easy when you have not gone far"
(= '(1) (recursive-reverse [1]))
"Yet it becomes more difficult the more steps you take"
(= '(6 5 4 3 2) (recursive-reverse [2 3 4 5 6]))
"Simple things may appear simple."
(= 1 (factorial 1))
"They may require other simple steps."
(= 2 (factorial 2))

What I struggled with the most with the examples I listed was that while there are obviously many ways in which you can solve these problems, I did not have enough context (for my liking) about what the result we needed to get to was, despite each koan building upon one another. As a result, I found it difficult to figure our how to solve the problems, as I did not understand the context around them well.

In contrast, I found Codewars to allow for more creativity, and to provide more context as well. Here are a couple of problems I tackled:

“Do I get a bonus?” kata:

# The problemIt's bonus time in the big city! The fatcats are rubbing their paws in anticipation... but who is going to make the most money?Build a function that takes in two arguments (salary, bonus). Salary will be an integer, and bonus a boolean.If bonus is true, the salary should be multiplied by 10. If bonus is false, the fatcat did not make enough money and must receive only his stated salary.Return the total figure the individual will receive as a string prefixed with "£" (= "\u00A3", JS and Java) or "$" (C#, C++, Ruby, Clojure, Elixir, PHP and Python, Haskell).-----
# The code provided
(ns clojure.bonus)(defn bonus-time [salary bonus]
;; Write your func)
-----
# The tests provided
(ns clojure.bonus-test
(:require [clojure.test :refer :all]
[clojure.bonus :refer :all]))
(deftest basic-tests
(is (= (bonus-time 10000 true) "$100000"))
(is (= (bonus-time 25000 true) "$250000"))
(is (= (bonus-time 10000 false) "$10000"))
(is (= (bonus-time 60000 false) "$60000"))
(is (= (bonus-time 2 true) "$20"))
(is (= (bonus-time 78 false) "$78"))
(is (= (bonus-time 67890 true) "$678900")))

“Calculate average” kata:

# The problemWrite function avg which calculates average of numbers in given list.-----
# The code provided
(ns calculate-average);Write a function that returns the average of a set of numbers.
;Be sure to return longs. We don't want to return Clojure Ratios!
(defn find-average
[numbers]
;Your code here!
)
-----
# The tests provided
(ns calculate-average-test
(:require [clojure.test :refer :all]
[calculate-average :refer :all]))
(deftest example-tests
(is (= 1 (find-average [1 1 1])))
(is (= 2 (find-average [1 2 3])))
(is (= 2.5 (double (find-average [1 2 3 4]))))
)

Here, I feel I am tackling problems I am more likely to encounter in my day-to-day job — I think I’m unlikely to be given a specific result and told to figure out how to get to that result (at least it’s not happened to me so far), but product owners will come to me with a problem, and it will be my responsibility to work within the confine of the acceptance criteria to provide a solution that solve that problem and behaves as expected.

I understand better what is expected of me, whether I need to multiply a salary by ten or calculate the average without using ratios. No cryptic “meditation” that has me scratching my head as to what I’m trying to solve for.

I also get a more adequate frame of reference, because there are several test cases provided, and I can use those as acceptance criteria and make sure my code behaves appropriately. Also sometimes the creators are slightly sadistic and you go through a hidden round of tests that you don’t know about until you submit your answer, and sometimes it fails you and you have to rethink it all.

Finally, what I especially like is that rather than focusing on one specific function of the language, it forces me to put several together, which in turns means I get to understand the syntax better, and figure out more efficient ways to piece my code together, which not all of the koans do.

Ultimately, I know that I struggle a lot with filling in the gaps in someone else’s ideas, and this is very much how I felt tackling koans. I knew something specific was expected of me, but I could not figure out what it was because I needed more context than provided. It’s a bit like when you read a crime novel, and you try really hard to figure out who committed the murder, but you can’t get there because the writer is withholding information from you that you only find out at the end of the book, when everything is revealed.

Or as another apprentice put it, the koans feel like looking at a list of vocabulary: they’re very good at making you aware of what functions exist in relation to what topic, and for this reason, going through them is useful, if only so that you can have a feel for how you can use the language. However, I do not feel they provide you with enough opportunities to practice problem solving in your chosen language, and with that in mind, I have found Codewars to be more beneficial.

Now I have been working with these two resources with the aim of writing a Tic-Tac-Toe game in Clojure. Originally I was looking at the koans to learn more about the language, but I found that I was still stuck as to how to work on my own programme because I did not know how the pieces fitted together. This is where Codewars came in useful, as it was all about piecing together the functions that I learnt about in the koans.

I have in fact been thinking about each element of my Tic-Tac-Toe game as a unit of smaller elements that I needed to get to work together as a result of coding on Codewars. Each of these smaller elements turns into a problem of its own, that I need to solve before I can move on to the next. This has stopped me from panicking about “The Big Picture” and all the things that still need to happen to make my game work, and I’m a much happier coder as a result!

At the end of the day, I would definitely recommend anyone starting in Clojure to look at the koans and attempts some of them, if only to understand what functions are out there for us to use. But by themselves, I don’t think they make for the best-rounded resource, and I’d advise people to find a concurrent source of practice to get a better handle on Clojure.

--

--