1. Computing

Overloading Operators

By

Overloading Operators

If you have two objects a and b, what does a + b equal? What does it even mean to add two arbitrary objects together? For Ruby's base types (numbers, strings, etc), sensible and useful behavior has been added. But for any types you define yourself, you'll get a simple undefined method exception if you try to add them. It's very easy to define a behavior though.

Operators are just methods in Ruby. All of the arithmetic operators in Ruby just call methods on the left hand operand. So in the example above, a + b, what you're really doing there is a.call(:+, b). You're calling the method called + and passing b as an argument. This is the same with all of the arithmetic operators.

Note that this is only true of the arithmetic operators. Other things that could be considered operators (and are considered operators in other languages) cannot be defined this way. For example, the method call operator (the dot operator) cannot be defined in this way, but you have method_missing for that. The call operator itself (the parentheses) operator cannot be defined in this way. And a few other things like the .. Range operator cannot be defined as a method. Only the arithmetic operators +, -, *, /, % and **, shift operators << and >>, bitwise operators |, ^ and &, comparison operators ==, ===, >, <, >=, <=, ⇔ and =~ and index operators [] and []= may be defined.

Defining Operators

Defining an operator is as easy as defining a method (because it is defining a method). Just remember that you'll be defining the operator for the left-hand side of any expression. If you have objects of different type, a + b will not necessarily do the same thing as b + a. To define an operator, simply define a method with the same name as the operator. In this case, symbols are allowed as method names. So, if you had a Money class that represented an amount of money, you could define the + and - methods to add or subtract sums of money.


class Money
  attr_accessor :value

  def +(b)
    result = dup
    result.value += b.value
    return result
  end

  def -(b)
    result = dup
    result.value += b.value
    return result
  end
end

And that's it. You can now add two and subtract Money objects. And not only that, but because of duck typing you can also add and subtract any objects that respond to the value message. So if you were to buy a car, the car's value could be subtracted from your balance.

Note that you must duplicate your object in order for this to really make sense. When you say a + b, you're really creating a third object to represent the result. No one expects a + b to modify either a or b. The line result = dup will duplicate the current object, in this case a from the left-hand side of the expression. The operator method should return the result.

What About Assignment?

Assignment is handled differently in Ruby. In some other languages, you can reassign the assignment operator to mean something different, but not in Ruby. And as for the arithmetic assignment operators, you they take their cues from the assignment operators.

There is no way or no need to assign the += type operators. The expression a += b is the same as a = a + b. Ruby uses the existing addition operator as well as its own assignment mechanics to implement the various assignment operators.

Non-Arithmetic Idioms

All languages have idioms. In Ruby, one common idiom is to add to a collection using the "shift left" operator. In other words, "shift this object onto the collection." This is similar to +=, add an element to the collection and assign it to the collection. However, it's faster as this doesn't create a third object. This is used with Ruby Arrays and Strings. You'll often see things like a = [1,2,3]; a << 4. This type of usage is implemented in the same way with the same methods, but a third object should not be created, the method should return self.

A More In Depth Example

We talked before about a Money class. A lot of applications need to deal with money, and the default numeric classes just can't cut it. For instance, many applications cannot have fractions of a cent, and the Money class must be able to deal with cents greater than 100 or less than zero. In other words, the application programmers need a class that allows you to easily add and subtract amounts of money without having to worry about the details.

So we create our Money class. First thing that you'll notice is that the actual money is represented with two Fixnum objects representing the dollars and cents. At the core of the logic here is the fix_cents private method, which will ensure that the number of cents doesn't go above 99 or below 0 and adjust dollars accordingly.

A method with the simple (and constant) name of S is also defined. It looks like a dollar sign, so you can say things like S(10.22) + S(4.99) and this one-line method has turned a simple class into a simple DSL that can be used transparently for any application.

Without further ado, the code.


#!/usr/bin/env ruby

def S(*m)
  Money.new(*m)
end

class Money
  attr_reader :dollars, :cents

  def initialize(dollars, cents=0)
    if dollars.is_a?(Fixnum)
      @dollars = dollars
      @cents = cents
    elsif dollars.is_a?(Float)
      @dollars = dollars.floor
      @cents = ((dollars - dollars.floor).round(2) * 100).to_i
    end
  end


  def add(m)
    @dollars += m.dollars
    @cents += m.cents
    fix_cents
  end

  def +(m)
    result = dup
    result.add(m)

    return result
  end


  def subtract(m)
    @dollars -= m.dollars
    @cents -= m.cents
    fix_cents
  end

  def -(m)
    result = dup
    result.subtract(m)

    return result
  end



  def to_s
    "$#{@dollars}.#{"%02d" % cents}"
  end

private
  def fix_cents
    if @cents >= 100
      @dollars += @cents / 100
      @cents -= (@cents / 100) * 100
    end

    if @cents < 0
      # Since @cents < 0, the division will result in
      # a negative number, so use +
      @dollars += @cents / 100
      @cents = (@cents / 100) * -100 + @cents
    end
  end
end

S = Money

Note the add and subtract methods are used to do the actual math. The operator methods simply duplicate the left-hand object and use these methods to do the math.

  1. About.com
  2. Computing
  3. Ruby
  4. Beginning Ruby
  5. Object Oriented Programming
  6. Overloading Operators

©2014 About.com. All rights reserved.