I've been an enthusiastic Mac user for about 12 years, but hardware problems with a recent MacBook Pro and friction surrounding the Catalina upgrade pushed me to evaluate other Unix-like systems. I pulled out an old ASUS laptop that originally had Windows 7(?) installed, but was most recently running CloudReady. I first tried installing FreeBSD because it seemed like an intriguing alternative, but the installation failed on the old hardware. I then tried Debian, but also failed. Finally, I reached for Ubuntu and, true to its reputation as being beginner friendly, was able to successfully complete the installation [1].

I have been learning Chez Scheme over the past 6 months and had previously written about getting started with Chez Scheme and Emacs on macOS and Windows. For this post, I've copied the text of the previous post and updated only the components that needed to be changed to work with Ubuntu.

Chez Scheme


I installed Chez with APT.

$ sudo apt install chezscheme

Alternatively, you can build it from source with the following commands:

$ curl -L -O https://github.com/cisco/ChezScheme/releases/download/v10.0.0/csv10.0.0.tar.gz
$ tar -xf csv10.0.0.tar.gz
$ cd csv10.0.0/
$ ./configure
$ make
$ sudo make install


To launch the Chez REPL, open Terminal and type scheme [2].

Test the REPL with simple expression.

> (+ 100 10 1)

The REPL has several nice features including:

  • Navigate through previous expressions with the up and down arrow keys.
  • Autocomplete functions and paths with TAB.
  • Write and edit multi-line expressions.
> (define (example x y z)
    (if (> x 0)
        (+ y z)
        (- y z)))
> (example 1 2 3)

When navigating through previous expressions, only the first line of a multi-line expression is shown. To see (and edit) all lines, type CTRL+L. In the middle of an expression, RET creates a new line; to execute an expression from the middle of an expression, use CTRL+J.

Library Directory

Chez does not come with a package manager, but there are 3rd-party options, e.g., Akku. In this post, though, I will describe manual package management.

library-directories returns the directories where Chez looks for libraries.

> (library-directories)
(("." . "."))

The "." indicates that Chez is looking in the current directory [3]. If you are using a project-based workflow, then you could include your dependencies in the current directory, perhaps in a lib folder. For a 'global' approach, I created a library directory at /home/username/chez-lib.

Before we go over where to stash that directory information, let's cover library extensions.

> (library-extensions)
((".chezscheme.sls" . ".chezscheme.so") (".ss" . ".so")
  (".sls" . ".so") (".scm" . ".so") (".sch" . ".so"))

These are the file extensions that Chez uses when searching the library directories.

I edited .bashrc to add information on library directories and extensions. From a Terminal window, open .bashrc with the gedit text editor.

$ gedit ~/.bashrc

These lines add a new directory to library-directories and a new extension to library-extensions.

export CHEZSCHEMELIBDIRS="/home/username/chez-lib:"
export CHEZSCHEMELIBEXTS=".sc::.so:"

The : at the end is used to indicate that the new entries should be appended to the existing entries. Remove the : to replace the default values with the new entries. After saving .bashrc, you need to logout from your user account and login again to make the changes to .bashrc permanent. For your current session, though, you can source .bashrc.

$ source ~/.bashrc

Now, from a Chez REPL, we can see the effect of our changes.

> (library-directories)
(("/home/username/chez-lib" . "/home/username/chez-lib")
  ("." . "."))
> (library-extensions)
((".sc" . ".so") (".chezscheme.sls" . ".chezscheme.so") (".ss" . ".so")
  (".sls" . ".so") (".scm" . ".so") (".sch" . ".so"))

If we have a library at home/username/chez-lib/srfi/s1/lists.sls, then we import the library with (import (srfi s1 lists)), i.e., you pass the components of the path to import. If you can't import the library, look at the library call at the top of lists.sls, for example, because that will give you a clue of where the library expects to be placed in library-directories.

> (xcons 1 2)
Exception: variable xcons is not bound
Type (debug) to enter the debugger.
> (import (srfi s1 lists))
> (xcons 1 2)
(2 . 1)


Emacs is the standard editor for writing Scheme code. I'm slowly becoming more comfortable with Emacs, but I'm far from proficient.


Emacs is available through the Ubuntu Software Manager. You can also install it through the Terminal with

sudo apt install emacs-gtk

Basic Usage

The power of Emacs is in the keyboard shortcuts and customization. I'm too early in my journey to have unlocked much of that potential. When you are browsing info on Emacs, you will see shorthand for referring to keyboard combinations, e.g., C-x C-f corresponds to CTRL+X followed by CTRL+F. The other important key is the meta key with M as the shorthand. The default meta key is ALT. Similar to .bashrc, Emacs can be customized through commands saved in the .emacs file [4].


Geiser is a package that provides the ability to run several different Scheme implementations from within Emacs. We can install Geiser through MELPA.

Open Emacs, enter C-x C-f to find a file, and type .emacs at the prompt and add the following to the top of the .emacs file.

(require 'package)
(add-to-list 'package-archives
             '("melpa" . "http://melpa.org/packages/") t)

Save .emacs and restart Emacs. Then type M-x followed by package-refresh-contents. If that is successful, you will see the message Package refresh done in the minibuffer. To install Geiser, type M-x and then package-install. In response to the Install package: prompt, type geiser and hit return. You will also need to install geiser-chez following the same package installation steps. Lastly, you need to add (require 'geiser-chez) to the .emacs file.

The Chez REPL is launched through Emacs with M-x followed by run-chez. You can navigate through the previous expressions with ESC+P and ESC+N. Multi-line expressions, autocomplete, and syntax highlighting are also supported.


In Emacs, there is an option to highlight matching parantheses, which I find very helpful. Select Options/Highlight Matching Parantheses and then Options/Save Options. I've also started using company-mode for text completion. I was also pleased to discover that reindenting lines in Emacs is as simple as selecting the section to indent and pressing TAB.

Add the following lines to your .emacs file for scheme-mode to recognize the .sc and .sls file extensions that are used with scheme code.

(add-to-list 'auto-mode-alist
             '("\\.sls\\'" . scheme-mode)
             '("\\.sc\\'" . scheme-mode))

In addition to using TAB to reindent lines in Emacs, my other most used keyboard shortcuts are for executing, commenting, and selecting code. To execute the code in an s-expression, place your cursor at the end of the s-expression and type C-x C-e. If the executed code displays any output, it will be shown in the minibuffer and not the REPL [5]. To evaluate several s-expressions, highlight the region and type C-c C-r. To select an s-expression, place your cursor at the beginning of the s-expression and type M-C-space (where space is the space bar). I've done a lot of fumbling around trying to select s-expressions by dragging the cursor with the mouse so I was excited to discover this last one.

[1] During installation Ubuntu identified the apparent problem as related to numerous partitions from previous operating systems and provided the option to completely erase the old operating systems before installing Ubuntu. I'm sure that would also have been possible with FreeBSD and Debian, but Ubuntu made it easier to diagnose and address.

[2] You can also type petite to launch Petite Chez Scheme. See here for more information on the differences between Chez Scheme and Petite Chez Scheme.

[3] You can find the current directory by with current-directory.

[4] More complicated file structures for customizing Emacs are possible, but my proficiency with Emacs is not at that level, yet.

[5] If you want the output of the code displayed in the REPL, you will have to copy and paste it to the REPL (AFAIK).