CHICKEN for Emacs Lisp programmers

  1. CHICKEN for Emacs Lisp programmers
  2. Programming paradigms
  3. Lisp-1 vs. Lisp-2
  4. Dynamic vs. lexical binding
  5. Truthiness
  6. Equality predicates
  7. Control flow
    1. Sequencing
    2. Simple conditionals
    3. Generic conditionals
    4. Specialized conditionals
    5. Pattern matching
    6. Simple loops
  8. Custom types
  9. Standard library
  10. Modules
  11. Editor integration
  12. Buffers vs ports
  13. Communication with the outside world
  14. Macros
  15. Tooling
  16. Application/library development
  17. Community

If you're here, chances are that you want to go beyond Lisp as an extension language and use a general-purpose Lisp dialect. This guide has been written by the author of a few outrageous Emacs packages who happens to write more serious projects in CHICKEN.

Programming paradigms

Emacs Lisp is a fundamentally imperative language while Scheme leans towards the functional side of things. Nevertheless, it's possible to extend both towards other paradigms and steal APIs as seen in other languages. The most popular Emacs packages for this purpose happen to be cl-lib and dash.el, to get a taste of what's possible with CHICKEN, check out the egg index, specifically the "Language extensions" and "Object-oriented programming" sections.

Lisp-1 vs. Lisp-2

A more accurate title for this section would be "Lisp-1 vs. Lisp-n" as Emacs Lisp has more than two namespaces (faces live in symbol plists and can therefore not collide with other symbols). The Lisp-1 vs. Lisp-2 debate revolves around the question whether it should be possible to define a variable with the same name as a function without collisions. Lisp-1 dialects (such as Scheme, Clojure and Picolisp) disallow this whereas Lisp-2 dialects (such as Emacs Lisp and Common Lisp) allow it. This design decision leads to a number of differences, the most important one being how named functions are passed as values:

(mapcar '1+ '(0 1 2)) ;=> (1 2 3)

The equivalent example in CHICKEN looks a bit different:

(map add1 '(0 1 2)) ;=> (1 2 3)

If you haven't spotted it, unlike in Emacs Lisp, add1 isn't quoted. In fact, quoting it yields an error:

(map 'add1 '(0 1 2))
;; Error: call of non-procedure: add1

add1 ;=> #<procedure (add1 n1097)>
'add1 ;=> add1

It turns out that evaluating 1+ in Emacs Lisp gives one a "Void variable" error while evaluating add1 in CHICKEN returns a procedure. From this one can deduce that mapcar in Emacs Lisp looks up the function value of the 1+ symbol while map in CHICKEN looks up the value of the add1 symbol. For this reason CHICKEN doesn't offer a symbol-function procedure, you'll have to (eval 'add1) instead or better, pass the procedure instead of its quoted symbol.

There are more differences arising from this:

Dynamic vs. lexical binding

Emacs Lisp defaults to dynamic binding for a number of reasons, the most important one being that doing so allows one to redefine nearly anything temporarily. While this is convenient in a text editor full of questionable defaults, it has the downside of making for slower compiled code and not allowing for lexical closures (which can be emulated with the lexical-let macro or by splicing in externally accessed values into a lambda).

In CHICKEN lexical binding is the default which means that lexical closures are created when closing over free variables in a lambda. If you do need variables that behave as if dynamically bound, you can use the fluid-let macro or better, define parameters.

Truthiness

Emacs Lisp has t as the canonical true value and nil as the canonical false value which doubles for the empty list, too. While it may look bizarre that () evaluates to nil, it's convenient to check this way whether a list is empty and to create a list by appending a value to nil. In the rare case when you need to tell nil apart from a non-empty list, you use the consp predicate over listp.

In CHICKEN, the truthiness values are #t and #f for true and false, with the empty list being a separate, non-falsy value. In other words, everything except #f is truthy. Code that checks for the empty or non-empty list must use the null? and pair? predicates. While this makes for clearer code, it's a bit more annoying to type than the Emacs Lisp equivalent. Similarly, (car nil) in Emacs Lisp will happily give you nil while doing (car '()) in Scheme yields an error.

A separate issue is that in Emacs Lisp nil is frequently used as placeholder for the undefined value. (if nil t) evaluates to nil, the result of (if #f #t) however gives you the undefined value in CHICKEN. If you wish to use the result of this in a different procedure, it's better to be explicit to avoid incomprehensible errors.

Equality predicates

Just like in Emacs Lisp, there's a plethora of equality predicates in CHICKEN. You'll typically want to use = (numbers), eqv? (identity) and equal? (structure). Depending on the situation, type-specific equality predicates might be useful, such as string=? and char=?.

Control flow

Sequencing

progn in Emacs Lisp groups a body and evaluates to its last form, the CHICKEN equivalent is begin. If you ever feel like you need prog1, either use begin0 from the miscmacros egg or (let ((result (first-form))) ... result).

Simple conditionals

if in Emacs Lisp has a then form and a else body, in Scheme both branches must be a single form and are indented by four spaces each. when and unless are provided in CHICKEN as well.

Generic conditionals

cond looks slightly different in Scheme as the else case is specified by the else keyword. Furthermore, it's possible to transform the result of a test with the => syntax, see R5RS for further details.

Specialized conditionals

Emacs Lisp doesn't offer case in its core, for that one people typically resort to cl-case (from cl-lib) or pcase (without using its pattern matching). CHICKEN has case and select which check whether an expression is eqv? to any of a given set of keys, with the difference between case and select being that the latter evaluates the keys to allow using variables.

Pattern matching

Emacs Lisp has pcase whereas CHICKEN has matchable. Make sure to check out Moritz Heidkamp's blog post on it.

Simple loops

The fundamental looping construct in Emacs Lisp is while, with throw and catch allowing one to terminate control flow early. Scheme does not have a direct equivalent as recursion and mandatory TCO are sufficient to express iteration in an elegant manner. When combined with the named let form, a simple while loop can be written as (let loop () (when (...) ... (loop))).

Further helpers for iteration can be found in SRFI-1, most importantly for-each (iterate across list), do (imperative iteration) and fold (generic iteration). While eggs providing complex loop macros do exist (including one implementing the infamous LOOP macro from Common Lisp), they aren't used much.

Custom types

Emacs Lisp doesn't really have records. cl-defstruct emulates them by sticking values into a vector, with the downside that you can't define a printer method for your new type or reliably figure out the type of a "struct". In CHICKEN, you get not one, not two, but three(!) ways to define records. You'll most likely want to stick to define-record, alternatively define-record-type (as seen in SRFI-9) if you find define-record too magic or want to specify alternative names for getters and setters. Printers are taken care of with define-record-printer. Finally, there's SRFI-99 records which you must install separately, these give you a procedural interface which allows you to do fancy stuff like inheritance and introspection.

As for CLOS-like libraries, the egg index has an entire section for these. COOPS seems to be the best, but I can't vouch for it. If none of them satisfies your needs, it's easy enough to roll your own (which would explain the wealth of options).

Standard library

Consult the following before rolling your own:

That being said, don't be afraid to roll your own. If you're considering to add one more dependency to your project, it might make more sense to bundle one function definition. It might also happen that there is no egg for what you want to use, so you might get to write your own and share it with the rest of the world!

Modules

While you can get away with writing scripts without worrying about those, it's recommended to use modules for bigger programs. This allows the compiler to find more mistakes for you (such as referring to unknown identifiers) and more importantly, to have namespaces when using the module from another file. These namespaces can be thought of as a set of identifiers which you create by using the import and use syntax. In other words, unlike in languages like Clojure, namespaces aren't like hashtables that you can modify freely after instantiation. It's possible to use extra qualifiers to selectively import or rename identifiers, see the manual for details.

Editor integration

The integration of Emacs Lisp into Emacs is unparalleled and one of the reasons why you'd want to pick this editor for your daily work. CHICKEN isn't nearly as integrated, for better (no chance for your hackery to corrupt your editor's internal state) or worse (more friction). Here's a list of possible workflows with Scheme and Emacs:

If you're using Vim, the wiki provides helpful tips. For other text editors, the first workflow will have to do.

Buffers vs ports

Buffers are the ubiquitious abstraction in Emacs. Whenever confronted with a text processing problem, one loads up the text into a (temporary) buffer, then navigates/edits it as needed to solve the task. CHICKEN's closest thing to this is the ports abstraction used for I/O, be it with strings, files, sockets, whatever. Refer to The R5RS standard for details.

Communication with the outside world

To interact with other programs, you have the following options:

Macros

Macros in Emacs Lisp are seemingly simple until you learn that they're unhygienic and require careful thought to not accidentally capture identifiers. Even more so if you do want to capture some of them and ensure nothing else is affected. CHICKEN offers more than one macro system, the default option is syntax-rules which combines pattern matching with hygiene, at the price of not being able to inject identifiers. Will Donelly wrote a good tutorial on basic usage, JRM's syntax-rules primer covers intermediate and advanced usage. Additionally to that CHICKEN comes with explicit and implicit renaming macros that allow you to inject identifiers on an opt-out or opt-in basis. Here's another tutorial.

You might be wondering now whether to bother writing Scheme macros at all. In fact, things like the with-resource class of macros with a body argument aren't encountered often, instead procedures like with-input-from-string take a so-called thunk, a zero argument procedure containing the body argument. Writing your own is simple thanks to functions being first-class and even encouraged to do as it allows for greater composability.

Tooling

While Emacs Lisp tooling is weird, there are many useful things provided by vanilla Emacs and third-party packages, with a good level of editor integration. The situation isn't nearly as good with CHICKEN, your choice of tools is limited to the following:

Additionally to that you can attack the problem from the C side of things and use tools like gdb and valgrind to debug deeper-seated failure and memory leaks.

People seeking for a more IDE-like experience are encouraged to give Geiser or the slime egg (to be ported) a try.

Application/library development

The obvious way to create an application is by using csc to combine all input files as needed to one executable. This makes for less work than by using Makefiles, but can be tricky to get right, especially if you need to reference resource files. In this situation it can be of advantage to structure your application as an egg and use chicken-install to install it. Static linking got easier in CHICKEN 5 as the entire system and eggs are built both in dynamic and static versions. The only issues you'll be running into is bindings to external libraries.

The vast majority of eggs consists of libraries pulled in from distributed sources. There are tutorials about writing and releasing eggs. If you want to study existing eggs for common solutions, I recommend downloading a local mirror with henrietta-cache. Documentation isn't kept in the sources (for the lack of docstrings and a culture discouraging entanglement of documentation and code) and goes to the wiki instead.

Community

The Emacs community is huge, but with a lot of code resting on the shoulders of a few skilled people. Most development activity revolves around the MELPA repository, where stability and the semblance of a sane development model is the exception. The core team does primarily use the emacs-devel mailing list for communication, but isn't particularly inviting to newcomers.

CHICKEN's community is considerably smaller, but more welcoming. The egg index serves as the central hub where one can install eggs from. If you encounter an egg outside it or write your own, you can simply run chicken-install in its directory. You can participate by hanging out on the #chicken channel and getting involved on the mailing lists. To get an idea how you can help out, check out the contributing and wishlist wiki pages.