Hello, interpolation!

You may have noticed in Command-line Clojure that we glued the string together in pieces

(println (str "Hello, " name "!"))

What are we, Neanderthals? There is a string format in Clojure, but that’s not much better

(println (format "Hello, %s!" name))

Doesn’t Clojure have interpolation? Apparently not, but Chas Emerick wrote a macro for it some years ago. According to that, we should be able to use

(println (<< "Hello, ~{name}!"))

instead. The “<<” is a macro that enables variable interpolation in the string with “~{}”. It does a whole lot more too. Nice!

So how do we use it? It’s not in the core of Clojure yet, but it has been moved into something called the core.incubator. It says there we just need to

Add a dependency on core.incubator to your Leinigen project.clj:

[org.clojure/core.incubator “0.1.4”]

So if we create a new project

$ lein new hello-world
$ cd hello-world

and then add that dependency

$ cat project.clj 
(defproject hello-world "0.1.0-SNAPSHOT"
  :description "Hello interpolation"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/core.incubator "0.1.4"]])

we should be able to use the macro.

$ cat src/hello_world/core.clj 
(ns hello-world.core)

(defn hello
  ([]
   (hello "World"))
  ([name]
   (<< "Hello, ~{name}!")))

But that doesn’t work. We still need to add the namespace. And not clojure.core.incubator as we might expect, but clojure.core.strint!

(ns hello-world.core
  (:use [clojure.core.strint]))

(defn hello
  ([]
   (hello "World"))
  ([name]
   (<< "Hello, ~{name}!")))

Ah, interpolation! Sweet, sweet, interpolation!

$ rlwrap lein repl
user=> (use 'hello-world.core)
user=> (hello)
"Hello, World!"
user=> (hello "Hank")
"Hello, Hank!"

I do hope they add the “<<” macro to the core soon!

Hello, interpolation!

Command line Clojure

I guess folks usually run Clojure in a REPL, but what if we wanted to write a command-line utility?

Clojure

That’s simple enough to do. If we had the following

#!/usr/bin/env clj

(println (str "Hello, World!"))

in an executable file hello.clj, then we could run it just like anything else.

$ ./hello.clj 
Hello, World!

But how do we interact with our environment? For example, most scripts need access to the command line arguments. In Clojure, these are provided in the *command-line-args* variable. So if we have the following

#!/usr/bin/env clj

(def args *command-line-args*)

(defn greet [name]
  (println (str "Hello, " name "!")))

(if (empty? args)
  (greet "World")
  (doseq [name args]
    (greet name)))

then we’ll greet each command line argument in turn.

$ ./hello.clj Hank Dean Brock
Hello, Hank!
Hello, Dean!
Hello, Brock!

If there are none, we’ll just greet “World” like before.

$ ./hello.clj 
Hello, World!

This is fine, but it’s kind of slow.

$ time ./hello.clj 
Hello, World!

real    0m2.278s
user    0m3.484s
sys     0m0.168s

I guess this is because we have to wait for the JVM to start up. I’m not sure we’d want a command line utility this slow. What can we do?

ClojureScript

How about ClojureScript? We normally think of that as running in the browser, but there are other ways to use it as well. Perhaps server-side JavaScript is faster to start up than Java.

ClojureScript with WebKit

Planck is a stand-alone ClojureScript REPL that is based on WebKit. We could write our script for that with just a small change

#!/usr/bin/env planck
;; -*- clojure -*-

(ns hello.core
  (:require [planck.core :refer [*command-line-args*]]))

(def args *command-line-args*)

(defn greet [name]
  (println (str "Hello, " name "!")))

(if (empty? args)
  (greet "World")
  (doseq [name args]
    (greet name)))

We have to add a line to get Planck to arrange for WebKit to get the *command-line-args* for us. Running this, we get

$ time ./hello-planck 
Hello, World!

real    0m3.318s
user    0m4.532s
sys     0m0.316s

Hrm. That didn’t help. What else?

ClojureScript with Node

Lumo is a stand-alone ClojureScript REPL based on Node.js. To use it, we change our script again

#!/usr/bin/env lumo
;; -*- clojure -*-

(def process (js/require "process"))

(def args (.slice (.-argv process) 3))

(defn greet [name]
  (println (str "Hello, " name "!")))

(if (empty? args)
  (greet "World")
  (doseq [name args]
    (greet name)))

Here we ask Lumo to get the Node.js process.argv for us. That is,

(def args (.slice (.-argv process) 3))

is the Lumo version of Node’s

var args = process.argv.slice(3)

We’re ignoring the first few arguments because they are our script’s name and lumo itself and whatnot.

$ time ./hello-lumo
Hello, World!

real    0m1.504s
user    0m1.840s
sys     0m0.148s

That’s a little faster, but still not great. Plus we pretty much still have to know Node.js to write it.

Summary

So, they’re all pretty slow.

And it would be nice if the Clojure code didn’t have to change. Like if there were some Clojure way to get the command line arguments and then Planck and Lumo made it look more like that. Interoperability with WebKit and Node is nice, but it would be even nicer if it were more transparent.

Command line Clojure