Website Logo. Upload to /source/logo.png ; disable in /source/_includes/logo.html

Ear to the Bits

Ramblings about code

Metaprogramming, in Brief

Coming from a background in compiled languages, the idea of metaprogramming is fascinating to me. In the world of C, writing and running programs are distinct actions. At runtime, a program cannot be altered. However, in Ruby, the actual code can be modified while it is executing. To those who only know Ruby or similar interpreted languages, it may not seem like a big deal, but to me, it’s the coolest thing ever. At a hackathon I went to once a guy wrote a self-modifying program in Python, and I thought it was black magic. I didn’t realize it was just a gift that comes with the language, and you didn’t have to do any serious hacking to dynamically alter the code.

Let’s just look at eval. Maybe this isn’t so exciting:

1
eval("2 + 2") # => 4

But then, consider this ditty:

1
2
3
4
5
6
7
8
9
puts "Please enter a phrase: "
# pretend you entered Hello World here
phrase = gets.strip
puts "Enter the name of a string method: "
# pretend that you entered upcase here
method_name = gets.strip
exp = "'#{phrase}'.#{method_name}"
puts eval(exp) # => HELLO WORLD
puts "#{exp}" # => 'Hello World'.upcase

That’s pretty cool. You just wrote a little program on the fly. What else can you do? You can write a program that writes programs.

Yeah dawg. I’m for real. This is all of the code it takes (Adapted from The Book of Ruby by Huw Collingbourne):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
program = ""
input = ""
line = ""
while line.strip() != "q"
  print( "?> " )
  line = gets.strip
  case line
  when ''
    puts "Evaluating..."
    eval input
    program += input
    input = ""
  when 'l'
    puts "Listing the program you've written so far..."
    puts program
  else
    input += line
  end
end

If you run this, you kind of have a REPL, but it’s significantly dumber than IRB:

1
2
3
4
5
6
7
8
9
?> def scramble(s)
?> s.split("").shuffle.join("")
?> end
?> 
Evaluating...
?> puts scramble("Hello")
?> 
Evaluating...
Holle

Cool. So what other applications does metaprogramming have in Ruby besides writing REPL’s? As it turns out, it’s one of my favorite things I’ve learned about in Ruby thus far: ActiveRecord. When you define associations between models, whether it be belongs_to or has_many or anything else, the methods that you get for free are all generated on the fly. It does this using eigen or singleton classes. This is having the ability to define methods for specific instances of a class, after the class has already been defined. For example, say you define a class like so:

1
2
3
4
5
6
7
8
9
10
11
12
class Roomba
  # Some other methods
  def vacuum_floor
    while floor.dirty?
      #vacuum for 5 seconds
      vacuum(5000)
      if clogged
        raise Roomba::CloggedException
      end
    end
  end
end

Then you would instantiate it as

1
fido = Roomba.new

But say you got the newer model, which knows how to unclog itself. Then you could do something like

1
2
3
4
5
6
7
def fido.vacuum_floor
  #vacuum for 5 seconds
  vacuum(5000)
  if clogged
    reverse_suction
  end
end

There’s also this other fancy syntax you can use:

1
2
3
4
5
6
class << fido
  def vacuum_with_style
    vacuum(5000)
    robo_boogie
  end
end

It looks mysterious, but it’s doing the same thing, I swear.

You can actually define singleton methods for classes as well, and they’re simply called class methods. You can do this in a number of ways:

1
2
3
4
5
class Roomba
  def self.warranty
    "Lifetime"
  end
end

This is the syntax we’re familiar with. But there’s more:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Roomba
  class << self
    def warranty
      "Lifetime"
    end
  end
end

def Roomba.warranty
  "Lifetime"
end

class << Roomba
  def warranty
    "Lifetime"
  end
end

ActiveRecord creates new singleton methods for instances and classes when you define associations. It may seem like magic, but that magic is just metaprogramming.