Hugh Winkler holding forth on computing and the Web

Saturday, June 14, 2008

Ruby: DSL for Writing Programs


PEOPLE = [{:first=>'scott', :last=>'guthrie', :age=>32},
{:first=>'susanne', :last=>'guthrie', :age=>32},
{:first=>'bill', :last=>'gates', :age=>50}]

def avg_age
guthries = {|p| p[:last] == 'guthrie'}
guthries.inject(0) {|s, p| s + p[:age]} / guthries.length

Common Lisp:

(defvar +people+
(list :first "scott" :last "guthrie" :age 32)
(list :first "susanne" :last "guthrie" :age 32)
(list :first "bill" :last "gates" :age 50)))

(defun avg-age ()
(let ((guthries (remove-if (lambda (p) (not (equal "guthrie" (getf p :last)))) +people+)))
(/ (reduce #'+ (map 'list (lambda (p) (getf p :age)) guthries)) (length guthries))))

These snippets calculate the average age of the people having last name "guthrie". The functions are both two lines long, but the Ruby one is more readable.

S-expressions give Lisp powerful ways to build code in code. But writing programs using S-expressions seems cumbersome compared to Ruby's syntax. The syntax of Ruby shortens the code you have to write to do everyday tasks. It's like a domain specific language for, er, writing computer programs.

You give up some programming power -- a fair trade if you rarely need that power.


Phillip Cal├žado said...


I think the right term is "expressive" and not "DSL to write programs" as all programming languages are DSLs to write programs.

The only exception I can think of would be using natural speaking (i.e. English or other language not invented to write computer programs) to describe programs.

Phillip Calcado

Anonymous said...

Just write a bit simpler Lisp:

Get rid of LIST, Lisp has a syntax for lists:

(defvar *people*
  '((:first scott :last guthrie :age 32)
    (:first susanne :last guthrie :age 32)
    (:first bill :last gates :age 50)))

You can say that Lisp is a DSL for list processing, where the code is represented as lists. Which allows to write programs that generate and process programs. That is it allows a great deal of automatic code generation and manipulation. Lisp is not optimized on the micro-token level. If I want that I can easily add it to Lisp, since the syntax is programmable. Lisp also is much better to edit, since the expressions have a begin and end tag (the parentheses), so you can manipulate the code with simple list manipulation editor commands. Optimized commands are list comprehensions or series.

Rewrite it as a LOOP with the added benefit, that the list is only traversed once.

(defun avg-age ()
  (loop for p in *people* when (eq 'guthrie (getf p :last))
    sum (getf p :age) into s and count t into c finally (return (/ s c))))

With the added benefit, that a good Lisp compiler will generate code that is up to orders of magnitude faster than Ruby...

Anonymous said...

These snippets calculate the average age of the people having last name "guthrie". The functions are both two lines long, but the Ruby one is more readable

BZZT! Thank you for playing, but that would be an opinion.

Markus said...

Having experience in both Ruby and Lisp, I would agree that the avg_age method is more readable in Ruby

Justin George said...

I wish Ruby's syntax were simpler, I love it, but it's a pain in the ass to cross compile. I really want to see a seperate front, middle, and back end so we can plug into different languages, and I think lisp is a really ideal middle in that scenario, being basically an executable parse tree.

Wilson said...
This comment has been removed by the author.
Wilson said...

I don't think the original code is very Ruby-like.

This is another way to write this same program:

(using pastie because this form doesn't seem to support the 'pre' tag.)

Wilson said...

Or we could get even weirder with inject for the Perl feeling.

hughw said...

@phillip: I was going for irony. I agree 'expressive' expresses it well.

@anon1: your loop is much more readable. And efficient. I prefer list comprehensions and functional style. Note to self: reconsider in face of evidence.

@anon2: This is my blog. There may be one or two opinions here, yeah.

@wilson: nice refinements using apt Ruby syntax.