Thursday, 12 June 2014

Clojure:Lisp :: LSD:Meditation

When I got back to my room last night, I found my roommate watching Scorsese's documentary on George Harrison, and immediately joined in.

The film suggests that the main motivation behind Harrison's foray into eastern mysticism and spirituality came out of a desire to tap into the mind-altering effects of LSD while steering free of chemical dependency. Today, the impact that the counterculture revolution has had on western society is very apparent, and much of it rose out of these same reasons. Many of the things that they now take for granted were born out of the same wants and needs, with Yoga being an obvious example.

Timothy Leary, in his version of the Tibetan Bardo Thodol, the 'Tibetan Book of the Dead' compares the phases that the soul passes through after death to good and bad trips, corresponding to Nirvana and rebirth respectively.

I have heard of the effects of Sudarshan Kriya from some of my college friends who had signed up for a summer course with the Art of Living and their experiences were very much like what we have come to know about the effects of external mind altering substances.

Anyway, my point is that experimenting with psychedelics led many to rediscover many spiritual aspects that were previously unknown to the vast majority, and brought them into the mainstream. Hoffman called his invention 'medicine for the soul', and wanted it to be regarded with awe rather than distrust, though it never really came to be. Acid led to the understanding of the the structure of DNA, a major revolution in thought, a dislike and deglamorization of war and things that lead to it, and to a whole lot of absolutely brilliant music.

Coding in Lisp seems to have infused many brilliant hackers (Alan Kay, Dijkstra, Stallman, etc.) with similar revelatory experiences.

It's been a long time since Common Lisp was standardized, and it has proven its worth time and again, and due to the largely orthogonal nature of the language, there have been little to no changes to its core, which some people see as a sign of 'death', and which couldn't be farther from the truth. Lisp embodies ideas that are timeless, and CL stuck to them and flourished, but didn't 'grow' in the sense other languages grow, because entirely new paradigms could be introduced in it without even touching the core. There was not a need to grow, only to embrace apparently new ideas.

But the language was dead to people who had not grown up with it, and lived in entirely foreign environments. Lispers tried time and again to reintroduce the power of their language through new implementations, but failed to grasp their attention. It was only with the addition of Lispy features to the now-mainstream languages, and the complexity that it exposed in them, that their followers regained that dying interest.

I would like to highlight this with some examples.
List comprehensions, for example, are an awesome feature, and Python supports them with the following syntax:
squares = [i*i for i in range(100)]

Clojure has a for macro to achieve the same thing, and it looks like this:
(def squares
  (for [i (range 100)])
    (* i i))

The Python example looks simple at first, but becomes increasingly complex when you try to do something useful.
Take this function (from Conway's Game of Life) for example, that returns a list of neighboring cells.
;Clojure
(defn neighbors [[x y]]
  (for [dx [-1 0 1]
        dy [-1 0 1]
        :when (not= 0 dx dy)]
    [(+ x dx) (+ y dy)]))

#Python
def neighbors(cell):
  [x, y] = cell
  ds = (-1, 0, 1)
  return [((x+dx, y+dy) for dy in ds if (dx!=0 and dy!=0)) for dx in ds]

Clojure's for scales gracefully to address the problem, whereas Python's list comprehension starts getting ugly. GvR wouldn't even let me break it into separate lines (which also results in a broken lambda, but I digress).

Then there's the issue of metaprogramming. One of the best Python frameworks I've ever come across is Django. And one of its best features is the Model-based database creation and interaction. Turns out it's written using metaclasses, which are (in Python) largely undocumented, really complex and nothing short of black magic. The best documentation on the subject is this answer on stackoverflow. Metaclass programming is easier in Ruby, and Ruby is more like Lisp, but as Paul Graham says - "If [Python hackers] don't want to wait for Python to evolve the rest of the way into Lisp, they could always just..."

In fact, this is exactly what prompted me to start learning Lisp.
I turned to Clojure because of these reasons:
1. It embraces the JVM, which I'm very comfortable with,
2. It has more novel ideas (STM, immutability-as-default, laziness, etc.) built-in than CL,
3. Rich Hickey is an awesome person, and his talks have made me a big fan (especially this one)

Yes, the current Clojure environment is not nearly as good as CL's, and ugly stack-traces litter the scene. Yes, there is no TCO happening automagically. Yes, there are no custom reader-macros.

But then, the community is working on a pure Clojure compiler, which will bring with itself a better environment, the recur keyword makes it absolutely clear that my code runs in constant space, and lesser diversity of reader-syntax makes the code easier to read.

This survey showed that 29% of Clojurians would switch to CL if Clojure disappeared, when only 5% of them were coming from it.

Clojure is re-introducing Lisp to a lot of people, much like Acid re-introduced spirituality, and for some reason, some Lispers (*cough* Pascal *cough* Costanza), instead of being happy about it, are bitching in public. There might be some truth to the myth of the SmugLispWeenie after all.

Monday, 9 June 2014

Queens

I finally got around to reading SICP, which is absolutely brilliant.
I love the way programming is treated as an extension of cognitive concepts that lie at the very core of our being. Here is a quote from the first chapter:

The acts of the mind, wherein it exerts its power over simple ideas, are chiefly these three:
  1. Combining several simple ideas into one compound one, and thus all complex ideas are made. 
  2. The second is bringing two ideas, whether simple or complex, together, and setting them by one another so as to take a view of them at once, without uniting them into one, by which it gets all its ideas of relations.
  3. The third is separating them from all other ideas that accompany them in their real existence: this is called abstraction, and thus all its general ideas are made.
- John Locke, An Essay Concerning Human Understanding (1690)

The book then goes on to describe the 3 important features of a programming language as follows:
  1. Primitive expressions, which represent the simplest entities the language is concerned with
  2. Means of combination, by which compound elements are built from simpler ones
  3. Means of abstraction, by which compound elements can be named and manipulated as units

Needless to say, I'm having a blast.

The book then explains these concepts with the help of various exercises, one of which is the N-Queens Problem. Here is a solution in Clojure that produces all possible configurations:

(ns queens)

(defn attacking? [[r1 c1] [r2 c2]]
  (or (= r1 r2)
      (= c1 c2)
      (= (+ r1 c1) (+ r2 c2))
      (= (- r1 c1) (- r2 c2))))

(defn safe? [board pos]
  (not (some #(attacking? % pos) board)))

(defn safe-poses [board n c]
  (for [r (range n)
        :let [pos [r c]]
        :when (safe? board pos)]
    pos))

(defn poss-boards [board n c]
  (for [pos (safe-poses board n c)]
    (conj board pos)))

(defn solutions [n]
  (loop [c 0, boards [[]]]
    (if (>= c n) boards
      (recur (inc c) (mapcat #(poss-boards % n c) boards)))))

Concise and expressive.
In fact, I was pretty surprised when i realized I was done, as my last attempt (in Java, around 3 years back) had been pretty complicated and unwieldy, and produced only one of the possible configurations.

Anyways, did I mention I'm having a blast?
Because I really am.