I’ve had a hankering to learn a lisp properly for some time, but what really started this particular ball rolling was a chance encounter with Clojure one lunchtime at work and it’s been all parens from then!
I started learning Clojure via the excellent 99 Lisp Problems and have been posting my solutions on github. It’s been fun, but there are a few syntactic things about Clojure that I don’t like that much so I started looking for other things.
Enter Chicken Scheme. Chicken identifies itself as a compiler for the Scheme language, so before we go any further we should probably work out what that means.
Scheme is one of the two main dialects of Lisp, the language designed by John McCarthy in 1958. The other being Common Lisp. As I understand it they share a lot of the same basic traits (the most visually jarring one being parenthesised prefix notation) but Scheme has a minimalist design philosophy which makes the syntax and structure easy to learn and simple to extend.
So we’ve ascertained that Chicken is a Scheme implementation, but what does it do and how do we use it? First of all let’s look at some code
(require-extension sequences) (define (my-last args) (if (empty? (cdr args)) (car args) (my-last (cdr args)))) (print (my-last '(1 2 3 4 5)))
The amount of parens might make your eyes bleed a little if you aren’t used to it, but as we’ll see from stepping throug the code, it removes all the ambiguity about argument ordering or operator precedence.
The basic way you read expressions in Scheme is
(function args). So for example
1 + 1 in an infix language would be represented in scheme as
(+ 1 1).
The first line of our little demo imports one of the many language extensions - Because core scheme is so minimal there are a lot of libraries and extensions provided by the core chicken team to make day to day things a lot easier.
The main bulk is made up with the
(define... block. If this looks a little like a method definition to you, then you’d be correct. We define a method (or a function) called
my-last that takes a single argument
args. The rest of the function is made up of the body of the if-condition. Like Ruby, you don’t need to explicity return from a function, the return value is the result of the last evaluated expression.
The only other slightly odd thing here is the use of
car. These two functions are very important in all Lisps as they are central to how the principle of a list works.
car returns the element at the front of the list and
cdr returns everything else. You can read more about what lists are on Wikipedia, the diagram near the top should give you the idea!
As an aside: Clojure maps these to
rest for readability, in Chicken you can import the
srfi-1 module to get similar shorthand.
So reading the function definition, we can see that if the end of our args list passed in is empty, then we have a single element in our list, which must be the last one, so return it. Otherwise we call our function again but this time we pass in our original list minus the first element. We are iterating through the list using recursion.
The last line of the code block prints the result of running the my last function over the list
(1 2 3 4 5). The quote literal tells Scheme that the list that follows is actually a list of data rather than a function call.
The next step in our journey to Chicken Scheme mastery is to actually make some code run. There are two ways that you can do this, Interpreting, and compiling.
Interpreting is pretty simple: Just copy and paste the code above into a file with a
.scm extension and run it as follows
csi -s my-file.scm
csi command is the basic Read-Eval-Print loop (repl) for Chicken, calling it with the
-s flag tells it to execute the script. If you call it with
-ss then it will look inside the script for a function with the signature
(define (main args) ...)
And run that, which can be really useful for debugging applications.
Chicken Scheme also compiles your programs into binaries, it uses C as an intermediate language, which makes programs generated with Chicken portable and efficient (a good choice for cross platform applications perhaps).
You can compile your chicken files with the
csc my-file.scm -o my-bin
This will produce the executable
my-bin which you can run in the same way as your interpreted script above.
There’s tons of features in Chicken that make it an exciting language to work with, especially if you’re looking for something functional.
It has an excellent foreign function interface for hooking into C and C++ libraries, which makes it compatible with pretty much anything, the module system is simple and elegant, and it has missbehave, one of the best BDD/RSpec like libraries I’ve used.
The documentation for Chicken is top notch. Some of the most useful resources are: