Programming ≈ Fun

Written by Krešimir Bojčić

Smalltalk, Lisp, Ruby Comparison - the Return of the Prodigal Son

Lets take the ball to the Lisp court where we want to add something new to the language itself, so we can check out this homoiconicity in practice. For instance I would like to be able to have special kind of “if” that reads like this:

(This is the way Pablo did it.)

(is-correct (= 0 0 )
             (println "Hooray it's true")
             (println "Not true"))

In Clojure it’s a breeze(other than breaking my heart in little pieces)

(defmacro is-correct [predicate if-true if-false]
    `(cond ~predicate ~if-true
           :else ~if-false))

Extending syntax like this is not possible in Smalltalk, but they have decent workaround:

(1 = 1)
    ifCorrect:    [Transcript show: 'I am correct']
    ifNotCorrect: [Transcript show: 'I am wrong'].

Usage is a beauty, and I find it even more readable. (Not to say that you can’t do the same thing in Clojure with keyword arguments.)

Seeing this and suddenly all “strange” Smalltalk stuff makes perfect sense

  • Not using dot for “methods” calls, but instead sending messages
  • Keyword arguments
  • Using dot as a delimiter on the end of statement instead of trying to guess where statement ends

Smalltalk implementation is simple:

# this is in True class
ifCorrect: trueAlternativeBlock ifNotCorrect: falseAlternativeBlock
     ^trueAlternativeBlock value

# this is in False class
ifCorrect: trueAlternativeBlock ifNotCorrect: falseAlternativeBlock
     ^falseAlternativeBlock value

Frustrating part is that Ruby is struggling quite a bit even with Smalltalk approach, implementation is simple, but syntax is getting in a way for usage:

def correct?(predicate, hash)
  predicate ? hash[:if_correct].call : hash[:if_not_correct].call
end

correct? (1==0),
            if_correct:->{print "Hooray it's true"},
            if_not_correct:->{print "Not true"}

Things that are missing are:

  • Ability to use multiple blocks in method call
  • Real keyword arguments
I am hoping there is a better way in Ruby, somebody please tell me how.
EDIT: there is a better way described here.

We can map Smalltalk approach more closely to the Ruby, but it is also lacking the elegance.

class FalseClass
  def if_correct(trueBlock, hash)
    hash[:if_not_correct].call
  end
end

class TrueClass
  def if_correct(trueBlock, hash)
    trueBlock.call
  end
end

(1==0).if_correct ->{ print "Hooray it's true"},
       if_not_correct:->{print "Not true"}

So what does all of this leaves us with? I think Alan Kay really nailed it when he said : “Lisp isn’t a language, it’s building material”. My interpretation would be that Lisp is really optimized for computers, not humans. Current syntax was first step, and was planned to be replaced with something more human friendly. Sitting in abstract syntax tree enables you to retain full power but requires some adjusting. The thing with humans is that they adjust. Somewhat similar example would be QWERTY keyboard which was designed to prevent jamming the typewriters by being counterintuitive enough to ever type fast. I am sure all of you here type like maniacs, that is basically brain 1 : QWERTY 0

McCarty said 31 years ago that Lisp is is an approximate local optimum in the space of programming languages. He was right, but for my local optimum, I am better of with stronger community that helps me overcome my time-brainpower constraints, so I am sticking it out with Ruby.

After all this exploring I reluctantly have to say that Lisp > Smalltalk > Ruby is correct observation. Big thank you to all good people telling it to me this all along. It seemed so unrealistic that I just had to check it out for myself. But at the end it looks like there ain’t no school like the old school.

As a side note: Smalltalk approach with keyword arguments is both readable and cool, so I would love to see real keyword arguments and support for multiple blocks (and hopefully better block syntax) in Ruby 2.0.

Comments