Fork me on GitHub
isolate.js via AST analysis
interactivate
Recent changes in SDK
nodeconf 2012
Write logic, not mechanics
protocol based polymorphism
(clojurescripting :intro)

This is a first in a series of posts I’m planning to write about my clojurescript learning experience. If you have not heard of clojurescript yet, it’s a lispy programing language and a flavor of pretty popular clojure that compile targets JS.

You can find a many impressive demos and blog posts about clojurescript, but most of them assume you’re coming from clojure. Even though I read a book and have watched all the clojure screencasts, I never managed to dive into it. Now with clojurescript it’s even more tempting, so I decided to start over again and document every iteration.

Objectives

1. Reduce dependencies to minimum

Most of the JS developers are spoiled by using a language that has a brilliant runtime, it requires absolutely nothing to get started. Also, nature of JS (everything usually comes over the wire) makes us pretty picky when it comes to introducing dependencies. No matter if it’s a library or tool we’d like to avoid it, unless absolutely necessary. So in order to be comfortable hacking with clojurescript my first objective is to reduce dependencies I’ll use to an absolute minimum.

2. Write “Hello world”

Another objective is of course “hello world” application, which in this case will mean writing a clojurescript program that writes “hello world” into an html document.

Start a package

First of all we need to create a project / package. Assuming you know and love npm first thing you will consider doing is finding an equivalent for a given language, which turned out to be a leiningen! There are multiple ways one can install it. I used brew install leiningen since homebrew is a package manager of my choice. Once leiningen is installed, lein new clojurescripting can be run to generate a blank package. Generated project.clj file is a package descriptor containing package metadata, equivalent of package.json in JS. After some tweaking I ended up with something like this:

(defproject clojurescripting "1.0.0-SNAPSHOT"
  :description "Learning clojurescript"
  :url "http://documentup.com/gozala/clojurescripting/"
  :license { :name "MIT"
             :url "http://jeditoolkit.com/LICENSE" })

Write some code

The most basic thing I could think of was an alert dialog with “Hello World” message in it. Note that it requires calling JS function out from the clojurescript, which turned out to be trivial:

(ns clojurescripting.core)

(js/alert "Hello World!")

Compile to JS

Now that the code is there, we need to compile it to JS. Clojurescript quick start document describes how to do that manually, but luckily there is a lein-cljsbuild plugin for leiningen which can be used to automates this process. To do that you need to configure project.clj accordingly:

(defproject clojurescripting "1.0.0-SNAPSHOT"
  :description "Learning clojurescript"
  :url "http://documentup.com/gozala/clojurescripting/"
  :license { :name "MIT"
             :url "http://jeditoolkit.com/LICENSE" }
  :plugins [[lein-cljsbuild "0.1.2"]]
  :cljsbuild { :builds [{ :source-path "src"
                          :compiler { :output-to "lib/app.js"
                                      :optimizations :whitespace
                                      :pretty-print true }}]})

Once project is configured we need to tell leiningen to install all the dependencies by running lein deps. The best way to compile cljs to JS is by running lein cljsbuild auto which will watch source files and automatically recompile to JS on changes. This way ergonomics of refresh-driven development is preserved when working in clojurescript.

Unfortunately I had to straggle for some time before I figured out why JS file was not generated. The problem is that lein new generates core.clj which is a clojure file not a clojurescript one, so one needs to make sure to rename it to core.cljs instead.

Play

In order to play with a result I needed an html page, so I’ve created most basic index.html in the root of the project directory:

<body></body>
<script src=./lib/app.js></script>

As you would expect opening it in a browser displayed alert dialog. But since I wanted to write more clojurescript code I have decided to advance my example just a little bit so it writes “Hello World” into document body:

(ns clojurescripting.core)

(defn set-html
  "Sets `.innerHTML` of the given tagert element to the give `html`"
  [target & html]
  (set! target.innerHTML (apply str html)))

(defn set-text
  "Sets `.textContent` of the given `tagret`  to the given `text`"
  [target & text]
  (set! target.textContent (apply str text)))

;; Ineject "Hello world!" into document body.
(set-html document.body
          "<div style='background: black; color: white;'>"
          "<p>Hello world!</p>"
          "</div>")

Just a refresh and changes are applied! In fact, clojurescript has a better alternative than page refresh but that’s topic of the next post!

Summary

Overall I’m pretty happy with amount of tooling I had to use in order to write this basic clojurescript powered page. I also really liked leiningen’s plugin system and I hope npm will get something similar at some point in a future. Working with pure JS and DOM on the page is seamless and ergonomics of refresh driven development is preserved! On the flip side, I found leiningen to be a painfully slow (as it’s written in clojure and runs on JVM). Hopefully they will speed it up by switch to clojurescript on node.js or clojure on bare metal sometime in a future.

Code used in this post can be found under my clojurescripting repository on github.

JavaScript JS Documentation: JS Array some, JavaScript Array some, JS Array .some, JavaScript Array .some
namespaces
JS Guards
Packageless modules
Addons in multi process future
Yet another take on inheritance
Shareable private properties
Evolving VS Adjusting
oh my zsh
Git status in bash prompt
CommonJS based Github library
Taskhub
Gist plugin for Bespin
Reboot
google pages is dead
narwzilla
JSDocs
bespin - JavaScript Server
bespin chromend
Google App Engine + Helma = geekcloud
Bespin to Helma
bespin multibackend mockup
Adjectives | Ubiquity + Bugzilla love
Some Mock-up around Ubiquity
Mozshell
Ubiquity command Say
ubiquity command dictionary
Picasa Photo Viewer (Linux port) - Updated
Ubiquity command for JIRA & Crucible
Picasa Photo Viewer (Linux port)
VirtualBox
KeyZilla 0.1
XUL Development