(NOTE: Edited slightly on 6/6/2015 for better clarity about the behavior of Object in different versions of Ruby. This post mostly applies to version 1.9.)
Something that confused me about Ruby at first was why tutorials, articles, and books kept referring to Ruby “methods” when talking about functions. For example:
def hello puts "hi there" end
Since hello is not being defined as part of a class or module, it stands on its own. Any reasonable programmer would call this a function, not a method. Initially, I took this as some odd quirk of the Ruby community. They include some pretty strange folks, after all.
Turns out there’s a good reason for this terminology. It IS a method.
As Pat Shaughnessy explains in this article, when you execute def hello
at the top level, it gets added as an instance method to the Object
class. You can verify this yourself:
def hello puts "hi there" end # this will print 'true' puts Object.respond_to? :hello
When you call hello
, the method is invoked on a receiver (ie. self
) that is an object instance called “main”, whose class is Object
.
def hello puts "hi there" puts "self is #{self}, its class is #{self.class}" end # this prints 'self is main, its class is Object' hello
This explains why defining methods at the top level appears to make them globally accessible. What actually happens is that, when you call hello
from any class or module, Ruby will look for it up the class hierarchy until it finds the method in Object
.
This also explains something else not immediately obvious to the newbie rubyist: things such as puts
are not builtins or special cases, but just regular methods in the Kernel
class, which is the parent of Object
. Whether you call puts
at the top level or from inside a class or module, Ruby will look up the class hierarchy and find it in Kernel
.
Kernel
‘s members include things that might surprise you, like require
!
These simple mechanisms—the implicit “main”, the Object
class, and the Kernel
module—are stunningly elegant design ideas (and, apparently, according to Shaughnessy, ideas adapted from Smalltalk). It allows Ruby to provide the convenient calling syntax of functions, while remaining object-oriented down to its very core.
(Compare this to Python, which does support “real” functions and differentiates them from methods. The distinction is less important in practice, as Python treats them both as “callables”–ie. arbitrary executable things.)
One small note: Shaughnessy writes that “All Ruby functions are actually private methods of Object.” In his example, he prints Object.private_instance_methods
and finds his newly defined function there. This is true in Ruby 2.1, but not in 1.9.3, where functions are public:
def hello puts "hi there" end # In Ruby 1.9.3, this will print 'false' and then 'true' puts Object.private_instance_methods.member? :hello puts Object.methods.member? :hello
I am just now beginning to realize how much Ruby is truly an object oriented language through and through. Parts of the language design might seem ad hoc and magical, which is indeed part of Ruby’s beauty and allure, but some very consistent and elegant mechanisms under the hood are what make them work.
(I’ve eagerly ordered Shaughnessy’s book, Ruby Under a Microscope, which looks terrific.)