Automatic class conversion / function overloading

T

Tore Halvorsen

Hi! Rather new to ruby, and I'm not quite sure where to look for the
information I want.

In C++ it's possible to have function overloading... ie

void foo(int a) and void foo(rational a)

Do I have to resort to

def foo(a)
case a
when Fixnum ...
when Rational ...
end
end

or is there any other way to do this?

....or perhaps a way to do the functional equivalent of C++s

class rational
{
rational(int a) { ... } // conversion constructor
}

void foo(rational a) should now work for foo(3)
 
R

Robert Klemme

Tore Halvorsen said:
Hi! Rather new to ruby, and I'm not quite sure where to look for the
information I want.

In C++ it's possible to have function overloading... ie

void foo(int a) and void foo(rational a)

Do I have to resort to

def foo(a)
case a
when Fixnum ...
when Rational ...
end
end

or is there any other way to do this?

...or perhaps a way to do the functional equivalent of C++s

class rational
{
rational(int a) { ... } // conversion constructor
}

void foo(rational a) should now work for foo(3)

You can't do that directly. However have a look at
http://www.rubygarden.org/ruby?MethodOverloading

Regards

robert
 
B

Brian Candler

In C++ it's possible to have function overloading... ie

void foo(int a) and void foo(rational a)

Do I have to resort to

def foo(a)
case a
when Fixnum ...
when Rational ...
end
end

or is there any other way to do this?

Essentially it's what you've written. However, you'll find that in more
cases than you expect you don't actually do it. As long as the objects
passed in respond to the same methods, you can just invoke them without
caring what the class is; there is no need for them to be inherited from
some common superclass as would be the case in C++.

As an example:

def foo(logger)
logger << "hello world\n"
end

This works whether logger is a File, a String, or an Array (even though
their common ancestors are Object and Kernel, which don't have a <<
operator). There's no need to write

def foo(logger)
case logger
when File ...
when String ...
when Array ...
end
end

It doesn't work if logger is a Fixnum, but then, it doesn't make sense to
append a log string to a Fixnum anyway.

Have a look through Ruby's built-in library. For example, in 'expect.rb'
you'll see several cases where a function can be called either with a hash
or a string as an argument, and it behaves differently in those cases.

Actually, you're often more interested in what methods an object responds
to, rather than its class. In the logger example you're more likely to have

logger.flush if logger.respond_to?:)flush)

rather than

case logger
when File, IO
logger.flush
end

You're basically assuming a convention that an IO-like object has methods
"<<" to append and "flush" to flush (if it can be flushed). Such conventions
seem to work well in practice. If you came across an object which didn't,
you could add a "<<" method to that object or to its class to do what you
want, or make a wrapper object which normalises the method call interface.

Exception handling is another way to avoid explicit tests for class:

begin
logger.flush
rescue NoMethodError
end

Regards,

Brian.
 
T

trans. (T. Onoma)

You can't do that directly.  However have a look at
http://www.rubygarden.org/ruby?MethodOverloading

That's very interesting. A little aliasing magic? Hmm... (see gears working
inside tommy's head)

Actually I'm more interested in overloading based on arity, rather then class.


Actually, you're often more interested in what methods an object responds
to, rather than its class. In the logger example you're more likely to have

  logger.flush if logger.respond_to?:)flush)

Exactly! This is a good practice to get in too.


--
( o _ カラãƒ
// trans.
/ \ (e-mail address removed)

I don't give a damn for a man that can only spell a word one way.
-Mark Twain
 
F

Florian Gross

trans. (T. Onoma) said:
Actually I'm more interested in overloading based on arity, rather
then class.

I've done a simply library that allows overloading on anything that can
be expressed as a lambda. It also does proper scoring so you can have an
Array and Enumerable version of a method and the right one will get called.

Of course there's not a big difference between this and the common case
when construct except that having the dispatchers outside of the method
lets you easily add new ones from the outside.

I've pasted it to http://www.codepaste.org/view/paste/150 -- maybe it
will be useful to you.

Regards,
Florian Gross
 
R

Robert Klemme

Exactly! This is a good practice to get in too.

Not generally. In the light of duck typing you normally just invoke
methods and get bitten by an exception. Of course, if you plan to use
objects as arguments that support or don't support #flush that might be
the only feasible approach.

Kind regards

robert
 
T

trans. (T. Onoma)

I've done a simply library that allows overloading on anything that can
be expressed as a lambda. It also does proper scoring so you can have an
Array and Enumerable version of a method and the right one will get called.

Of course there's not a big difference between this and the common case
when construct except that having the dispatchers outside of the method
lets you easily add new ones from the outside.

I've pasted it to http://www.codepaste.org/view/paste/150 -- maybe it
will be useful to you.

Cool. I'll have a look at it.

BTW, is there anyway to download codepastes? Maybe I'm missing something
obvious, but I've yet to figure out a simple way to "grab" the code and bring
it down to my machine.

Thanx,
T.


--
( o _ カラãƒ
// trans.
/ \ (e-mail address removed)

I don't give a damn for a man that can only spell a word one way.
-Mark Twain
 
T

Tore Halvorsen

Brian Candler wrote:
[...]
Essentially it's what you've written. However, you'll find that in
more cases than you expect you don't actually do it. As long as the
objects passed in respond to the same methods, you can just invoke
them without caring what the class is; there is no need for them to
be inherited from some common superclass as would be the case in C++.
[...]

Thanks for helpfull info, but still...

I agree, but when trying to make a class for rational numbers, I
wanted to be able to multiply both rational and fixnums to it.

class Rational
attr_reader :u, :d #up, down
def *(other)
Rational.new(self.u * other.u, self.d * other.d)
end
end

This obviously doesn't automatically work when passing a Fixnum as the
argument. The C++ way is to create an automatic converter - or
function overload. So the ruby way here would be:

class Rational
attr_reader :u, :d
def *(o)
case other
when Rational
other = o
when Fixnum
other = Rational.new(o, 1)
end
Rational.new(self.u * other.u, self.d * other.d)
end
end

?


I need a ruby-idioms book :)
 
M

Markus

So the ruby way here would be:

class Rational
attr_reader :u, :d
def *(o)
case other
when Rational
other = o
when Fixnum
other = Rational.new(o, 1)
end
Rational.new(self.u * other.u, self.d * other.d)
end
end

Perhaps. But (as others have noted) there isn't a single unique ruby
way. My ruby way would be more like


class Rational
attr_reader :u, :d
def *(other)
other = Rational.new(other, 1) if other.is_a? Fixnum
Rational.new(self.u * other.u, self.d * other.d)
end
end


But as you can see, I even use the old salient-structure
indentation rules. Depending on the situation, I might also write:


class Fixnum
def to_rational
Rational.new(self,1)
end
end

class Rational
attr_reader :u, :d
def *(other)
other = other.to_rational unless other.is_a? Rational
Rational.new(self.u * other.u, self.d * other.d)
end
end

Which would permit types other than Fixnum, provided they implemented
to_rational. Taking this to its logical conclusion I would surely then
rewrite it as:

class Fixnum
def to_rational
Rational.new(self,1)
end
end

class Rational
attr_reader :u, :d
def to_rational
self
end
def *(other)
other = other.to_rational
Rational.new(self.u * other.u, self.d * other.d)
end
end

...which eliminates all class testing and is fairly easily extensible.
But of course others might have different solutions.

-- MarkusQ
 
R

Robert Klemme

Markus said:
Perhaps. But (as others have noted) there isn't a single unique ruby
way. My ruby way would be more like


class Rational
attr_reader :u, :d
def *(other)
other = Rational.new(other, 1) if other.is_a? Fixnum
Rational.new(self.u * other.u, self.d * other.d)
end
end


But as you can see, I even use the old salient-structure
indentation rules. Depending on the situation, I might also write:


class Fixnum
def to_rational
Rational.new(self,1)
end
end

class Rational
attr_reader :u, :d
def *(other)
other = other.to_rational unless other.is_a? Rational
Rational.new(self.u * other.u, self.d * other.d)
end
end

Which would permit types other than Fixnum, provided they implemented
to_rational. Taking this to its logical conclusion I would surely then
rewrite it as:

class Fixnum
def to_rational
Rational.new(self,1)
end
end

class Rational
attr_reader :u, :d
def to_rational
self
end
def *(other)
other = other.to_rational
Rational.new(self.u * other.u, self.d * other.d)
end
end

..which eliminates all class testing and is fairly easily extensible.
But of course others might have different solutions.

-- MarkusQ

Btw: you know that there are classes Rational, Complex etc. already, do
you?

robert
 
A

Austin Ziegler

I agree, but when trying to make a class for rational numbers, I
wanted to be able to multiply both rational and fixnums to it.

1) There is a Rational class in Ruby already.

2) Look at #coerce (I believe it's either Fixnum#coerce or
Numeric#coerce). This will allow you to coerce numbers into different
forms.

-austin
 
M

Markus

So the ruby way here would be:...
Btw: you know that there are classes Rational, Complex etc. already, do
you?

Yes. But it's a convenient context for discussing the real issues
(why overloading isn't as needed in ruby, how duck typing reduces the
need for class-tests, etc.) since it's reasonable to assume that
everyone knows what the semantics _should_be_ and thus we can focus on
how they are best implemented.

-- MarkusQ
 
T

Tore Halvorsen

Markus said:
Which would permit types other than Fixnum, provided they implemented
to_rational. Taking this to its logical conclusion I would surely then
rewrite it as:

class Fixnum
def to_rational
Rational.new(self,1)
end
end

class Rational
attr_reader :u, :d
def to_rational
self
end
def *(other)
other = other.to_rational
Rational.new(self.u * other.u, self.d * other.d)
end
end

..which eliminates all class testing and is fairly easily extensible.
But of course others might have different solutions.

Thanks :)

.. o O (You learn something every day)
 
T

Tore Halvorsen

Austin said:
1) There is a Rational class in Ruby already.

Figured as much, but I wanted to know how the mechanism is done in
ruby.
2) Look at #coerce (I believe it's either Fixnum#coerce or
Numeric#coerce). This will allow you to coerce numbers into different
forms.

I'll take a look at that. Thanks :)
 
T

Tom Counsell

void foo(int a) and void foo(rational a)
Do I have to resort to

def foo(a)
case a
when Fixnum ...
when Rational ...
end
end

As I understand it, the idiom in ruby would be to, where possible,
ignore the difference and assume that both a (rational) and a (fixnum)
respond to the methods you want to call on them ...

def increment(a) # a could be a Fixnum, Integer, Rational, Bignum
whatever
a += 1 # Rely on the fact that they all respond to the method +
end

If classes might have different methods then you could redefine them
so that they all have the same method:

class Integer
alias :newnamefor :eek:ldmethod
end

Hope that helps

Tom
 
B

Brian Candler

Yes. But it's a convenient context for discussing the real issues
(why overloading isn't as needed in ruby, how duck typing reduces the
need for class-tests, etc.) since it's reasonable to assume that
everyone knows what the semantics _should_be_ and thus we can focus on
how they are best implemented.

If you want to see an example of Ruby idiom in practice, just see
/usr/local/lib/ruby/1.8/rational.rb (or wherever it is on your system)

The stuff with #coerce I never 100% understood, but then I've never had to
use it for any of my own classes. I've only ever seen it used for the
various different ways of representing numbers.

Regards,

Brian.
 
R

Robert Klemme

Ah, I see.
If you want to see an example of Ruby idiom in practice, just see
/usr/local/lib/ruby/1.8/rational.rb (or wherever it is on your system)

The stuff with #coerce I never 100% understood, but then I've never had to
use it for any of my own classes. I've only ever seen it used for the
various different ways of representing numbers.

I saw a really nice explanation of all these Ruby protocols (#coerce,
#to_str etc.) - I don't exactly remember where it was, could be in Pickaxe
II.

Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top