Lisp! An elegant language from a more civilized age. Yet there has always been the matter of having to count brackets, the difficulty of seeing which bracket goes with which.
Users of Lisp and its children cope with this in various ways:
- Indentation.
- Writing only small functions.
- Turning common nested chains of function calls into a single function call. For example, cond can stand for a chain of ifs, and let for a series of assignments.
- Arranging the order of parameters to a function so that the most complex parameter is the final one.
This final method is effective, but still requires counting back the brackets at the end of a function. Text editors help with this, but would it not be nice if you did not need the text editor's help to understand your code?
Suppose we add a tiny bit of syntactic sugar that lets us express a list
(a b c)
as
(a b] c
with the final element of the list after the closing bracket.
I tried this in Clojure, and found the result pleasing. Consider quicksort (from Rosetta Code).
(defn qsort [L]
(if (empty? L)
'()
(let [[pivot & L2] L]
(lazy-cat (qsort (filter (fn [y] (< y pivot)) L2))
(list pivot)
(qsort (filter (fn [y] (>= y pivot)) L2))))))
See how it drifts right. See the final )))))). See how the simpler case is handled first in the if, putting the meat at the end. Now let's apply sugar:
(defn qsort [L]]
(if (empty?] L '()]
(let[ [pivot & L2] L
]]
(lazy-cat (qsort] (filter (fn[y]] (< y pivot)] L2
(list] pivot
(qsort] (filter (fn[y]] (>= y pivot)] L2)
Note how the function is revealed to be a series of qualifications -- this is qsort, what to do in the base case, some variable definitions -- to a final statement of how to decompose sorting into two smaller sorting problems. Within each line, you can also see similar transformation into series of qualifications. No more )))))), the structure can be seen at a glance.
The let here is a little ugly, it would be nicer to be able to write:
(let variable-name value]
and simply use multiple let statements for multiple assignments.
You'll also note I left the comparisons unsugared. I feel the order of parameters would ideally be reversed for numerical operators. For example, (< pivot] y. Or for an average (/ (count] values] (sum] values. The denominator is usually less meaty than the numerator.
Another example, the Mandelbrot Set task from Rosetta Code:
(ns mandelbrot
(:refer-clojure :exclude [+ * <])
(:use (clojure.contrib complex-numbers)
(clojure.contrib.generic [arithmetic :only [+ *]]
[comparison :only [<]]
[math-functions :only [abs]])))
(defn mandelbrot? [c]]
(loop [ iters 1
z c
]]
(if (< 20] iters
true]
(if (< 2] (abs] z
false]
(recur (inc] iters] (+ c] (* z] z
(defn mandelbrot []]
(apply str]
(interpose \newline]
(for [y (range 1 -1 -0.05)]]
(apply str]
(for [x (range -2 0.5 0.0315)]]
(if (mandelbrot?] (complex x y)
"#"]
" "
(println] (mandelbrot)
Download
Here is the patch to Clojure:
Here is a Clojure jar with the patch applied (compiled from git repository as at 19 December 2009):
If you have installed Java, you can use it with:
java -cp melodic-clojure.jar clojure.main
Why Melodic Clojure?
My initial application for this notation was writing musical melodies. For example, this is the music for the Buffens:
F ((G] A] (A] Bb
A ((G] A] (F] G
F ((G] A] (A] Bb
A ((F] G] (G] F
c (d] (c] Bb
A ((G] A] (Bb] c
c (d] (c] Bb
A ((F] G] (G] F
After desugaring, the brackets indicate the division of time before each note. Before desugaring, the brackets indicate the emphasis pattern, the prosody. The deeper bracketed notes are given reduced emphasis.
--> See Metrical Phonology. For Melodic Clojure perhaps follow these rules:
- (a b c] is a compound, the Compound Stress Rule applies, giving emphasis to a. Example: (apply +]
- (a] (b] c is a phrase, the Nuclear Stress Rule applies, giving emphasis to c. Example: (println] (reverse] x
- Where a-b-c is more like a compound than a phrase, prefer sugarless (a b c) to (a b] c. Example: accessing a member of an object, (table :leg)