Posts Tagged ‘defined’

Ruby’s Splat Operator

December 22, 2010

Ruby is a dynamic language. One of the things it lets you do is define methods with an unknown (or variable) number of arguments. It does this using the splat operator. But the splat operator can actually be used for other things in your code, especially if you’re using Ruby 1.9. That’s because a small change to how splat operators work make them much more useful.

In the beginning

The humble splat operator was first used to slurp up unnamed arguments to a method:

def sum *numbers
  numbers.inject{|sum, number| sum += number}
end

Sometimes, this is just syntactic sugar, because passing a list of numbers like this:

sum(1, 2, 3)

Is more intuitive and prettier, and less error-prone, than passing them as an explicit array:

sum([1, 2, 3])

In many cases though, the splat operator is more than just a pretty face. Take Ruby’s own method_missing instance method, that is available in every class. If defined, it will attempt to handle any calls to methods that don’t explicitly exist:

class Person
  def method_missing sym, *args
    if sym.to_s =~ /^(\w+)=$/
      instance_variable_set "@#{$1}", args[0]
    else
      instance_variable_get "@#{sym}"
    end
  end
end

method_missing must be able to accept an unknown number of arguments, since just about any method call could be thrown at it. In this case, we’re using it to get and set instance variables without having to define them first using attr_accessor:

ruby-1.8.7-p299 > p = Person.new
 => #<Person:0x10018f640 @turtles=nil> 
ruby-1.8.7-p299 > p.turtles = 'I like them'
 => "I like them" 
ruby-1.8.7-p299 > p.turtles
 => "I like them" 
ruby-1.8.7-p299 > p.squirrels
 => nil 
ruby-1.8.7-p299 > p.squirrels = 'are okay'
 => "are okay" 
ruby-1.8.7-p299 > p.squirrels
 => "are okay" 

We’ve just created a class that lets us define any attributes we want, and our method doesn’t care whether *args contains zero arguments, or a hundred.

It gets unintentionally cooler

Up through Ruby 1.8 you could use this splat operator, in a limited fashion, for things other than method argument lists. You could use it to flatten an array, in contexts where it was the last element in the list:

ruby-1.8.7-p299 > last_numbers = [3, 4, 5]
 => [3, 4, 5] 
ruby-1.8.7-p299 > all_numbers = [1, 2, *last_numbers]
 => [1, 2, 3, 4, 5]

The splat operator took the last_numbers array and expanded it inline! Now our new array contains five numbers, instead of two numbers and a nested array. This comes in handy for meta-programming. So let’s try putting the splat operator somewhere else in the array:

ruby-1.8.7-p299 > middle_numbers = [2, 3, 4]
 => [2, 3, 4] 
ruby-1.8.7-p299 > all_numbers = [1, *middle_numbers, 5]
SyntaxError: compile error
(irb):4: syntax error, unexpected ',', expecting ']'
all_numbers = [1, *middle_numbers, 5]
                                  ^
	from (irb):4
ruby-1.8.7-p299 > first_numbers = [1, 2, 3]
 => [1, 2, 3] 
ruby-1.8.7-p299 > all_numbers = [*first_numbers, 4, 5]
SyntaxError: compile error
(irb):6: syntax error, unexpected ',', expecting ']'
all_numbers = [*first_numbers, 4, 5]
                              ^
	from (irb):6

We can’t use the splat operator anywhere else except the end of a list, just like in method calls. This really limits its value in Ruby 1.8.

Things get intentionally cooler

As of Ruby 1.9, however, the splat operator has been given a little more love, and now it can be used almost anywhere. Basically, any array that is given the splat operator will “flatten” itself, and return the list of elements NOT in an array. Now we can do cool stuff like this:

ruby-1.9.2-p0 > first_numbers = [1, 2, 3]
 => [1, 2, 3] 
ruby-1.9.2-p0 > middle_numbers = [4, 5, 6]
 => [4, 5, 6] 
ruby-1.9.2-p0 > last_numbers = [7, 8, 9]
 => [7, 8, 9] 
ruby-1.9.2-p0 > all_numbers = [*first_numbers, *middle_numbers, *last_numbers]
 => [1, 2, 3, 4, 5, 6, 7, 8, 9] 

This is handy for all sorts of meta-programming challenges – namely, handling dynamic argument lists. It’s also great when you’re building an array out of smaller pieces where some of the pieces are scalars (single values) and some are arrays. Let’s say we have a Family class, that contains myself, my parents, and my siblings. I want a method that returns everybody in one large array. The usage would look like this:

ruby-1.9.2-p0 > family = Family.new
 => #<Family:0x000001018ed2d8> 
ruby-1.9.2-p0 > family.myself = 'Greg'
 => "Greg" 
ruby-1.9.2-p0 > family.parents = ['Mike', 'Carol']
 => ["Mike", "Carol"] 
ruby-1.9.2-p0 > family.siblings = ['Peter', 'Bobby', 'Marsha', 'Jan', 'Cindy']
 => ["Peter", "Bobby", "Marsha", "Jan", "Cindy"] 
ruby-1.9.2-p0 > family.everybody
 => ["Greg", "Mike", "Carol", "Peter", "Bobby", "Marsha", "Jan", "Cindy"] 

If I want to do this without the splat operator (as I’d have to in Ruby 1.8) it would look like this:

class Family
  attr_accessor :myself, :parents, :siblings
  
  def everybody
    members = []
    members << myself
    members += parents
    members += siblings
  end
end

As you can see, I have to go line-by-line, interacting with the members array differently depending on if I’m adding one value or an array. Here’s how you’d do it without the splat operator:

class Family
  attr_accessor :myself, :parents, :siblings
  
  def everybody
    [myself, *parents, *siblings]
  end
end

Much cleaner! This version is shorter and easier to understand. This is a technique that might not seem useful, until you have a need for it. Then you’ll start finding uses for it everywhere.

Advertisements

Ruby’s defined? Operator

December 20, 2010

Even if you’ve used Ruby’s defined? operator on a daily basis, you may not understand how it works. I sure didn’t until recently, but it’s worth a look.

A refresher in memoization etiquette

If you’re acquainted with memoization, this might look familiar:

class Person
  attr_accessor :first_name, :last_name

  def full_name
    return @full_name if defined?(@full_name)
    @full_name = "#{first_name} #{last_name}"
  end
end

The full_name method above uses memoization – the return value of the method is calculated just once, on the first call. It’s stored in the instance variable @full_name, and used for subsequent calls to the method. I first discovered this technique digging through the code base for Thoughtbot’s shoulda gem. I’ve used it hundreds of times, and never really questioned how the defined? operator works until recently.

Question mark?

Ruby methods can contain some non-alphanumeric characters like “!” and “?”, and rubyists take advantage of this to add readability to our code. Methods ending in “!” typically mean one of two things: the method is altering its receiver, or it’s going to complain loudly if it fails (usually by raising an exception). By the same convention, methods ending in “?” are asking a question, and the answer is usually boolean (yes/no).

The defined? method follows this convention…sort of. I always assumed it returned true/false, but that’s only half the story. If the object in question is defined, defined? gives you a string description of the object. This equates to “true” in any conditional arguments. If the object is not defined, it returns nil, which equates to “false”.

Test anything. Almost.

So defined? works in any boolean context, but it also provides a little more info. And it works on just about anything. Classes:

ruby-1.9.2-p0 > defined? Person
 => nil 
ruby-1.9.2-p0 > class Person
ruby-1.9.2-p0 ?>  end
 => nil 
ruby-1.9.2-p0 > defined? Person
 => "constant" 

It works on methods:

ruby-1.9.2-p0 > def bark
ruby-1.9.2-p0 ?>  puts "woof"
ruby-1.9.2-p0 ?>  end
 => nil 
ruby-1.9.2-p0 > defined? bark
 => "method" 

And of course it works on variables of all kinds:

ruby-1.9.2-p0 > defined? @@a
 => nil 
ruby-1.9.2-p0 > @@a = 'a'
 => "a" 
ruby-1.9.2-p0 > defined? @@a
 => "class variable" 
ruby-1.9.2-p0 > defined? @b
 => nil 
ruby-1.9.2-p0 > @b = 'b'
 => "b" 
ruby-1.9.2-p0 > defined? @b
 => "instance-variable" 
ruby-1.9.2-p0 > defined? c
 => nil 
ruby-1.9.2-p0 > c = 'c'
 => "c" 
ruby-1.9.2-p0 > defined? c
 => "local-variable" 

I even tried other operators, just on a whim. But of course, this was too much to hope for :)

ruby-1.9.2-p0 > defined?(+)
SyntaxError: (irb):1: syntax error, unexpected ')'
	from /Users/bellmyer/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

defined? is an operator, not a method

Because you can enclose the object you want to check in parentheses (as in, defined?(@full_name)), you might be tempted to think it’s a method. It’s not, it’s a native operator. This is an important distinction, because it means defined? can’t be overridden:

ruby-1.9.2-p0 > x = 'test variable'
 => "test variable" 
ruby-1.9.2-p0 > defined? x
 => "local-variable" 
ruby-1.9.2-p0 > def defined? object
ruby-1.9.2-p0 ?>  puts "defined? has been overridden!"
ruby-1.9.2-p0 ?>  end
 => nil 
ruby-1.9.2-p0 > defined? x
 => "local-variable" 

I don’t get an error trying to override the operator with a method definition, but it doesn’t work, either. Honestly, Ruby is so permissive I half expected the override to work anyway! Another clue that you’re dealing with an operator is that it has no receiver. That’s why you give it the object, instead of calling it from a receiver, like the nil? method:

ruby-1.9.2-p0 > @x.nil?
 => true 
ruby-1.9.2-p0 > @x.defined?
NoMethodError: undefined method `defined?' for nil:NilClass
	from (irb):8
	from /Users/bellmyer/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

While defined? is most valuable (and most commonly used) in a boolean context, there may be meta-programming applications where you’d want to what type of “thing” you’re dealing with. While you can always use the .class method, you have to already know that the object is defined. In the world of meta-programming, that’s often a luxury you don’t have.