A performance-oriented Lisp-like language where I can have my cake, and eat it (too)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7.8 KiB

Cakelisp vs. Other Languages

Naughty Dog

Game Oriented Assembly Lisp was my chief inspiration.

In Naughty Dog's Uncharted (and possibly other titles), Scheme is used to generate C structure definitions (and do various other things). See Jason Gregory's Game Engine Architecture, p. 257. See also: Dan Liebgold - Racket on the Playstation 3? It's Not What you Think!


Some Lisp-family languages with active development which transpile to C, or compile to native:

  • Carp: Performance-oriented. Requires writing bindings. See Language guide

  • Chicken scheme: Transpiles to C. Has heavyweight C function bindings, garbage collection

  • ECL: Embeddable Common Lisp

  • Ferret: Lisp compiled down to C++, with optional garbage collection runtime

  • Dale: "Lisp-flavoured C". Requires function bindings, uses LLVM (large dependency)

  • lcc: Generate C from SBCL-powered Lisp

  • Jank: LLVM JIT compile Clojure code

Lisp-style languages for other non-GC languages:

  • Liz: Transpiles to Zig

  • GameLisp: Seamless Rust API, no GC

The following I believe have little or no activity, implying they are no longer supported:

  • Bone Lisp: Lisp with no GC. Creator has abandoned it, but it still gets some attention

  • Thinlisp: No GC option available. Write your stuff in CL using the cushy SBCL environment, then compile down to C for good performance

  • Shiv: Notably has a non-S-expr wrapper that still gets tokenized into S-exprs

Additional related works:

Compared to C-mera

The most similar thing to Cakelisp is C-mera. I was not aware of it until after I got a good ways into the project. I will be forging ahead with my own version, which has the following features C-mera lacks (to my limited knowledge):

  • Automatic header file generation

  • Powerful mapping file for debugging, error reporting, etc. on the source code, not just the generated code

  • Scope-aware generators. You can make the same generator work in multiple contexts (at module vs. body vs. expression scopes)

  • Intended to support more than "just" code generation, e.g. code to support hot-reloading and runtime type information will be created

  • DONE I will likely add some global environment that will be modifiable by any modules in the project. This is useful for things like automatic "command" function generation with project-wide scope

  • Built-in build system can manage entire project build process

Features C-mera has that Cakelisp doesn't:

  • Access to Common Lisp macros, which is a huge swath of useful code generators

  • Support for generating other languages. At this point, the C/C++ output is hardcoded, and would be a bit painful to change

  • Multiple contributors and years of refinement

  • It's done, and has proven itself useful

Implementation language pros and cons

Cakelisp is written in C/C++ while C-mera is written in Common Lisp.

This is good and bad: the advantages of writing it in C/C++ are:

  • It is fast; no garbage collection pauses etc. to deal with. This might not actually be the case if intermediate compilation and loading of generators and macros ends up being slow

  • C++ is what I'm most familiar with; it would've taken me much longer in Common Lisp simply because I'm inexperienced in it

  • Cakelisp does not depend on a runtime (except for the C runtime), which means it would be possible to integrate the Cakelisp compiler into the project being compiled itself. This could be pretty handy for in-process self-modification thanks to the hot-reloading features

  • Macros and generators can be written in the same language being generated (and in Cakelisp, of course, because Cakelisp itself can load its own generated code to expand itself)

The bad things:

  • There's no macro-writing library to draw from (macros which help write macros)

  • Like previously mentioned, macros and generators need to be converted to C/C++ and compiled by an external compiler to be executed, whereas Common Lisp would make this whole process much easier by natively supporting macro code generation and evaluation

Compared to Scopes

Scopes was also made as an alternative to C++ for game development.

I appreciate the existence of Scopes, and it provides some interesting features (esp. GPU support). Scopes also provides the tight 3rd party C/C++ build system support that I haven't seen any other language offer (besides Scopes and Cakelisp).

In short, Scopes is trying to be its own language, with much more radical ideas and features. Cakelisp is a much more incremental improvement over C.

We do have different approaches to solving similar problems, so I recommend anyone give both a deep look before deciding. Here are some things which I think make Cakelisp more suitable to me, personally:

  • Cakelisp is very lightweight in terms of installation. Windows has no dependency other than MSVC, and is a one-click batch script after you have MSVC installed. Linux is a single shell script execution, assuming your system already has g++ (which I think is a safe assumption, but I haven't done a survey or anything)

  • If you are porting to another platform (e.g. game console), you may also need to port the Scopes runtime compiler, unlike Cakelisp, which doesn't run on the target hardware. In Cakelisp, the compilation stage is separate from the runtime, unlike Scopes

  • Scopes uses a mixed syntax instead of being strictly S-expressions. I'm somewhat hard-line in applying S-expressions, e.g. I don't have [] for function signatures like Clojure. I figure if I'm going to be using a constraining syntax for consistency, I'm going to be consistent!

  • Cakelisp is explicitly typed. I personally am not a fan of C++'s auto; I like seeing types because it helps me imagine what's actually going on much better, and better know what's possible given the existing arguments/variables. Scopes allows you to be explicit with your types, but doesn't require it

  • Cakelisp to C++ is much closer than Scopes. This means a shorter ramp-up time for an existing C++ programmer to adapt to a Cakelisp codebase

  • Cakelisp outputs clean, human-readable C/C++. This makes debugging support solid as well as gives an out if you decide you would rather switch back to straight C/C++

Overall, I'd say Scopes is a much more heavyweight set-up. It has more features and more time has gone into it. If you'd rather go for something more minimal, Cakelisp may be a better option.

Other languages of interest

  • Jai: Jonathan Blow's talks on Jai have been a source of ideas and inspiration to continue

  • V: Cakelisp has different goals, but V acts as another reference for how fast things should be, and how easy they should be to set up. I agree with their assessment of the validity of using C as a backend instead of native code generation

  • Zig: I'm primarily interested in the ease of cross-compiling which Zig provides, plus Andrew seems like a good guy