Ruby Operator Overload

Posted by irevived1 on August 10, 2016

08.10.2016

Operator Overload

Operator Overload, I have been overloading operators with objects before I was introduced to the term itself. In fact, operator overload is pretty common in the world of programming languages. We just take things for granted. In fact, we take things for granted all the time anyways.

Let’s define what is operator overload. According to Techopedia, Operator overloading is a technique by which operators used in a programming language are implemented in user-defined types with customized logic that is based on the types of arguments passed.

What does that mean?

Well, I don’t quite understand completely either, but let’s do some live coding, shall we?

Let’s take a look at the String class in Ruby. When you define, or create two strings, you can do math on it. Simple add them together and you can easily concatenate the two strings. This allows you to effortlessly join the two together:

[1] pry(main)> string1 = "Hello"
=> "Hello"
[2] pry(main)> string2 = "World"
=> "World"
[3] pry(main)> string1 + " " + string2
=> "Hello World"

If addition works in string, let’s play with subtraction too!

[1] pry(main)> string1 = "Hello World"
=> "Hello World"
[2] pry(main)> string2 = "World"
=> "World"
[3] pry(main)> string1 - string2
NoMethodError: undefined method `-' for "Hello World":String

Oh no, NoMethodError! I wonder why . .

It is because in the String library, the creator did not include the - (subtraction) operator. Therefore, the computer does not know the logic behind it, or the meaning behind that particular operator. Though it may seems very logical to the human brain, it is not to the computer. The user or the creator has to manually define all operator operations for each and every object. The best way to understand it is to treat the operator as the name of the method. Let’s take a look at the code below:

[1] pry(main)> class String
[1] pry(main)*   def subtract(string)
[1] pry(main)*     self.gsub(string,"")
[1] pry(main)*   end
[1] pry(main)* end
=> :subtract
[2] pry(main)> string1 = "Helloooooo"
=> "Helloooooo"
[3] pry(main)> string2 = "ooooo"
=> "ooooo"
[4] pry(main)> string1.subtract(string2)
=> "Hello"

This seems so boring. Let’s make things look more interesting with the help of operator overload.

[1] pry(main)> class String
[1] pry(main)*   def -(string)
[1] pry(main)*     self.gsub(string,"")
[1] pry(main)*   end
[1] pry(main)* end
=> :-
[2] pry(main)> string1 = "Hell no"
=> "Hell no"
[3] pry(main)> string2 = " n"
=> " n"
[4] pry(main)> string1 - string2
=> "Hello"

How cool is that?

You may also override the existing methods as well. Which means…. You can use this technique to mess with your friends !

[1] pry(main)> class Fixnum
[1] pry(main)*   def * (num)
[1] pry(main)*     self - num
[1] pry(main)*   end
[1] pry(main)* end
=> :*
[2] pry(main)> 10 * 5
=> 5

Messing with your friends is not the main purpose of this blog. The important thing is to get the practicality of operator overload into practice. Here is a little demo with doing subtraction and addition with an object called Circle using its area.

class Circle
  @@pi = Math::PI
  attr_reader :area, :radius 

  def initialize( area: nil , radius: nil)
    if area && area > 0
      @area = area
      @radius = (Math.sqrt(area/@@pi))
    elsif radius && radius > 0
      @radius = radius
      @area = @@pi * (radius**2)
    else
      @area = @@pi
      @radius = 1
    end
  end

  # Operator Overload here:

  def +(circle)
    Circle.new(area: (area+circle.area))
  end

  def -(circle)
    Circle.new(area: (area-circle.area))
  end

  def *(circle)
    Circle.new(area: (area*circle.area))
  end

  # having too much fun here, playing with meaningless circle definitions

  def %(circle)
    Circle.new(area: (area%circle.area))
  end

  def /(circle)
    Circle.new(area: (area/circle.area))
  end

  def **(circle)
    Circle.new(area: (area**circle.area))
  end

#this method overrides the puts statement
  def to_s
    puts "Area:\t#{@area}"
    puts "Radius:\t#{@radius}"
  end
end

a = Circle.new(radius: 2)
b = Circle.new
puts a * b

#output
Area:   39.47841760435743
Radius: 3.5449077018110318

Ta da!

I hope you have enjoyed it and have a nice day,

-irevived