1. Computing

More About Methods

By

More About Methods

First-class Methods

In some programming languages, such as Javascript, methods are first-class objects. This means they can be assigned to variables, passed around and treated like any other object. Surprisingly (because Ruby is usually so progressive in these respects), Ruby methods are not first class objects. They exist in a kind of limbo. They can be referred to by name and called using the dot operator or the send method, but they can't be assigned to variables or copied.

There are some methods for dealing with methods though, such as define_method for programatically defining methods, alias_method for making new names for methods and remove_method to remove them. This is because how methods are implemented.

Every object in Ruby has an instance variable table. You can refer to these instance variables using the instance variable prefix (@), or programatically with instance_variable_get and instance_variable_set. Similarly, every class has a table of methods. These methods can be defined and called using the typical syntax, or programatically with the methods described above. Since methods exist in a different world, so to speak, from variables, they can't be assigned to variables.

There are, however, similar features in Ruby that do allow this. Both procs and lambdas can be assigned to variables.

Run-Time and Compile-Time

Ruby makes no distinction between run-time and compile-time. The "compile" phase of Ruby does almost nothing except compile the Ruby code to a syntax tree or byte code. At this time, none of the methods or classes from the source code are defined in the runtime.

When the Ruby code containing the methods is run, this is when the methods are actually inserted into the method table. Understanding this is key to understanding how two things work: defining methods inside of methods and defining methods twice.

Consider the following code. Remember that methods are not defined until the code containing the method definition is actually run.


def outer
 def inner
   puts "Hello from inner"
 end
end

# If you try to call inner now, you'll get an error
begin
 inner()
rescue NameError
 puts "It wasn't defined"
end

# But if you first run call outer, it will run the code
# that defines inner
outer()
inner()

You'll probably never see code like this, but it really demonstrates how method definition works. Something you will need to understand well is defining the same method twice. Doing this simply overwrites the old method. Picture the method table in your head as something like a hash, with method names as the keys and the method code as the values. If you assign a value to the same key twice, the old value is discarded. This is also how methods work.


def method
 puts "First definition"
end

def method
 puts "Second definition"
end

# Will print "Second definition"
method

Unlike the previous example you will see this type of code at some point. However, don't confusing defining the same method twice with overriding a method in the ancestor chain. Those are two separate mechanisms at work.

Where Methods Live

As mentioned before, all methods live inside classes (or modules). Methods live inside of the class pointed to by self.class. What class is that? That depends on where the code is. In the global lexical scope (that is to say, outside of any other classes or modules) the self object is an object called main, an invisible object that bootstraps all Ruby code. Its class is Object, so any methods defined there become instance methods of the Object class. This can lead to surprising effects.


def monkey
 puts "Testing monkeys"
end

10.send(:monkey)

First, we define a method called monkey in the global scope. This ends up as a private instance variable of the Object class. It's instantly available in all objects. It's private so we can't call it directly, so we use send to call it on the 10 object (a simple number). So be careful when defining methods in the global scope, they may mangle important instance methods from the Object class. This example shows how things can go wrong.


def inspect
 return "Aliens (and unicorns)"
end

puts 10.inspect

We defined a method called inspect. It doesn't really do anything, just return a silly string. But as soon as we try to call inspect on any object, which usually returns some information about the object, we get an error. We threw away the old Object#inspect method and defined a new private one. In other words, it's a bad idea to use these "global" methods in larger programs. Put them in a module.

But back on track, where do methods live? They live in the class you'd get by running self.class. Inside of a class or module definition, that's most certainly the class or module you're defining. This is where most methods are defined, but if you ever come across a method definition in an odd place, just ask yourself "What type is 'self'?"

  1. About.com
  2. Computing
  3. Ruby
  4. Beginning Ruby
  5. Object Oriented Programming
  6. More About Methods

©2014 About.com. All rights reserved.