Overload in Ruby

I want to use the overload function in Ruby, like many other languages, but Ruby itself does not support this function.

Should I implement it using the method definition method with the *args argument and determine the number and types of arguments inside the method? Some people like:

 class A def foo(*args) case (args.length) when 1 do something when 2 do something-else .... end end end 

You can see, this is really uglier than directly overloading.

I want to know if there are any keywords or any other manners (for example, a metaprogramming module) that may allow me to define the overload method in more detail.

+7
source share
3 answers

You can try metaprogramming to achieve your goal.

See the following code:

 class OverloadError < ArgumentError; end class Class =begin rdoc =end def define_overload_method( methodname, *methods ) methods.each{ | proc | define_method("#{methodname}_#{proc.arity}".to_sym, &proc ) } define_method(methodname){|*x| if respond_to?("#{methodname}_#{x.size}") send "#{methodname}_#{x.size}", *x else raise OverloadError, "#{methodname} not defined for #{x.size} parameters" end } end end class X define_overload_method :ometh, Proc.new{ "Called me with no parameter" }, Proc.new{ |p1| "Called me with one parameter (#{p1.inspect})" }, Proc.new{ |p1,p2| "Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } end x = X.new p '----------' p x.ometh() p x.ometh(1) p x.ometh(1,2) p x.ometh(1,2,3) #OverloadError 

You can define your overloaded method with define_overload_method . Parameters are the name of the method and the list of procedures. The methodname method is methodname and calls the corresponding method. Which method is determined by the number of parameters (not type!).

Alternative syntax:

 class OverloadError < ArgumentError; end class Class def def_overload( methodname) define_method(methodname){|*x| if respond_to?("#{methodname}_#{x.size}") send "#{methodname}_#{x.size}", *x else raise OverloadError, "#{methodname} not defined for #{x.size} parameters" end } end def overload_method( methodname, proc ) define_method("#{methodname}_#{proc.arity}".to_sym, &proc ) end end class X def_overload :ometh overload_method :ometh, Proc.new{ "Called me with no parameter" } overload_method :ometh, Proc.new{ |p1| "Called me with one parameter (#{p1.inspect})" } overload_method :ometh, Proc.new{ |p1,p2| "Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } end 

def_overload defines a frame for your overloaded methods, overload_method defines one "overload method".

But as mentioned by Holger :

You should try to adapt to Ruby. There is a reason why there is no overload in Ruby. Methods should do only one thing, and not magically decide to do completely different things just because of different arguments. Instead, try using Duck Typing, and if in doubt, use different methods with meaningful names.


I was curious how I can implement a version with overload sensitive type. Here he is:

 class OverloadError < ArgumentError; end class Class def def_overload( methodname) define_method(methodname){|*x| methname = "xxx" methname = "#{methodname}_#{x.size}#{x.map{|p| p.class.to_s}.join('_')}" if respond_to?(methname) send methname, *x elsif respond_to?("#{methodname}_#{x.size}") send "#{methodname}_#{x.size}", *x else raise OverloadError, "#{methodname} not defined for #{x.size} parameters" end } end def overload_method( methodname, *args, &proc ) types = [] args.each{|arg| types << arg.to_s} define_method("#{methodname}_#{proc.arity}#{types.join('_')}".to_sym, &proc ) end end class X def_overload :ometh overload_method(:ometh){ "Called me with no parameter" } overload_method(:ometh, String ){ |p1| "Called me with one string parameter (#{p1.inspect})" } overload_method(:ometh ){ |p1| "Called me with one parameter (#{p1.inspect})" } overload_method(:ometh){ |p1,p2| "Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } end 

When you call it with

 p x.ometh(1) p x.ometh('a') 

You get

  "Called me with one parameter (1)" "Called me with one string parameter (\"a\")" 
+4
source

You can check for each argument separately, since they are set to nil if not passed (provided they are passed in order!).

If you insist on very different arguments, I suggest a hash argument with characters for each argument that you intend to .. and approriate.

** UPDATE **

You can also rename methods that are overloaded with more specific names, for example

 def perform_task_with_qualifier_1 
+5
source

The defining characteristic of congestion is that sending occurs statically. In Ruby, sending is always dynamic, there is no other way out. Therefore, overloading in Ruby is not possible.

0
source

All Articles