Foognostic blogs Seeking knowledge of foo

4Nov/09Off

Macros make Elvis better

Standard disclaimer: Clojure is a new hobby.

The C programming language provides a ternary operator. It's a terse if/else expression:

void print_something(const char *str) {
  const char *text = (str != NULL) ? str : "null";
  printf("%s\n", text);
}

The team behind the Groovy programming language took a look at the ternary operator and realized something. Developers frequently use it during a variable assignment to take the value of the test expression when not null, otherwise take the value of the "else" expression. A new operator was added to the language to support exactly this usage and was dubbed the Elvis operator.

def print_something(str) {
    def text = str ?: "null"
    println text
}

print_something("foo")
print_something(null)

The Scala programming language does not have an Elvis operator, but Daniel Spiewak posted an impressive implementation.

This led me to thinking about defining the Elvis operator in Clojure, my current spare-time language. Clojure is a Lisp hosted on the JVM (with some plans for the CLR). This was a fun exercise but I make no claims that this is a good way to be Elvis-ish in Clojure.

First, here's a naïve implementation:

(defn elvis_fn [a b]
  (if-not (nil? a) a b))

It works:

user> (elvis_fn 123 456)
123
user> (elvis_fn nil 456)
456

But it always evaluates the "else" expression. FALE.

(defn nap [duration]
  (println (format "Sleeping for %d ms" duration))
  (Thread/sleep duration)
  duration)
user> (elvis_fn (nap 123) (nap 456))
Sleeping for 123 ms
Sleeping for 456 ms
123

So that's bad. Let's make a macro version. This should prevent the expressions from being evaluated before Elvis gets a chance to do his thing:

(defmacro elvis_macro [a b]
  (if-not (nil? a) a b))

user> (elvis_macro (nap 123) (nap 456))
Sleeping for 123 ms
123

To verify this, I added

(println "Blue suede shoes")

before the (if-not) form and called the method and the macro again:

user> (elvis_fn (nap 123) (nap 456))
Sleeping for 123 ms
Sleeping for 456 ms
Blue suede shoes
123
user> (elvis_macro (nap 123) (nap 456))
Blue suede shoes
Sleeping for 123 ms
123

So it was easy to avoid evaluating things by simply making the function into a macro. I'm sure there are many things above and beyond what I am doing here, but it was a nice experiment.

Filed under: clojure, code 2 Comments
   

Foognostic blogs is Digg proof thanks to caching by WP Super Cache