Why doesn't ruby support method overloading?
Method overloading can be achieved by declaring two methods with the same name and different signatures. These different signatures can be either,
- Arguments with different data types, eg:
method(int a, int b) vs method(String a, String b)
- 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.
Function overloading in Ruby
Ruby doesn't support method overloading. In your code, your second definition of foo1
replaced the first. That's why you get an error when you try to pass arguments, the method that accepted arguments is gone.
There is a question on SO about this topic here, with some good explanations.
Does function overloading work in ruby?
That is done all over the place in Ruby. There are several ways to do it.
By using duck typing, you can do:
def meth arg1, arg2 = nil, arg3 = nil
if arg1.respond_to?(:some_method) then ...
else ...
end
end
By the number of arguments, you can do:
def meth *args
case args.length
when 3 then ...
when 1 then ...
end
end
By the class of the first element, you can do:
def meth *args
case args.first
when String then ...
when Symbol then ...
end
end
Using optional arguments, you can do:
def meth arg1, arg2 = nil, arg3 = nil
if arg2 then ...
else ...
end
end
My favorite application of this practice is when I have pairs of setter and getter methods. When no argument is given, the method works as a getter; when an argument is given, it works as a setter. For example, if there is obj
on which a getter/setter method foo
is defined, I can use it in either way:
obj.foo # as a getter
obj.foo(some_value) # as a setter
I learned this from jQuery API.
Avoiding method overloading in Ruby
Personally, I don't see a big problem in branching that way. Although it would look cleaner with a case
def filter(f)
case f
when Car
filter_by_car(f)
else
filter_by_name(f)
end
end
Slightly more complicated example involves replacing branching with objects (ruby is oop language, after all :) ). Here we define handlers for specific formats (classes) of data and then look up those handlers by incoming data class. Something along these lines:
class UserComposite
def filter(f)
handler(f).filter
end
private
def handler(f)
klass_name = "#{f.class}Handler"
klass = const_get(klass_name) if const_defined?(klass_name)
klass ||= DefaultHandler
klass.new(f)
end
class CarHandler
def filter
# ...
end
end
class DefaultHandler # filter by name or whatever
def filter
# ...
end
end
end
Getting wrong number of arguments when trying to overload method
Ruby allows one and only one method for a given unique name, regardless of the method signature. The Ruby interpreter only looks at the names of methods, as opposed to the Java interpreter, which considers parameters as well as method names.
You can only override methods, with the latest version of a method of a given name taking precedence over all previous methods which share that name.
You can, however, get around the inability to overload by taking advantage of the splat (*) operator to pass a variable number of arguments to your method in an array, and then branching based on the number of passed parameters.
def foo(*args)
if args.length == 1
# logic for a single argument
elsif args.length == 2
# logic for two arguments
else
# logic for other conditions
end
end
Why doesn't my ruby method call work? (yield)
It's a whitespace issue. Your problem is in this line:
puts block_splitter(beatles) do |beatle|
# ...
end
The above code is being interpreted like this:
puts(block_splitter(beatles)) do |beatle|
# ...
end
I.e. the ruby interpreter thinks that the block is being passed to the puts
method, not the block_splitter
method.
By assigning a variable and printing the result, you'll see that this works as expected:
result = block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end
puts result
Or, you can define this as a 1-liner, and the ruby interpreter handles it like you expected:
puts block_splitter(beatles) { |beatle| beatle.start_with?("P") }
Or, you could wrap it in extra brackets:
puts(block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end)
method with same name and different parameters(Method Overloading) in Ruby
Ruby doesn't really support overloading.
This page gives more details and a workaround. Basically you create a single method with a variable number of parameters, and deal with them appropriately.
(I'd personally recommend writing one method to recognise the two different "faked overloads" and then one method for each overload, with different names reflecting the different parameters.)
Alternatively, just provide different names to start with :)
Is it possible to do polymorphism by doing overloading?
If the addition is what you're after, you can do it more simply as:
def add(*numbers)
numbers.inject(&:+)
end
add(1) # => 1
add(1, 3, 5) # => 9
If you're looking for a more general solution to the problem of how to provide behavior dependent on the number of arguments, then you use *args
in the signature and then branch based on args.size
:
def foo(*args)
case args.size
when 1
# do something
when 2
# do something else
when (3..5)
# do another thing
end
end
Overloading in Ruby
You could try some meta programming to reach your target.
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 method name and a list of procedures. The method methodname
is created and calls the corresponding method. Which method is determined by the number of parameters (Not type!).
An alternative syntax would be:
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 the frame for your overloaded methods, overload_method
defines one 'overload-method'.
But as already mentioned by Holger:
You should try to adapt to the Ruby way. There is a reason why there is no overloading in Ruby. Methods should only do one thing, not magically decide to do vastly different things just because of different arguments. Instead try to take advantage of Duck Typing and if in doubt, use different methods with meaningful names.
I was curious how I could implement a version with type sensitive overloading. Here it 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\")"
Related Topics
How to Calculate Number of Chars Common to Two Strings
How to Write a Switch Statement in Ruby
Difference Between Class Variables and Class Instance Variables
Get the Name of the Currently Executing Method
What Are the Ruby Gotchas a Newbie Should Be Warned About
When to Use 'Self.Foo' Instead of 'Foo' in Ruby Methods
Paginating an Array in Ruby With Will_Paginate
Regular Expressions With Validations in Ror 4
Open an Io Stream from a Local File or Url
Error When Running Rails App - Execjs::Runtimeerror
Case Statement With Multiple Values in Each 'When' Block
Ruby, Difference Between Exec, System and %X() or Backticks
Difference or Value of These Block Coding Styles in Ruby
How to Get Source Code of a Method Dynamically and Also Which File Is This Method Locate In
How to Install Postgresql'S Pg Gem on Ubuntu
Find Indices of Elements That Match a Given Condition
Ruby 2.4 and Rails 4 Stack Level Too Deep (Systemstackerror)