\( \newcommand\D{\mathrm{d}} \newcommand\E{\mathrm{e}} \newcommand\I{\mathrm{i}} \newcommand\bigOh{\mathcal{O}} \newcommand{\cat}[1]{\mathbf{#1}} \newcommand\curl{\vec{\nabla}\times} \newcommand{\CC}{\mathbb{C}} \newcommand{\NN}{\mathbb{N}} \newcommand{\QQ}{\mathbb{Q}} \newcommand{\RR}{\mathbb{R}} \newcommand{\ZZ}{\mathbb{Z}} \)
UP | HOME

Apologia

Table of Contents

1. Programming Language

I'm still debating the programming language I should use for this endeavor. Among the choices, it seems that Lisp is best prepared for my task:

  • Lisp has no syntax, so language extensions are natural, and everything has the same "sentence structure" (verb subject object)
  • Macros make language extensions on par with special forms
  • Symbolic computation can be handled easily and naturally (e.g., the pattern matching code and algebraic simplifier couldn't be easily done in non-Lisp)
  • Lisp allows for Bignum computations, so integer and rational arithmetic is precise

At the same time, I wish to use IEEE floating point (which seems to rule out Common Lisp, except sbcl and possibly a few other implementations). The easiest solution is to use SBCL, and not worry about the details. One way to resolve this problem is to implement my own floating point arithmetic with arbitrary precision algorithms, or fall back to using the GNU Scientific Library, or something similar. I suppose I could write some unit tests to check if the system uses IEEE floating point, so we can be certain.

FEMLISP look interesting, it's a Common Lisp library for solving partial differential equations via finite element methods.

Scheme has the disadvantage of being interpreted (read: slow) and limited support for macros. Guile looks like it remedies these problems by having the programmer implement the critical parts in C, then embed a Scheme interpreter/VM in the C code. I like this approach better.

  • One problem with Guile is that complex numbers are necessarily inexact.
  • On the other hand, arithmetic operators can be extended as if they were generic functions in Guile.

1.1. Dialects

Scheme is probably the first dialect most people encounter, since it's used in the textbook Structure and Interpretation of Computer Programs ("SICP"). The Scheme dialect has the hall-mark of minimalism. In fact, it's so minimal that the reader implements a scheme interpreter in chapter 4 of SICP.

Common Lisp emerged from the 1980s after Lisp 1 fractured in a Cambrian explosion of dialects. Common Lisp sought to unite them all into one common language. Unlike Scheme, Common Lisp compiles to machine code, has a comparatively large number of functions (Scheme fits on a cocktail napkin, Common Lisp requires a bit more space). This dialect also has macros, which Scheme did not until relatively recently.

Emacs Lisp is a cousin to Common Lisp, because both descend from Maclisp (a shared Cambrian ancestor). They share similar syntax, and are about as distinct as Latin to modern Italian, or Urdu and Farsi.

Clojure is the newest dialect, which borrowed as heavily from Haskell as from other Lisps. It uses immutability, emphasizes functional style, and runs on the Java Virtual Machine or in the browser as Javascript. While it has marginally cleaner syntax (using not just parentheses but also square braces [...] for vectors, and braces {...} for hashmaps), its STM memory model leads to accidentally bloated software all too easily.

1.2. Flaws

The biggest flaws that come to mind are figuring out which dialect to use. Each of them have their quirks and shortcomings.

1.2.1. Scheme

1.2.1.1. No Canonical Implementation

One of the features of Scheme's minimalism is that it's not hard to write your own Scheme interpreter. The problem: there are dozens of Scheme implementations.

MIT/GNU Scheme
What SICP and SICM use
Guile
GNU's official Scheme implementation for extensible usage
Racket
Flashy new Scheme implementation
Chicken
Compiles Scheme to C
Gambit
Another Scheme-to-C compiler, plus an interpreter
Chez Scheme
An older implementation, among the fastest

And on and on and on. If starting from scratch, it's unclear which one to pick, they're all decent choices. But each one has language-dependent variations of expected features — e.g., modules are either unimplemented or use different syntax.

Although I really like Guile, I'm afraid it may be unstable and remove functionality I'd use.

1.2.2. Common Lisp

Probably the biggest complaint is how baroque the language is (for example: setf and setq are both included, but only one is really needed). Equality testing also exemplifies this problem (we have = for numbers, eq for pointers, eql for pointers or numbers, equal, and equalp, but we'll need to roll our own if we want equality of CLOS instances [objects]).

1.2.2.1. Floating Point Arithmetic isn't part of the Standard

The Language standard was written before IEEE 754 floating point was finalized (or written), so this is a serious shortcoming with Common Lisp for numerical analysts. Since it's not part of the standard, it's not violating the standard to use IEEE floating point arithmetic. But it's just compiler dependent.

  • SBCL uses IEEE-754 on x86
  • ABCL uses IEEE-754 if the JVM running it uses the standard

1.2.3. Clojure

Slow startup with repls and bloated software are two of the biggest issues facing Clojure.

1.2.3.1. Memory Usage

Clojure's memory usage can easily run out of control. The STM memory model requires greater diligence when programming, otherwise duplicate data can be created and not freed all-too-easily by accident. For example, holding onto the head of a cons will keep the rest of the list, even if the rest of the list is not needed or used. The JVM garbage collector doesn't seem to handle this memory model all too well, either.

This frequently leads to rewriting the code in Java.

1.2.3.2. There is no standard

Unlike Scheme and Common Lisp, there is no standard. So, is mapcat doing what I expect? Erm…maybe?

1.2.3.3. JVM Historically didn't have IEEE 754 floating point

This may be a subtle source of bugs, but floating point support is comparatively modern. For old-timers using JVM 6 (or whatever), this is problematic.

1.3. Further Reading

Last Updated 2021-04-11 Sun 17:48.