Getting open for new paradigms

I think that all you five readers (ok, I’m being too generous) of my blog know that I love Ruby. Well, I’ll tell you why I haven’t posted in awhile: I have met someone new, and now Ruby only feels… well, it is still pretty good, it just is hiding even better stuff from me.

Yes, that’s right. I’m learning that old-fashioned, parenthesis-filled, known as crappy language called Common Lisp. And… well, parenthesis is a silly reason for you to not learn it, too.

I tried to make a long post explaining why the language is good, but it would require a book. So, instead, I’ll post a link to Practical Common Lisp and tell some things that may convince you:

Ruby’s internal object system is based on Emacs Lisp’s object system. Emacs Lisp’s object system is based on CLOS (Common Lisp Object System).

So imagine all the power of Ruby’s internal typing and message passing (which in Common Lisp is actually message dispatching, and for good reasons), but exposed to you, everything modifiable.

Code is data. Data is code.

Everything in Lisp is composed by S-Expressions, which is simply a syntax for linked lists. And the language has lots of functions to manipulate linked lists. Yes. Yes, you can manipulate your code with these concepts.

First-class functions and lambdas are more flexible than Ruby’s blocks.

You can easily have more than one function as a parameter on Lisp. You can pass a function as a block more easily. For instance, compare this:

[3, 1, 2].sort { |x,y| y <=> x }

With this:

(sort '(3 1 2) #'>)

In the second case, you’re passing the “>” (higher than) function directly to the sort function. On the case of sort it may not be much, but see how you multiply all the elements on your array:

(reduce #'* '(3 1 2))

What if I need to pass a custom function? Well, use a lambda:

(sort '("a" "def" "bc") (lambda (x y) (< (length x) (length y))))

And if I’m creating a function that receives another function? Ruby has yield, is Common Lisp as easy?

Yes. Funcall:

(funcall <variable storing the function> <arguments>)

It can be interpreted, but it can also be compiled.

You can compile Common Lisp code to native code and run it with a great speed, unlike Ruby, which needs to be interpreted or to run under a virtual machine. And yet, you have the facilities of an interpreted language, as garbage collection, on the fly evaluation, first class functions and closures.

Common Lisp isn’t functional, as some people may seem to think.

As I’ve said before, Common Lisp has a powerful object system. It may give support for imperative style programming and declarative style programming. Most lispers seem to have some preference for functional and declarative programming, because it’s often shorter and more trustful. So, don’t be afraid of Common Lisp because of something like programming paradigm: it will fill your needs.

If you complain about lack of libraries, you shouldn’t have started to use Ruby in the first place.

Using something only because it’s popular leads to average technology, not better technology.

I mean, Ruby is good. But you should try Common Lisp yourself and see if it’s better.

If parenthesis bother you, try a good editor, most of them have great navigation for it.

Working with lambda functions in Ruby

Ruby provides an easy way of working with closures: the code blocks. Everybody that uses Ruby for a minimum amount of time knows how code blocks work. You can do it this way:

def foo(a)
  # Some code here, create n
  yield(n)
  # More code here
end

# Some code here, create x
foo(x) do |y|
  # Your code block goes here. You can access any variable from previous scope
  # But everything created here is exclusive for this scope
end

Or like this:

foo(x) { |y| do_stuff_here }

This is a lot similar, but not exactly equal, to what you can do with a functional language (where you can, for example, pass a function as an argument to another). Code blocks are really practical, but they have some limitations:

  1. You can only pass one block per function;
  2. Blocks can’t return anything. If you need the result of an anonymous function, blocks are not what you need. That’s because the return is non-local; that is, the function that is declaring the block will be the one returning.
  3. You can’t pass the same block more than once, because you can’t storing it anywhere.

But there are two other ways of creating anonymous functions in Ruby that addresses some or all of these problems: procs and lambdas.

1. Procs

Actually, I lied. You can store blocks somewhere. And the place is a proc.

Blocks are, actually, a way for creating procs. They are the same type of closure: they have access to the scope that creates them, their return is non-local and they can be passed as a parameter to another function. If you try to call a proc with additional parameters, they will be ignored, just like it would happen on blocks.

Generally, it is better to use a block than a proc, except when you need to pass more than one to a function or when you want to send the same block to two functions.

But how do you use a Proc?

Simple enough:

def receiver_method(parameter)
  y = 5
  parameter.call(y)
end

test_proc = Proc.new { |x| x * 3 }
puts receiver_method(test_proc)

This will print 15 on the screen.

2. Lambdas

Lambdas are similar to procs, but there are two main differences:

  1.  If you try to pass the wrong number of parameters to a lambda function, it’ll throw an error
  2. Lambdas can return values without affecting the callee

That is, you can create a lambda function like this:

fat = lambda do |x|
  result = 1
  1.upto x do |i|
    result *= i
  end
  return result
end

And call it wherever you want that it’ll not affect the flow of the code. In this aspect, lambda functions are similar to regular functions.

Beginning in Ruby 1.9, there is a cleaner syntax for creating lambda functions:

square = ->(x) { x * x }

And actually, lambdas are ALSO procs internally. But this doesn’t matter a lot from a pragmatic point of view.

So, that’s it. Whoever likes functional languages, Ruby has the tools for the job.