One of the things that always irked me about my EPUB reader was
only way one can script a browser view these days. I’ve always wanted
to give the SPOCK compiler a try to see how well it does compared to
ClojureScript, so this project gave me the perfect chance to cure me
of delusions. After an evening, I got it running, it took me a few
more evenings to iron out bugs introduced by the conversion and add a
new bugfix. Here’s a list of observations made in that time:
- Both versions are about the same size, with the Scheme version being
a bit shorter (which is mostly closing parentheses not going on a
separate line). I’d expect a greater difference in favor of the
Scheme version if I had any noteworthy business logic embedded into
this, but alas.
- Debugging got significantly harder as there is no REPL, no source
maps integration and no debugger for the Scheme code. I’ve had to
do with classic printf-Debugging, except that it looked more
like (%inline "console.log" (jstring (string-append "Foo "
- While there is documentation (which includes a few working
examples), it isn’t clear how to use the compiler to its fullest
abilities. I’ve resorted to compiling all kinds of code and staring
at the compiler output to see what works and what doesn’t. This
experimentation revealed that you’ll want to use %inline for
most interop, with a bit of dot syntax for property access.
ClojureScript beats SPOCK easily in this aspect, including its
#js reader macro and conversion macros from/to JS data
- Error reporting is extremely basic, with some errors being silent
and merely preventing code from executing any further.
- The supported language is restricted to R5RS with a few useful
macros and JS-specific helpers. In other words, while you might
off writing your own helpers as needed.
- Tooling is simple and quick. Recompiling code is instantaneous,
it’s easy to see what part of your own code maps to the generated
parts. This is the only benefit I see in SPOCK over ClojureScript.
To summarize, if you want maximum comfort and features, go for
ClojureScript. The price you pay for it is significant friction while
developing, but other than that it’s pretty advanced. Personally I
keep things as simple and painless as possible.
I predict that Guile Emacs won’t lead to a significant increase in
packages written in Scheme for similar reasons. Much like in
browsers, the majority of Emacs Lisp usage doesn’t have complex
business logic and follows the principle of practicality over purity.
Perhaps it’s different for big projects like Magit or Evil, but even
these cases are doubtful to me, simply because they have higher
priorities than speculative rewrites that might as well kill the
project. I could keep rambling about my reasons for this assessment,
but that is better left for a separate blog post…
I do occasionally hold talks, mostly about Lisp-related topics. My
medium of choice is PDF, as generated by Org’s export with the Beamer
backend. When it’s demonstration time, Emacs isn’t nearly as simple
to adjust for comfortable viewing as a browser or terminal. My first
instinct was to look for a function that allows increasing the font
size, similar to C-+. It turns out that C-x C-+ is a thing,
however it’s not ideal as it only increases the font size of the
current buffer. A quick look at the sources reveals why:
:lighter (" " text-scale-mode-lighter)
(format (if (>= text-scale-mode-amount 0) "+%d" "%d")
text-scale-mode is implemented in terms of
face-remap-add-relative, a function describing itself as “Add a
face remapping entry of FACE to SPECS in the current buffer.”.
Funnily enough, both live in face-remap.el, probably because
scaling text is merely a demonstration of the buffer-local face
remapping capabilities of Emacs. While it’s a cute demo, it’s clearly
not what I’d want from a C-+ replacement, so I wrote an
alternative solution operating on the frame:
(defun my-alter-frame-font-size (fn)
(let* ((current-font-name (frame-parameter nil 'font))
(decomposed-font-name (x-decompose-font-name current-font-name))
(font-size (string-to-int (aref decomposed-font-name 5))))
(aset decomposed-font-name 5 (int-to-string (funcall fn font-size)))
(set-frame-font (x-compose-font-name decomposed-font-name))))
(defun my-inc-frame-font-size ()
(defun my-dec-frame-font-size ()
(global-set-key (kbd "C-+") 'my-inc-frame-font-size)
(global-set-key (kbd "C-=") 'my-inc-frame-font-size)
(global-set-key (kbd "C--") 'my-dec-frame-font-size)
This is a bit less hacky, but still disgusting. The code fetches the
font name (which curiously comes in the XLFD notation) from the frame,
converts it into an array, extracts the font size, manipulates it,
puts it back into the array, converts it to a font name and sets the
frame’s font to it. You can find this snippet and many more
in my init.org, so if you haven’t already, give it a look to find
In case you haven’t done it yet, I strongly recommend you to give
telnet nyancat.dakko.us a try. If you don’t have a telnet client
ready, head over to nyancat.dakko.us
instead and enjoy the pretty pictures.
I’ve wondered for quite some time whether it would be possible to run
the same thing on a SSH server. It turns out that it’s not too hard
to do as not only Kevin Lange’s creation can be run in a TTY, but
OpenSSH allows you to do something else than giving you a shell after
a successful authentication attempt.
First of all you’ll need to install and test the nyancat program:
% pacman -S nyancat
To restrict the impact of the public-facing service, I decided to
create a new user for it and run a separate SSH daemon with its own
config and service file:
% useradd -m -s /bin/sh anonymous
% cp /usr/lib/systemd/system/sshd.service /etc/systemd/system/nyanpot.service
% cp /etc/ssh/sshd_config /etc/ssh/nyanpot_sshd_config
Relevant changed bits in the service file:
ExecStart=/usr/bin/sshd -D -f /etc/ssh/nyanpot_sshd_config
The SSH config is a bit special as it locks out everyone who isn’t the
Match User anonymous
You can test the service with systemctl start nyanpot.service and
logging in (ideally from a different system) as the anonymous user.
If everything works fine, enable the service permanently with
systemctl enable nyanpot.service. My honeypot is available via
ssh email@example.com (PW: anonymous). Enjoy!
Update: Finally figured out the layout after digging a bit more
into the sources, it’s a QWERTY-UK (see
devices/rpi2/uspi/include/uspios.h). Looks like I’ll have to
modify the bundled USPI library to include a QWERTY-US layout
before I can make any progress on keyboard remapping in Lisp…
I believe I’ve found an even greater time sink than writing Lisp
interpreters for fun. Long time ago, I’ve read an encouraging blog
post on the future of the LispM, not expecting to find an
implementation of the ideas presented therein. Turns out I was wrong
about that. Meet Interim OS!
In case you’re wondering why you should possibly care:
- Small and readable codebase (most of the code is device drivers for
the Raspberry Pi)
- Simple to hack on
- Plan9-style APIs
- Minimal Lisp dialect
- Runs on your favorite desktop OS in hosted mode, that is, safely
contained to a terminal with the ability to spawn graphical windows
- Runs on bare metal (Raspberry Pi 2)
Getting it to run in hosted mode is simple enough, so I won’t explain
it here. Booting on bare metal however is a different story, so here
$ git clone https://github.com/mntmn/interim
$ cp interim/docs/interim-0.1.0-rpi2.tgz ./
$ bsdtar -xf interim-0.1.0-rpi2.tgz # cry me a river
% mkdir /media/boot
% mount /dev/sdXN /media/boot
% cp release-rpi2/* /media/boot/
% rm /media/boot/cmdline.txt
% umount /media/boot
- Plug in the SDHC card, a HDMI monitor and a USB keyboard
- Optionally: Plug in a network cable and/or a USB mouse
- Power up
You’ll be greeted by a “Welcome to Interim OS” and dropped into a
promptless shell. If you’re unlucky, the chosen resolution may be
unreadable, so feel free to retry this process a few times. The
keyboard layout is hardcoded and somewhere between QWERTY-US and
QWERTZ-DE, something I intend to fix soon. For basic usage
instructions, type (bytes->str (load "/sd/hello.txt")) and hit the
enter key. Happy hacking!
Update: There is a CL merge request that supports SBCL among
other implementations, as expected it beats the picolisp
implementation in speed by a factor of 2x. Still, I’m fine with being
second place :)
I’ve implemented MAL for the third time by now, this time in
PicoLisp, a language priding itself on its implementation simplicity.
While it clearly is a Lisp dialect, it has foregone a good amount of
classic Lisp design choices in favor of terse code. Despite this,
there are practical inclusions for writing application software, like
the GUI system and a distributed database implementation with a
Prolog-style query language. Other interesting features are an
unobtrusive OOP system, a FFI for C and Java, live debugging
utilities, pattern matching and more in a 1MiB tarball.
You might wonder why I’d be up for learning yet another Lisp dialect,
after having learned Emacs Lisp, Clojure and Scheme. Furthermore,
Scheme already claims to take minimalism as language design principle
and of course there are more obscure Lisp dialects, like Arc and
newLISP. I can only blame a friend who told me about this
fascinating talk given by the PicoLisp author demonstrating the
abilities of his programming language. The overall picture my friend
painted was that of a bizarro world where a crazy German ignored all
established rules for a Lisp dialect, walking the fine line between
insanity and practicability. Most surprisingly though was that he
used his own invention to write business applications and succeeded in
making a living off it. Naturally I was intrigued and kept PicoLisp
on my backlog of things to play with.
My implementation is a bit smaller than the Emacs Lisp one, is the
first one to actually make use of GNU readline and went for a purely
Lisp tokenizer as I couldn’t figure out how to use PCRE for this task.
It also appears to be the fastest one out of all Lisp family
implementations. This might change though once the “clisp”
implementation gains support for using SBCL instead of CLISP…
Regarding oddities, here’s an incomplete list:
- No lambda. You pass a quoted list instead.
- Quote returns more than only its first argument. However (quote 1
2 3) is equivalent to '(1 2 3) so you’ll most likely not ever
- No macros. Functions can instead not evaluate their arguments, your
job is to evaluate them as needed.
- No strings. String syntax creates so-called “transient” symbols.
Additionally to doubling as string replacement, these are not equal
to other symbols with the same contents and are therefore used to
avoid name clashes in macro-like functions.
- No implicit closures. If you need to capture something from the
environment, you must do this explicitly and have the choice between
mutable and immutable ones.
- Unknown symbols evaluate to NIL instead of throwing an
- Indentation (and even pretty-printing) is a lot easier than in
classic Lisp dialects as it’s basically about increasing the depth
by three spaces for each level instead of lining up parentheses.
- Closing parentheses that do not belong to the current line are
separated by spaces. For convenience’s sake, a closing bracket is
interpreted as “super paren” and closes all remaining parentheses.
- The style guide has an interesting solution to the problem of local
variables potentially shadowing built-in functions: Capitalized
- NIL is not only equivalent to the empty list, but to the empty
string as well. This bit me when wrapping readline as an empty
string couldn’t be discerned from NULL with the naïve
- Error handling is close to non-existing. If you screw up things too
much, the interpreter will segfault on you. This is not considered
- Identifiers for built-in functions are very short and at times
cryptic. Clojure got nothing on that!