Method overloading

In IronRuby you can create a number of different implementations of a method that each take different parameters; much in the same way that you can do in C#.
E.g.
In BignumOps there is a * (multiply) method of the form: self * other.
Here are the overloads:

[RubyMethod(“*”)]
public static object Multiply(BigInteger self, BigInteger other)
[RubyMethod(“*”)]
public static object Multiply(BigInteger self, double other)
[RubyMethod(“*”)]
public static object Multiply(CodeContext/*!*/ context, BigInteger self, object other)

As you can see, if Multiply is called with a BigInteger (or int for that matter) as the “other” parameter the first method is executed. If it is a double, which maps to Float in Ruby, then the second is executed and any other type as other invokes the third implementation (the context parameter is a hidden support mechanism for providing access to stuff such as the dynamic invocation mechanism).

In other words the DLR is responsible for selecting the correct method overload to call at runtime based on the number and type of parameters.

This is different to how the overloading is achieved in the standard Ruby implementation. In this case you have just one method and this method works out which implementation it should execute itself. This is the equivalent C function in the C Ruby implementation:

VALUE
rb_big_mul(x, y)
VALUE x, y;
{
long i, j;
BDIGIT_DBL n = 0;
VALUE z;
BDIGIT *zds;

if (FIXNUM_P(x)) x = rb_int2big(FIX2LONG(x));
switch (TYPE(y)) {
case T_FIXNUM:
y = rb_int2big(FIX2LONG(y));
break;

case T_BIGNUM:
break;

case T_FLOAT:
return rb_float_new(rb_big2dbl(x) * RFLOAT(y)->value);

default:
return rb_num_coerce_bin(x, y);
}

j = RBIGNUM(x)->len + RBIGNUM(y)->len + 1;
z = bignew(j, RBIGNUM(x)->sign==RBIGNUM(y)->sign);
zds = BDIGITS(z);
while (j–) zds[j] = 0;
for (i = 0; i <>len; i++) {
BDIGIT_DBL dd = BDIGITS(x)[i];
if (dd == 0) continue;
n = 0;
for (j = 0; j <>len; j++) {
BDIGIT_DBL ee = n + (BDIGIT_DBL)dd * BDIGITS(y)[j];
n = zds[i + j] + ee;
if (ee) zds[i + j] = BIGLO(n);
n = BIGDN(n);
}
if (n) {
zds[i + j] = n;
}
}

return bignorm(z);
}

You can see here that there is a lot of type checking and conversion going on. I think that the IronRuby/DLR way is a much more pleasant and maintainable way of developing.

One thing that did worry me though was what happens if someone monkey patches your class. For instance what if I did the following in a Ruby program?

class Bignum
def *(other)
puts “hello”
self + other
end
end

In the C Ruby implementation, the whole C function is overridden and so all that complex type conversion goes out the window and you are left with the simple method given:

>> 0x80000000 * 2
hello
=> 2147483650
>> 0x80000000 * 0.3
hello
=> 2147483648.3

What would happen in the IronRuby case?

I felt it could have gone either way. At first I expected that it would just add a method with the equivalent of the following signature:

[RubyMethod(“*”)]
public static object Multiply(CodeContext/*!*/ context, BigInteger self, object other)

In actual fact, it appears that all the overloaded methods are wiped out. This is perhaps common sense: the DLR now has no type information on the parameters to distinguish the new Ruby method from those written in C#, so it just blats them all.

Even in cases where there where different overloads accepted different numbers of parameters and you could distinguish based on the number of parameters, those get wiped out too. You just get the one method and calls to the method with a different number of parameters causes an ArgumentError.

So all is good and you don’t have to worry about spreading your code out into multiple overloaded methods to make it easier to read.