< Innovations

Lamb Lisp - A Scheme Implementation for Embedded Controls

Overview

Lamb Lisp is an industrial-quality implementation of the Scheme dialect of the programming language Lisp, optimized for microprocessors.  It’s currently under development on the Espressif ESP32S3 AI-enhanced processor.

LISP is the original language of artificial intelligence, and is the second-oldest programming language still in use (after Fortran).

The Scheme variant of Lisp has many features familiar to programmers in other languages, such as lexical scoping, duck typing, and interactive programming, as well as advanced features such as tail recursion, first-class procedures, and dynamic code generation.

Lamb Lisp additionally adds a compact interpreter-centered architecture, hashed symbol table and environment frames for speed, and adaptive real-time garbage collection for predictable loop times with minimal jitter.

Lamb Lisp has a full Arduino API compatibility layer, and a simple interface for additional application-specific functions written in C++.

Lamb Lisp code can be downloaded over-the-air, incrementally, without requiring reboot.

Mascot

Details

Lamb Lisp is an implementation of the Scheme R7RS dialect of Lisp. LISP is the original language of artificial intelligence, and is the second-oldest programming language still in use (after Fortran).

Lamb Lisp has additional features optimized for microprocessors that are sensing or controlling the real world. Whether operating a camera, sniffing the air, or operating mechanical devices, microprocessors are at the heart of every control system in the 21st century.

Lamb Lisp provides an Arduino-compatible API for I2C (Wire) digital and analog pin I/O, and for network connectivity.

Lamb Lisp puts the entire language system on the microprocessor, on top of a base layer in C++. The base layer is much smaller than a typical all-C++ application, leaving more room for the Lamb Lisp application layer.  This also means fewer opportunities for bugs in the base layer, and fewer reasons to require an update.

No external tool chain (gcc-based or otherwise) is needed to update Lamb Lisp code. Use any available communication channel to read in new or replacement Lamb Lisp code into the file system. Organize the Lamb Lisp code into dictionary entries or file system entries, and they can be replaced incrementally, on the fly, without full over-the-air update and without requiring a reboot.

Lamb Lisp’s optimized architecture utilizes pre-treated code (i.e., not fully compiled) to reduce the “semantic distance” between the application as-computed and the idealized executing application, without requiring a native code generator and off-board compilation.  Lamb Lisp uses a “direct-connect” technique, in which high-speed operations implemented in C++ are executed through pointers directly embedded into Lisp objects. In this way the semantic distance between the Lamb Lisp layer and the machine layer can be optimized, by implementing time-critical Lisp layer functions in C++. All of the Lamb Lisp language primitives are implemented this way, and it is possible to add additional Lamb Lisp functions implemented in C++. They are treated as any other first-class function.

 

Lamb Key Features

  • Designed to Scheme R5RS and R7RS specifications.
  • Optimized for microprocessors. ESP32S3 is the current development platform.
  • Interpreter-oriented architecture for compact high performance.
  • Arduino-compatible interfaces for digital & analog I/O, I2C, WiFi, etc.
  • Adaptive incremental garbage collector for uniform loop times.
  • Selectable run-time packages (math, ethernet etc) to control program size.
  • Incremental updates over-the-air without rebooting.
  • Easy addition of new C++ functions. All Lamb buil-in functions are implemented this way.
  • Leverages existing gcc-based tool chain, C runtime.

Benefits of the Interpreter-oriented architecture.

  • Interpreter is compact. Direct-connect technique provides high performance.
  • No need for full over-the-air updates and reboot. Just download the portions of code required.
  • Lisp code can be stored in file system, purged, reloaded etc, providing a virtual memory capability.
  • No off-board compiler/linker are required to run Lamb Lisp code. Just download the source code to the device.
  • Incremental, adaptive garbage collector recycles memory automatically and predictably, without worst-case pauses. Python and C++ can’t do this, which is one reason Python has not succeeded in embedded systems; C++ programmers tend to avoid garbage collection altogether.
  • The base Lamb Lisp machine, written in C++, is smaller than an all-C++ application, with fewer features, fewer bugs, requiring less memory and fewer updates than a full C++ application.
  • Arduino compatibility, with easy addition of extensions.
  • No “foreign functions” as in other implementations. Functions written in C++ can be natively incorporated into the symbol table, or called as part of a lambda function.

Other implementations

This project was started after a review of available options for embedded Lisp/Scheme came up with no candidates. General objections to existing Lisp/Scheme implementations were:

 

  • Required off-board compilation.
  • Out of scale for a microprocessor.
  • Non-portable hardware tricks (e.g., using the bottom bits of addresses as flag bits).
  • Non-Scheme lisps (e.g., no lexical scoping or tail recursion).
  • Bad fit on the interpret<->compile spectrum (e.g, low-level interpreter, separate execution stack).
  • Lisp-on-Python, Lisp-on-JavaScript, and other such proofs.
  • Stop-the-world or copying garbage collectors.