Why doesn't ruby support method overloading?

Ruby

Ruby Problem Overview


Instead of supporting method overloading Ruby overwrites existing methods. Can anyone explain why the language was designed this way?

Ruby Solutions


Solution 1 - Ruby

"Overloading" is a term that simply doesn't even make sense in Ruby. It is basically a synonym for "static argument-based dispatch", but Ruby doesn't have static dispatch at all. So, the reason why Ruby doesn't support static dispatch based on the arguments, is because it doesn't support static dispatch, period. It doesn't support static dispatch of any kind, whether argument-based or otherwise.

Now, if you are not actually specifically asking about overloading, but maybe about dynamic argument-based dispatch, then the answer is: because Matz didn't implement it. Because nobody else bothered to propose it. Because nobody else bothered to implement it.

In general, dynamic argument-based dispatch in a language with optional arguments and variable-length argument lists, is very hard to get right, and even harder to keep it understandable. Even in languages with static argument-based dispatch and without optional arguments (like Java, for example), it is sometimes almost impossible to tell for a mere mortal, which overload is going to be picked.

In C#, you can actually encode any 3-SAT problem into overload resolution, which means that overload resolution in C# is NP-hard.

Now try that with dynamic dispatch, where you have the additional time dimension to keep in your head.

There are languages which dynamically dispatch based on all arguments of a procedure, as opposed to object-oriented languages, which only dispatch on the "hidden" zeroth self argument. Common Lisp, for example, dispatches on the dynamic types and even the dynamic values of all arguments. Clojure dispatches on an arbitrary function of all arguments (which BTW is extremely cool and extremely powerful).

But I don't know of any OO language with dynamic argument-based dispatch. Martin Odersky said that he might consider adding argument-based dispatch to Scala, but only if he can remove overloading at the same time and be backwards-compatible both with existing Scala code that uses overloading and compatible with Java (he especially mentioned Swing and AWT which play some extremely complex tricks exercising pretty much every nasty dark corner case of Java's rather complex overloading rules). I've had some ideas myself about adding argument-based dispatch to Ruby, but I never could figure out how to do it in a backwards-compatible manner.

Solution 2 - Ruby

Method overloading can be achieved by declaring two methods with the same name and different signatures. These different signatures can be either,

  1. Arguments with different data types, eg: method(int a, int b) vs method(String a, String b)
  2. Variable number of arguments, eg: method(a) vs method(a, b)

We cannot achieve method overloading using the first way because there is no data type declaration in ruby(dynamic typed language). So the only way to define the above method is def(a,b)

With the second option, it might look like we can achieve method overloading, but we can't. Let say I have two methods with different number of arguments,

def method(a); end;
def method(a, b = true); end; # second argument has a default value

method(10)
# Now the method call can match the first one as well as the second one, 
# so here is the problem.

So ruby needs to maintain one method in the method look up chain with a unique name.

Solution 3 - Ruby

I presume you are looking for the ability to do this:

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end

Ruby supports this in a different way:

def my_method(*args)
  if args.length == 1
    #method 1
  else
    #method 2
  end
end

A common pattern is also to pass in options as a hash:

def my_method(options)
    if options[:arg1] and options[:arg2]
      #method 2
    elsif options[:arg1]
      #method 1
    end
end

my_method arg1: 'hello', arg2: 'world'

Hope that helps

Solution 4 - Ruby

Method overloading makes sense in a language with static typing, where you can distinguish between different types of arguments

f(1)
f('foo')
f(true)

as well as between different number of arguments

f(1)
f(1, 'foo')
f(1, 'foo', true)

The first distinction does not exist in ruby. Ruby uses dynamic typing or "duck typing". The second distinction can be handled by default arguments or by working with arguments:

def f(n, s = 'foo', flux_compensator = true)
   ...
end


def f(*args)
  case args.size
  when  
     ...
  when 2
    ...
  when 3
    ...
  end
end

Solution 5 - Ruby

This doesn't answer the question of why ruby doesn't have method overloading, but third-party libraries can provide it.

The contracts.ruby library allows overloading. Example adapted from the tutorial:

class Factorial
  include Contracts

  Contract 1 => 1
  def fact(x)
    x
  end

  Contract Num => Num
  def fact(x)
    x * fact(x - 1)
  end
end

# try it out
Factorial.new.fact(5)  # => 120

Note that this is actually more powerful than Java's overloading, because you can specify values to match (e.g. 1), not merely types.

You will see decreased performance using this though; you will have to run benchmarks to decide how much you can tolerate.

Solution 6 - Ruby

I often do the following structure :

def method(param)
    case param
    when String
         method_for_String(param)
    when Type1
         method_for_Type1(param)

    ...

    else
         #default implementation
    end
end

This allow the user of the object to use the clean and clear method_name : method But if he want to optimise execution, he can directly call the correct method.

Also, it makes your test clearers and betters.

Solution 7 - Ruby

there are already great answers on why side of the question. however, if anyone looking for other solutions checkout functional-ruby gem which is inspired by Elixir pattern matching features.

 class Foo
   include Functional::PatternMatching

   ## Constructor Over loading
   defn(:initialize) { @name = 'baz' }
   defn(:initialize, _) {|name| @name = name.to_s }

   ## Method Overloading
   defn(:greet, :male) {
     puts "Hello, sir!"
   }

   defn(:greet, :female) {
     puts "Hello, ma'am!"
   }
 end

 foo = Foo.new or Foo.new('Bar')
 foo.greet(:male)   => "Hello, sir!"
 foo.greet(:female) => "Hello, ma'am!"   

Solution 8 - Ruby

I came across this nice interview with Yukihiro Matsumoto (aka. "Matz"), the creator of Ruby. Incidentally, he explains his reasoning and intention there. It is a good complement to @nkm's excellent exemplification of the problem. I have highlighted the parts that answer your question on why Ruby was designed that way:

> Orthogonal versus Harmonious > > Bill Venners: Dave Thomas also claimed that if I ask you to add a > feature that is orthogonal, you won't do it. What you want is > something that's harmonious. What does that mean? > > Yukihiro Matsumoto: I believe consistency and orthogonality are tools > of design, not the primary goal in design. > > Bill Venners: What does orthogonality mean in this context? > > Yukihiro Matsumoto: An example of orthogonality is allowing any > combination of small features or syntax. For example, C++ supports > both default parameter values for functions and overloading of > function names based on parameters. Both are good features to have in > a language, but because they are orthogonal, you can apply both at the > same time. The compiler knows how to apply both at the same time. If > it's ambiguous, the compiler will flag an error. But if I look at the > code, I need to apply the rule with my brain too. I need to guess how > the compiler works. If I'm right, and I'm smart enough, it's no > problem. But if I'm not smart enough, and I'm really not, it causes > confusion. The result will be unexpected for an ordinary person. This > is an example of how orthogonality is bad.

Source: "The Philosophy of Ruby", A Conversation with Yukihiro Matsumoto, Part I by Bill Venners, September 29, 2003 at: https://www.artima.com/intv/ruby.html

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRamboView Question on Stackoverflow
Solution 1 - RubyJörg W MittagView Answer on Stackoverflow
Solution 2 - RubynkmView Answer on Stackoverflow
Solution 3 - RubyDerek EkinsView Answer on Stackoverflow
Solution 4 - RubybjelliView Answer on Stackoverflow
Solution 5 - RubyKelvinView Answer on Stackoverflow
Solution 6 - RubyDamView Answer on Stackoverflow
Solution 7 - RubyOshan WisumperumaView Answer on Stackoverflow
Solution 8 - RubyMagneView Answer on Stackoverflow