Counting with Clojurescript
by Stephen Pike

Clojure is my new favorite language for side projects. It provides the simple, elegant syntax of Lisp inside the fast, battle-tested world of the JVM. I’ve had a soft spot for the lisps since watching the outstanding SICP lectures and going through the Little Schemer, and clojure feels like a modern evolution of the family.

Clojure itself is tied to the Java ecosystem, which is great for production but not so great for sharing in blogs. Fortunately there’s a separate project — Clojurescript — which leverages Google’s Closure compiler to compile Clojure code to Javascript, letting you run clojure code right in a browser. Getting started with Clojurescript is a little tricky (particularly if your experience with Clojure’s ecosystem is limited to hacking around on weekends) so before I launch into writing about clojure I wanted to start by walking through a simple app. Our toy program will count the characters, words, and lines in a text area. Here’s the result (try typing in the box):

Characters: | Words: | Lines:

1. Set up a development environment with Leiningen

Leiningen is the standard environment / build management tool for clojure. It’s a swiss army knife of project management, handling dependency management (with maven), running code, running tests, and generating builds. Once leiningen is installed, we can use it to generate the scaffolding for a new clojure app:

lein new app cljs-wordcount

Leiningen created a clojure source file at src/cljs_wordcount/core.clj:

    (ns cljs-wordcount.core
      (:gen-class))

    (defn -main
      "I don't do a whole lot ... yet."
      [& args]
      (println "Hello, World!"))

We can run this code by specifying the namespace we want to load:

$ lein run -m cljs-wordcount.core
Hello, World!

Clojure is running!

2. Build javascript with Clojurescript

Our goal with Clojurescript is to write clojure and compile that clojure into a javascript source file we can drop into a webpage. The lein-cljsbuild plugin to leiningen makes this easy. With the plugin we can tell Leiningen to watch our project folder and compile our output js automatically. Configure lein-cljsbuild in project.clj:

    (defproject cljs-wordcount "0.1.0"
        :dependencies [[org.clojure/clojure "1.5.1"]]
        :plugins [[lein-cljsbuild "0.3.2"]]
        :cljsbuild {:builds [{:source-paths ["src/cljs_wordcount"]
                        :compiler {:output-to "js/counter.js"}}]}
        :main cljs-wordcount.core)

This tells leiningen we want to compile all .cljs files in src/cljs_wordcount into a javascript file at js/counter.js. Start lein watching the folder with like this (the lein-cljsbuild plugin will be automatically downloaded on the first run):

lein cljsbuild auto

Now let’s create our first clojurescript file, src/cjs_wordcount/counter.cljs:

    (ns cljs-wordcount.counter)
    (js/alert "Hello world")

As soon as this file is saved, leiningen should pick it up and compile it. You’ll see a new file at js/counter.js.

3. Run the compiled clojurescript

To run our new counter.js, create an index.html file in the project root:

    <html>
      <body>
        Hello, World!
        <script src="js/counter.js"> </script>
      </body>
    </html>

Open index.html in a browser and you’ll see an alert. It works!

4. Write something useful

Let’s write a little clojure to count the number of characters, words, and lines in an input string. Put it in our counter.cljs file:

    ; count-chars is just count
    (defn count-words [text] (count (split text #"\s+")))
    (defn count-lines [text] (count (split text #"\n+")))

We need some input to count. Let’s add a textarea and some places to put the results:

    <textarea id="input" rows="10" cols="80">  </textarea>
    Chars: <span id="chars"></span> |
    Words: <span id="words"></span> |
    Lines: <span id="lines"></span>

To interact with the dom, we’ll need to require two namespaces: clojure.browser.dom and clojure.browser.event. We’ll use dom/get-element to find our input and read its value. event/listen will let us watch for events and respond to them. Tying it all together looks like this:

    (ns cljs-wordcount.counter
      (:use [clojure.string :only (split blank?)])
      (:require [clojure.browser.dom :as dom]
                [clojure.browser.event :as event]))

    (defn count-words [text] (count (remove blank? (split text #"\s+"))))
    (defn count-lines [text] (count (remove blank? (split text #"\n+"))))

    (defn count-text
      "Calculate counts from t and write them to the page"
      [t]
      ; `.` calls a function on a JS object. `.-` gets a property value
      (set! (.-innerHTML (dom/get-element "chars")) (count t))
      (set! (.-innerHTML (dom/get-element "words")) (count-words t))
      (set! (.-innerHTML (dom/get-element "lines")) (count-lines t)))

    (def input (dom/get-element "input"))
    (defn input-text [] (.-value input))

    (event/listen input :keyup (fn [evt] (count-text (input-text))))
    (count-text (input-text)) ; we want initial counts

You can see the full example app on github.