like working in high-level languages like Lisp and Ruby, but finding one that produces the small native standalone applications that I sometimes need to build is not easy. The two languages I have found useful in the past are Scheme, a portable Lisp dialect that is often used in computer science classes, and Haskell. However, in the past year I discovered the Gambit-C Scheme
system, which I have used ever since to build my small native applications. When combined with an effective Emacs-based development environment, Gambit-C Scheme provides a high-level dynamic language with good runtime and memory performance, as well as good deployment options.
In this article, I provide a quick introduction to Scheme and cover the issues involved with using it to build native applications on Linux and OS X (there is a Windows installer for Gambit-C and my instructions should also get you going if you have mingw installed). I conclude with two simple application examples: extracting information from text files and writing a mini web service that can support REST clients. Each application is self-contained in a single executable file (click here to download the source code).
This article is for developers who already know some Scheme and want to use it to create small, efficient standalone executables, as well as for those developers who don't yet know the language. The developers with Scheme knowledge can skim the tutorial information and concentrate on the details for building executable programs. The Scheme newbies will find enough introductory information to be able to understand the example programs.
A Crash Course in Gambit-C
Before following along with the rest of the article, you first need to install Gambit-C. If you are using OS X or Windows, use the corresponding Gambit-C installers. Linux has no installer, but you can easily install Gambit-C from source:
tar xvfz gambc-v4_4_4-devel.tgz
sudo make install
Now, you are ready for an introduction to Gambit-C Scheme, which covers just enough Scheme so that you can understand the two examples towards the end. For more in-depth coverage, see Sidebar 1. Scheme Learning Resources.
The Interpreter (gsi) and Compiler (gsc)
The examples use two command-line utilities: the Gambit Scheme interpreter (gsi
) and the Gambit Scheme compiler (gsc
). You can use the define
Scheme special form to create both global variables and functions. Here are some examples in an interactive gsi
session where I define a variable x
, define a function double
, and show how to recover from a runtime error:
> (define x 123)
> (define (double x) (+ x x))
> (double 4)
> (double 3.14159)
> (double "cat")
*** ERROR IN (console)@8.1 -- (Argument 1) NUMBER expected
(+ "cat" "cat")
The prompt 1> indicates that gsi recognized a runtime error and is now in a nested REPL. I recovered from this error by typing Ctrl-D, which brought back the usual > prompt.
While you can experiment with Scheme and play with the examples in this article using the Gambit-C Scheme interpreter gsi, I recommend using Emacs with the Gambit-C Emacs support.
The next example shows how to use if
statements and illustrates that Scheme is a free-format languageyou could put an entire program on one very long line!
> (if (equal? x 123) (display "OK\n") (display "what?\n"))
> (if (equal? x 1567)
Notice that it makes no difference whether you put the if statement all on one line or use three lines to make it more readable. If statements require two arguments and usually are called with three arguments. The first is a Boolean expression. If this expression has a true value at runtime, then the program executes the second argument (an expression). If the program evaluates the first argument to false and you supply a third argument, then the program evaluates the third argument. If you want multiple statements to be evaluated in an if expression, you can use a let block to group multiple expressions into a single expression (that is nested).
Next, let's look at using the let
statement to define two local variables and print their sum:
> (let ((i 1)
(display (+ i j))
Here, I and j are local variables that are defined only inside of the let statement. Notice the two statements after the two variables are defined. Let statements can contain any number of statements; it is not unusual to see nested let special forms and nested functions.
The order in which I and j are defined in the previous example is undefined. If you want to both know the order in which variables are defined and use early variables to define variables declared later in the let special form, then use let*:
> (let* ((x 1)
(y (+ x 100)))
(display (+ x y))
(set! y (+ y 1))
(display (+ x y))
The above example uses set! to change the value of the local variable j. By convention in Scheme, functions that end with a ! character modify the state of their first argument.