Here are a list of things I did with Cakelisp which I thought showcase its niceties. Nothing is too small, because small things can make a big difference!
They’re such an annoyance to manage, which causes me to not want to create separate files. Cakelisp still retains the concept of declarations vs. definitions via local
and other keywords. This gives you the control to expose limited parts of a module, without having to declare those parts in a separate file.
I made AutoTest.cake, which automatically finds functions preceded by test--
and compiles them all into a single main
function, calling them one by one. It’s a dead-simple way to embed high-level tests.
cakelisp --execute
makes it possible to run .cake
files as if they were simple scripts. Cakelisp will handle all the building and caching for you. No more error-prone and tedious “compile, link, run, make change, compile…” which you get with C/C++ files.
With a simple "hello world"
application, I found Cakelisp to be about half as fast as Python to build and run the program. You only pay this price when the code changes, however. If there are no changes, the Cakelisp version evaluates and executes an order of magnitude faster than Python (which isn’t saying much, admittedly - Python is known for being slow).
I also check for “shebang” #!
and ignore the line if found on the first line in a .cake
file. This allows users to execute their scripts via e.g.:
chmod +x MyScript.cake
./MyScript.cake
…when MyScript.cake is:
#!/usr/bin/cakelisp --execute
(c-import "<stdio.h>")
(defun main (&return int)
(printf "Hello, execute!\n")
(return 0))
I got rid of external build systems! I used to use Jam (which is still used to build Cakelisp itself). Now, Cakelisp can handle it all internally.
This has made it much more fun to program with, because all I need to do to use a new dependency like SDL is (import "SDL.cake")
. It’s the responsibility of the module to know how it needs to be built, which makes all that complexity stay there instead of spreading into every new project. I got this idea from Jai.
In GameLib, I have simple modules for Tracy, SDL, and Ogre already set up.
I have not yet seen the need for partial builds. Partial builds make the Cakelisp internals much more complicated. So far, the high-level performance of Cakelisp has me optimistic that I will be able to continue fully parsing and evaluating the files without resorting to partial loading. Note that this does not include compiling and linking, which are handled by external processes, and dominate the total time when building clean.
Build configurations are constructed “lazily”, meaning all you need to do to create a new configuration is make the necessary changes to the environment and add a unique label.
For example, a build configuration Debug-HotReloadable
could be constructed via:
- Overriding the build command via
(set-cakelisp-option build-time-compile-arguments ...)
, adding debug flags.(add-build-config-label "Debug")
and that’s all needed to create the Debug configuration - Importing
HotReloadingCodeModifier.cake
, which adds(add-build-config-label "HotReloadable")
. This is important because hot-reloadable builds are different from regular builds - they expect their variables to be initialized by the loader, and a dynamically linked library is created instead of a standalone executable
I like this solution because it gives the user the ability to make their configurations as complex as they want, without having to face any additional/introductory complexity. For example, we could add processor architecture, operating system, and C standard library selections to our configurations, if necessary. A/B comparisons between runtime performance could also be done easily, just by adding a label to the alternate.
If you are just writing a quick one-off script, you need not worry about configurations at all.
Because all options must be provided in Cakelisp files, it encourages composable configurations. For example, we could take the Debug
configuration from above and put it in Config_Debug.cake
, then import it and build the program via cakelisp Config_Debug.cake MyProgram.cake
.