Here at Scalac, we are always trying to be innovative and creative in everything we do. This is why when I heard that we were planning to write some blog post on Clojure/ClojureScript, I asked myself: why can’t I write a ClojureScript post in ClojureScript itself?
The advantage of this is evident, our pages will be way more interactive and therefore interesting to read, play with and learn from.
In order to start, I want first to give the reader some general notion on how the compiler works and why it is so powerful. I am not going to spend too much time on it because you can find better detailed articles out there.
The most significant difference between Clojure and ClojureScript is that ClojureScript concretely isolates the code interpretation phase (reading in Lisp terms) from the actual compilation phase (analysis and emission). The text that a programmer writes is read by the ClojureScript reader, then passed to the macro expansion stage and ultimately to the analyzer. The outcome is the abstract syntax tree (AST), a tree-like metadata-reach version of it.
This is paramount for a bunch of obvious reasons, but mostly because you have
separation of concerns between tools that understand text and tools that
understand ASTs, like compilers. Indeed this is why ClojureScript can actually
platforms: in order to emit
instructions belonging to the
X programming language you “only” need your own
compiler to read the AST, prune it and emit. This is also how currently the
GitHub blog posts are nothing but standard
.md files that Jekyll builds
locally and then serves. The Markdown syntax allows adding standard HTML tags
<script> tags. Therefore, embedding ClojureScript
was relatively easy once I got pass configuring it. This is
mainly why I thought writing a blog post might be a good idea and save some
I wanted to be able to plug my changes in transparently, still allowing to
create posts without Clojure. A simple
start.sh was previously used to create
.md files in Jekyll’s
_posts folder that then had to be worked on by the
author and committed.
Instead, I needed to create fully-fledged ClojureScript projects, potentially
one per blog post, somewhere. I chose to hide them in a brand new
git submodules and for this reason I added a few lines to
git submodule add.
Materializing the project with
lein new <template> <project-name>.
As before, copy the
.md template to
Now switching to the project folder was showing me the reassuring sight of the
scripts folder. According to my template default, the
<project-name>/resources/public/js/compiled so I
Remember that we are inside a project folder under
_cljs, therefore Jekyll’s
root is two directories up. Typically, only
:output-to is significant for the
final version as this option contains the path of the generated (and minified)
.js file. In
jekyll-dev though, you can also specify
temporary files used during compilation are written, and
:asset-path, that sets where to find
:output-dir files at run-time. This
way you have full visibility of the output.
Now I was finally able to
cd to my project, execute
lein cljsbuild once
jekyll, and see my generated
Markdown blog page. There are many ways to do this, but the one I found most
intuitive and straightforward was by using
reagent. This is not a post about
reagent per se (we wrote about it some time
but its lean and unopinionated architecture struck me as
the way to
go. Reagent, a React wrapper, dynamically mounts DOM elements and
re-renders them when necessary, effectively hiding the complications of
managing React component’s life cycle.
Consequently, on the HTML side I needed to: define a
include the compiled
main() which mounts my
Note that it is very important to prepend a slash to
script and replace
compiler always transforms namespace dashes in underscores.
On the ClojureScript side instead I needed to ensure that the
<div> with id
cljs-on-gh-pages was correctly mounted:
Now every time the blog post page is shown,
reagent intercepts the
renders anything our
main() returns, typically
If you have had the patience of reading till the end, here is a reward for you: a ClojureScript REPL to toy with!
Not everything works in this
ClojureScript-in-ClojureScript habitat at
the moment. However, thanks to other inspiring implementations, here too
you have access to Clojure’s superpowers. Note that the REPL has history (
down to navigate) plus
other handy shortcuts. Enjoy!