Learning Programming Languages with Koans
By Mario Aquino, OCI Principal Software Engineer
January 2011
Introduction
The idea of learning by starting with very basic concepts and building on them is not new. In the 1960s and 70s, the state-of-the-art in computer programming was defined by two predominant programming paradigms: procedural (imperative) style (of which Fortran was the most widely used) and functional (declarative) style (of which LISP was the most common).
Various influential books presented readers with thorough analytical explanations of the functional programming style, perhaps the most famous of which was Structure and Interpretation of Computer Programs (alternately known either as ‘SICP’ or the “Wizard” book due to the artwork on the cover).
This book presented a comprehensive explanation of functional programming theory and the mathematics involved in its application. Although its influence for functional language programmers cannot be overstated, SISP was a difficult book to read because so many of the topics it covered were complicated to grasp.
It wasn’t until the late 1980’s that a book called The Little LISPer (and later a book called The Little Schemer) introduced a novel way to learn functional programming.
In these books, Daniel P. Friedman and Matthias Felleisen combine small examples of LISP concepts with explanations of whether the examples are valid. The entire format of the 2006 book follows a conversation in which a question is asked and then the answer is given, as so:
Is it true that this is an atom?turkey
Yes,
because turkey is a string of characters beginning with a letter.
Is it true that this is a list?(atom turkey or)
Yes,
because it is a collection of atoms enclosed by parenthesis.
This simple format of an example posed as a question and then the answer was easy to follow and provided a basis on which complex ideas could be broken down and explained in small, bite-sized chunks. It should come as no surprise that this format would also work well for teaching the syntax and details of other programming languages. In fact, the pattern of proposing a truth and then verifying the validity of that proposal is the essence of a test. More specifically, it is the pattern followed by classical automated (unit) tests. This article will show the natural fit of unit tests as a device for teaching a programming language as it has been realized by several independent projects, all using the metaphor of “Koans”. There are too many projects to cover in a single piece, so this article will be limited to coverage of exercises for three languages: Ruby, JavaScript, and Clojure.
Koans Projects: Ruby
While there are several Koans efforts under active development, the most thorough (as of this writing) is the Ruby Koans project (by Jim Weirich and Joe O'Brien). This project (as with the others that are covered in this article) follows the metaphor of a Koan in relaying pieces of knowledge about the Ruby programming language. What is a koan (you say)?
First, download the Ruby installer for Windows from RubyForge (I chose the rubyinstaller-1.8.7-p302.exe). In the installer, make sure the option labeled “Add Ruby executables to your PATH” is selected, and otherwise accept the defaults. Next, download a zip file containing the latest version of koans from the github repository for the project. The zip file can be extracted anywhere (e.g. c:\koans).
Additionally, you will need to install a Ruby gem (‘gem’ is the Ruby packaging system for distributable libraries) called ‘rake’. Installation is easy. From a command prompt (assuming you have added Ruby to the PATH of your system), you should be able to run the following to install Rake:
c:\> gem install rake
If you are in an environment behind an internet proxy, the above command will probably not work. However, you can remedy this by first telling Ruby to make its internet calls via an HTTP_PROXY:
c:\> set HTTP_PROXY=http://<PROXY-ADDRESS>:<PORT>
At this point, the koans can be run by opening a command window and moving to the directory where they were extracted (e.g. c:\koans). Within this directory, there are many Ruby source files (all the files have the “.rb” extension) as well as some text files and a file called “Rakefile”. This last file executes all the koans, stopping at the first failing exercise. It can be run with the following command:
c:\koans> rake
And should produce output that looks as follows:
c:\koans>rake
(in c:/koans)
C:/Ruby187/bin/ruby.exe path_to_enlightenment.rb
AboutAsserts#test_assert_truth has damaged your karma.
The Master says:
You have not yet reached enlightenment.
Do not lose hope.
The answers you seek...
<false> is not true.
Please meditate on the following code:
./about_asserts.rb:10:in 'test_assert_truth'
path_to_enlightenment.rb:37:in 'each_with_index'
path_to_enlightenment.rb:37
mountains are merely mountains
your path thus far [X_________________________________________________] 0/274
c:\koans>
Starting the Path
The Ruby Koans are organized as a series of failing unit tests. To make these exercises pass, something in the test needs to be changed to resolve a statement that the test is trying to assert. Each test illustrates a small aspect of the Ruby language. By running the rake
command, the first test has run (and failed) and it is to the student to investigate the problem and try to resolve it.
The console output from running the rake
command contains details about the failing test, what file the test is in and the line with the offending code. From the previous run, line 10 in the ‘about_asserts.rb’ file (in a method called ‘test_assert_truth’) contains the first failure.
#!/usr/bin/env ruby
# -*- ruby -*-
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
class AboutAsserts < EdgeCase::Koan
# We shall contemplate truth by testing reality, via asserts.
def test_assert_truth
assert false # This should be true
end
# Enlightenment may be more easily achieved with appropriate
# messages.
def test_assert_with_message
assert false, "This should be true -- Please fix this"
end
# To understand reality, we must compare our expectations against
# reality.
def test_assert_equality
expected_value = __
actual_value = 1 + 1
assert expected_value == actual_value
end
# Some ways of asserting equality are better than others.
def test_a_better_way_of_asserting_equality
expected_value = __
actual_value = 1 + 1
assert_equal expected_value, actual_value
end
# Sometimes we will ask you to fill in the values
def test_fill_in_values
assert_equal __, 1 + 1
end
end
The top of the file starts out with two lines, the first character of which is a hash ('#') followed by what seem to be declarations of some kind. Next there is a line starting with the word require
and then some other code. Then a line that says class AboutAsserts...
, which is similar to what appeared when the rake
command was run but that still seems mysterious. Afterwards, another line with a hash character and a seemingly helpful note saying something about testing reality via asserts. For someone who has never seen Ruby code, this file has a fair amount code that needs to be ignored in order to arrive at line 10, the line mentioned in the output of the rake
command as containing something on which meditation is required. That line says:
assert false # This should be true
Just from this line, it seems likely that the hash is the character for code comments in Ruby. This can be tested by adding or removing words after the #
and re-running the rake
command. Doing so and noticing that the output from running rake is identical to what appeared when the command was first run indicates that the #
is indeed a code comment character. That is the first lesson of the Ruby language koan, and yet the initial problem is still unsolved. From the commented out portion of that line, #This should be true
probably refers to the two things that appear before the commented-out part: assert false
. Another hint on what the problem may be is from the output of the rake command, which says:
The answers you seek...
<false> is not true.
An aha! moment later, and it seems that this lesson is pointing out that assert
is a command of some kind that expects something truthy to be passed to it. As the output from running rake
says, "false is not true", so to test an assumption about assert, the next logical change should be changing the word 'false' to 'true' and re-running the rake
command.
c:\koans>rake
(in c:/koans)
C:/Ruby187/bin/ruby.exe path_to_enlightenment.rb
AboutAsserts#test_assert_truth has expanded your awareness.
AboutAsserts#test_assert_with_message has damaged your karma.
The Master says:
You have not yet reached enlightenment.
You are progressing. Excellent. 1 completed.
The answers you seek…
This should be true — Please fix this. <false> is not true.
Please meditate on the following code:
./about_asserts.rb:16:in `test_assert_with_message’
path_to_enlightenment.rb:38:in `each_with_index’
path_to_enlightenment.rb:38
learn the rules so you know how to break them properly
your path thus far [.X________________________________________________] 1/274
The assumption about assert seems to be proven out, that it requires something “truthy” to be passed to it. But is that what just happened? Clearly, the rake output is different from the first run and this must mean that the correct change was made, yet there are still unanswered questions about what just happened. It is not obvious what assert
actually is; it could be a language keyword or a function that is just available. If it is a function, why are there no parenthesis surrounding the parameter? On the line above, what does def
mean? Subsequent exercises attempt to answer these as well as many other questions.
The tests in this first source file all deal with a facility to make assertions that is exposed by a testing framework packaged with the Ruby standard library (a testing API called Test::Unit). Examining how to make assertions with this library is useful because tests in other files use these assertions in the act of demonstrating how many aspects of the Ruby language and its core library work.
In all, there are tests covering aspects of Ruby including objects, arrays and array assignment, control statements, strings, hashes, regular expressions, methods, constants, classes, blocks, inheritance, iteration, exceptions, scope as well as many other topics totaling over 270 tests. It even includes a set of tests that teach how to integrate Ruby with Java code (though to access these, a version of Ruby called JRuby is needed).
Koans Projects: JavaScript
Just as the Ruby Koans project was inspired by The Little LISPer, so to was the JavaScript Koans project (by Liam McLennan) inspired by the Ruby Koans project. Like the other project, the JavaScript koans provide a set of tests that explore many aspects of the JavaScript language.
Of the three projects covered in this article, the setup for the JavaScript koans is the easiest. This project is hosted on GitHub and the source code can be easily cloned using your preferred Git client. As well, the source can be downloaded here without using Git.
The tests for this project run within a web browser, so nothing further need be installed. Opening the file jskoans.htm
from the top-level directory of this project produces a familiar message.
JavaScript is a very different language from Ruby, but learning the language by going through these exercises follows the same steps as the previous project.
$(document).ready(function(){
module("About Asserts (topics/about_asserts.js)");
test("ok", function() {
ok(false, 'what will satisfy the ok assertion?');
});
test("not", function() {
not(__, 'what is a false value?');
});
test("equals", function() {
equals(1+1, __, 'what will satisfy the equals assertion?');
});
});
This code block (from the about_asserts.js
file) has a fair amount of content that should be ignored in order to focus on what is failing this first test. The hint from the test report asks “what will satisfy the OK assertion?”, which is duplicated in this file on line 7. This line shows a call to something called ok
that seems to take two parameters: a value (false
as seen above) and a message (the text displayed as a hint in the test report). This line is surrounded by other code (test("ok", function() {
above and a curly brace, parenthesis, and semicolon below). The line above is notable because it contains something (“ok”) that also appears in the test report: 1. About Asserts (topics/about_asserts.js): ok (1, 0, 1). From this, it can be assumed that the “ok” parameter to “test” is a label of some kind for the current test. This can be confirmed by changing “ok” to something else and refreshing the browser. In fact, line 4 also contains text that appears in the test report: module(“About Asserts (topics/about_asserts.js)”);, indicating that “module” is also something that builds a label for the current test. These observations, however, don’t help to solve the problem of the failing test.
Luckily, the first test seems to fail for the same reason that the first Ruby koan failed: the ok
function expects some kind of truthy parameter passed to it. Replacing the false
with true
and refreshing the browser confirms this suspicion. Is that the only right answer? A great benefit of learning with a test is that it provides an environment for experimentation to learn whether the only correct answer is true
or if there are more. Indeed, after trying many different values it seems that there are more answers that are “truthy” than just the literal true
. The following also pass the test: "foo"
, "true"
(in quotes), "false"
(also in quotes — actually, almost any string seems to work with either single or double quotes), 7 (almost positive or negative whole number or decimal passes the test), and the expression function(){}
. Not all values pass, however, including false
(the value originally in the source code), the number 0, an empty string (either ""
or ''
), or the literal value null
.
While the test series provides conditions for exploring different possible answers to the problems they provide, they don’t and can’t explain why some things work and others don’t. These kinds of details must be sought through accompanying literature or other resources (language specifications or other reference books).
Going a bit further in JavaScript tests, the next one asks what is a ‘false’ value which (from the exploration during the previous test) includes several possible answers. The last test of this section introduces a comparison function (equals
) that takes two values (and a message) and compares them. The first of the two values is an expression 1 + 1
and the test requires that an equivalent value be supplied for the second argument. While the value of 2
is the obvious answer, the other tests demonstrated how more than one value might be correct for a given assertion in JavaScript. Indeed, this test also passes for the values of "2"
(the number 2 in single or double quotes), the decimal 2.0, "2.0"
(the number as a string), as well as "2.0" + ""
(a decimal in quotes, the plus sign, and an empty string), while it fails for the string value of "1+1"
. This implies that JavaScript treats integer, decimal and string representations of numbers equally.
By now, it should be obvious that much learning can be done by exploration (if not just through trial and error). The JavaScript koans go on to cover equality & “truthyness”, assignment, control structures, strings, numbers, objects, arrays, scope, regular expressions, and other topics. Much the same ground is covered in these exercises as is covered by the Ruby Koans (though at this point not at the same depth).
Koans Projects: Clojure
The last of the three koans projects covered in this article focuses on the Clojure programming language. Clojure (pronounced like “closure”) is a functional language (a LISP dialect) designed to run on the Java Virtual Machine (JVM) and easily interoperate with Java (and other languages running on that platform).
As with the JavaScript koans, this project is hosted on Github, was started by Aaron Bedra and is maintained by Colin Jones. Setting up the environment to run the koans and start learning Clojure takes several steps.
- Checkout the koans from Github (
git clone https://github.com/functional-koans/clojure-koans.git
) or download the latest snapshot of the project from here. - Ensure that a somewhat recent version of the Java Runtime Environment (v1.5 or higher) is already installed locally. If not, download it from here.
- Install a clojure build utility called Leiningen, following the instructions from here — there are instructions for both Windows and *nix environments.
- With Leiningen installed and visible on the PATH, run the following from a command prompt at the top of the clojure-koans workspace:
lein deps
Once the workspace is configured and all the dependencies are accounted for, the koans can be run by executing the following from the top of the clojure-koans workspace: script/run
(on *nix) or script\run
(on Windows). The output of the first run should look like this:
$> script/run
FAIL in clojure.lang.PersistentList$EmptyList@1 (equalities.clj:1)
We shall contemplate truth by testing reality, via equality.
expected: (= __ true)
actual: (not (= nil true))
By now, this exercise should feel very familiar. Like the other two koans, the clojure koans start with an examination comparing a true
value with something that should be its equal.
(meditations
"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) __ (+ 2 __))
"Some things may appear different, but be the same"
(= 2 2/1 __)
"You cannot generally float to heavens of integers"
(= __ (= 2 2.0))
"But a looser equality is also possible"
(== 2.0 2 __)
"When things cannot be equal, they must be different"
(not= nil __))
Though these first tests for examining equality are similar in flavor to the previous language tests, one notable difference is the absence of function calls for making assertions (assert
from Ruby or ok
, equal
etc from the JavaScript koans). The reason for the difference is that the tests for the previous two languages leveraged testing libraries in each respective language. In the Clojure koans, all but two comparisons of expected to actual values in the entire collection boils down to evaluative equality using the =
function (defined in the Clojure core library), which returns true or false when called.
All of the tests call for a value to replace a double underscore placeholder (__
) in the S-expression under evaluation. For someone who has never seen a LISP variant before, S-expressions might look confusing. Everything is wrapped by parenthesis and the order of things within the parenthesis follows prefix notation.
Looking at these tests, a few details catch the eye. The word meditations appears inside the first open parenthesis (though at first glance it is unclear what the significance of this is). The body of this test file is composed of a series of quoted text strings followed by an S-expression. The quoted text before the expression shows up in the console output from the initial test run, making it obvious which test has failed. Other than appearing as a test label, it is unclear what to call the quoted text; is it a label of some kind? documentation? parsable metadata? The answer is very interesting, actually, though it is left as an exercise for the reader to investigate (hint: a file in the workspace called path_to_enlightenment.clj
has the answer).
Returning to the tests, the first demonstrates that the =
function can evaluate simple literal values (true
), while the second test shows that =
can also evaluate S-expressions passed as parameters. The third test shows that more than two parameters can be considered for evaluation.
By the fourth test, things start getting interesting. This test compares three values for equality: the integer 2
, something represented as 2/1
, and something that needs to be substituted for the__
placeholder. That second parameter is the interesting bit. It looks like a division expression, though this can’t be because Clojure uses prefix notation. In fact, this parameter is actually a fraction (Clojure supports integers, floating-point numbers and fractions).
Beyond the first set of tests, the collection of Clojure koans goes on to cover many other topics of the language including lists, sets, maps, functions, conditionals, recursion, higher-order functions, and more with over 100 tests (and growing — the project is still under active development). This collection of exercises is a great sandbox for someone looking to learn Clojure anew as well as for an experienced Clojure programmer to test their own understanding of the language or just practice its use.
A Foundation for Learning
This article has just scratched the surface on three very compelling projects that aim to make the process of learning a programming language more interactive than what is possible by simply reading a book. However, as was pointed out earlier, the exercises from these koans projects present the ‘what’ and the ‘how’ for their respective languages, but they don’t (and can’t) explain the ‘why’. Collections of tests, no matter how comprehensive, should not be the only tool in the hands of someone looking to learn a new programming language. Tests aren’t a substitute for the deliberative thoughts of a language expert (in the form of textbook prose), but they can definitely be used as a supplement to the traditional learning process.
Among the benefits of language reference books is that they often include valuable graphical representations of programming concepts and relationships. As well, reference books usually include discussion explaining why certain decisions were made in the design of a language as well as history on a languages’ main influences and principles. Digesting these kinds of details helps an aspiring programmer gain a more complete understanding of a given language, beyond just how to use it. When coupled with a good language reference book, koans present both a guided tour of the use of a language as well as a dojo for experimentation.
The Habit of Practice
It would be an unfortunate omission to complete this article without acknowledging the obvious Eastern influence of the koans metaphor. The cover art of “The Little LISPer” features an embellished image of the Taijitu (often referred to as the ‘yin yang symbol’). This symbol is meaningful in many contexts, particularly within martial arts where it connotes balance (among other things). The habits of a martial artist include striking a balance between practicing techniques previously learned and adding new forms of attack and defense.
References
Study of the languages that are the focus of koans covered by this article would be incomplete without good reference books for researching questions not illustrated by the programming exercises. The following are good reference books for Ruby, JavaScript and Clojure:
- [1] Programming Ruby (the “PickAxe” book), by Dave Thomas, Chad Fowler and Andy Hunt
- [2] JavaScript: The Good Parts, by Douglas Crockford
- [3] JavaScript Patterns, by Stoyan Stefanov
- [4] Programming Clojure, by Stuart Halloway