coerce(), what protocol to implement it

J

Jean-Hugues ROBERT

Hi,

I need to write a class whose instances can be on the left side of
a + operator. I am ready to implement whatever make the other side
happy. But what is that I need to implement ?

I am trying to understand coerce()

Apparently when a receiver is not happy with the type of a parameter,
it invokes coerce() on the parameter.
if not_happy_with? param then x = param.coerce( self)

coerce() then returns an array of two items. Both items can be new
objects. However coerce() says that they are "compatible".

Question:

1) What happens if coerce() cannot produce such a result when there
are no such "compatible" objects ? Apparently coerce() raises an
ArgumentError exception. Right ?

2) How can I make MyClass instances coercible ? That must be possible,
because 1.coerce( "2") works, yet "2".coerce( 1) does not...

3) Why does 1.coerce( "3") returns [3.0,1.0] instead of [1,3] while
1.coerce( 3) returns [1,3] ?

4) Can I redefine coerce() ? If so, when is it called ? (I tried,
it is not called...)

5) By defining to_str() I can have "xx" + MyClass.new(xx) work. What
do I have to redefine to have 1 + MyClass.new(xx) work ?

Google "ruby coerce" provided little help.

Thanks in advance,

Yours,

Jean-Hugues
 
J

Jean-Hugues ROBERT

Hi,

I need to write a class whose instances can be on the left side of

Sorry, I meant on the right side. 38 and still I can figure it out ;-)
a + operator. I am ready to implement whatever make the other side
happy. But what is that I need to implement ?

I am trying to understand coerce()

Apparently when a receiver is not happy with the type of a parameter,
it invokes coerce() on the parameter.
if not_happy_with? param then x = param.coerce( self)

coerce() then returns an array of two items. Both items can be new
objects. However coerce() says that they are "compatible".

Question:

1) What happens if coerce() cannot produce such a result when there
are no such "compatible" objects ? Apparently coerce() raises an
ArgumentError exception. Right ?

2) How can I make MyClass instances coercible ? That must be possible,
because 1.coerce( "2") works, yet "2".coerce( 1) does not...

3) Why does 1.coerce( "3") returns [3.0,1.0] instead of [1,3] while
1.coerce( 3) returns [1,3] ?

4) Can I redefine coerce() ? If so, when is it called ? (I tried,
it is not called...)

5) By defining to_str() I can have "xx" + MyClass.new(xx) work. What
do I have to redefine to have 1 + MyClass.new(xx) work ?

Google "ruby coerce" provided little help.

Thanks in advance,

Yours,

Jean-Hugues
 
C

Christoph

Jean-Hugues ROBERT wrote:
....
Google "ruby coerce" provided little help.

Thanks in advance,

The coerce framework makes up for the lack of
multiple dispatch aka "method overloading",
whose lack can be a major nuisance when doing
numerical work. You probably should study the
source code of the standard libraries "complex"
or "rational, if you want to get a feel for
using this framework. Here is a scaled down
example


---
class MyClass

def initialize(val)
@value = val
end

def coerce(other)
[MyClass.new(other),self]
end

def -(lhs)
if MyClass === lhs
MyClass::new(@value - lhs.value)
else
x,y = lhs.coerce(@value)
MyClass::new(x - y)
end
end

protected
attr_reader :value
end

p 1 - MyClass.new(5)
p MyClass.new(5) - 1


require 'complex'

z = Complex(1.0,1)

p z - MyClass.new(5)
p MyClass.new(5) - z
 
J

Jean-Hugues ROBERT

Jean-Hugues ROBERT wrote:
....
Google "ruby coerce" provided little help.
Thanks in advance,

The coerce framework makes up for the lack of
multiple dispatch aka "method overloading",
whose lack can be a major nuisance when doing
numerical work. You probably should study the
source code of the standard libraries "complex"
or "rational, if you want to get a feel for
using this framework. Here is a scaled down
example


---
class MyClass

def initialize(val)
@value = val
end

def coerce(other)
[MyClass.new(other),self]
end

def -(lhs)
if MyClass === lhs
MyClass::new(@value - lhs.value)
else
x,y = lhs.coerce(@value)
MyClass::new(x - y)
end
end

protected
attr_reader :value
end

p 1 - MyClass.new(5)
p MyClass.new(5) - 1


require 'complex'

z = Complex(1.0,1)

p z - MyClass.new(5)
p MyClass.new(5) - z
---
#<MyClass:0x28a63e8 @value=-4>
#<MyClass:0x28a6358 @value=4>
#<MyClass:0x28955b8 @value=Complex(-4.0, 1)>
#<MyClass:0x28954e0 @value=Complex(4.0, -1)>

Thanks a lot.

Looking for some example code I had had a look at
ruby/lib/ruby but there were so many files that I was
slightly lost.

Is there something like
Class.implementers( meth)
that would return a list of all known classes that implement
some method ? It could not returned the classes that where
never required but it may help understand the protocols for
common methods that where designed to be redefined

Additionally, is there something like
a_class.sources()
that would return an array of "positions" where source code
for a a_class was "loaded". It would return something similar
to what you get when an exception bombs at these positions.

If so, then
Class.implementers( :coerce).each do | klass |
p "coerce() is implemented by #{klass}, maybe in:"
p a_class.sources.join( "\n")
end
would list the files where there might be examples of how
to implement the :coerce method.

If I come up with a solution for this issue with Introspection,
I will share it here.

Thanks in advance,

BTW: With all the help I received I am well on my way to implement
a Reference class, like a Pointer but with some auto-dereferencing.
OTOH maybe I should investigate a "proxy" type of solution, like
the ones used by Drb or similar stuff.

Yours,

Jean-Hugues
 
J

Jean-Hugues ROBERT

class MyClass
def initialize(val)
@value = val
end

def coerce(other)
[other, @value]
end
end

1 + MyClass.new(5)
#=> 6

For me too. I must have been sleeping when I tried it.
Thanks !
You can find some information by searching the ruby-talk archives
http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml for coerce.


That link does not work for me :-(

I tried with Search (Namazu): coerce =>
Namazu: a Full-Text Search Engine
This index contains 0 documents and 0 keywords.

I tried with (Recent 1000 files, Regular Expression): coerce =>
TIMEOUT may occur. Using Netscape is recommended.

I tried (Subjects, Regular Expression): coerce =>
TIMEOUT may occur. Using Netscape is recommended.

It had happened to me before.
I must be out of luck today too ;-)

Thanks anyway.

Jean-Hugues
 
H

Harry Ohlsen

Is there something like
Class.implementers( meth)
that would return a list of all known classes that implement
some method ? It could not returned the classes that where
never required but it may help understand the protocols for
common methods that where designed to be redefined

If you have ri installed (and if you don't, I'd definitely recommend doing so), try something like

ri <method>

For example, "ri open" gives me

The method named `open' is not unique among Ruby's classes and modules:
Dir::eek:pen, File::eek:pen, Kernel::eek:pen

Cheers,

Harry O.
 
R

Robert Klemme

Jean-Hugues ROBERT said:
Hi,

I need to write a class whose instances can be on the left side of
a + operator. I am ready to implement whatever make the other side
happy. But what is that I need to implement ?

I am trying to understand coerce()

Apparently when a receiver is not happy with the type of a parameter,
it invokes coerce() on the parameter.
if not_happy_with? param then x = param.coerce( self)

coerce() then returns an array of two items. Both items can be new
objects. However coerce() says that they are "compatible".

Question:

1) What happens if coerce() cannot produce such a result when there
are no such "compatible" objects ? Apparently coerce() raises an
ArgumentError exception. Right ?

2) How can I make MyClass instances coercible ? That must be possible,
because 1.coerce( "2") works, yet "2".coerce( 1) does not...

3) Why does 1.coerce( "3") returns [3.0,1.0] instead of [1,3] while
1.coerce( 3) returns [1,3] ?

Just a minor note: this is not true:

irb(main):017:0> 1.coerce 3
=> [3, 1]

Order matters! coerce always returns values in reverse order of the
source.
4) Can I redefine coerce() ? If so, when is it called ? (I tried,
it is not called...)

5) By defining to_str() I can have "xx" + MyClass.new(xx) work. What
do I have to redefine to have 1 + MyClass.new(xx) work ?

It depends: there are two basic options, which are important to
understand:

i) You class can easily be converted to a standard numerical type. In
that case you can use the standard implementation of operators:

class YourClass
def to_i; 1; end
def coerce(x) [x, to_i]; end
def +(o); o + self; end
end

irb(main):034:0> 1 + YourClass.new
=> 2
irb(main):035:0> YourClass.new + 1
=> 2

ii) There is no such easy conversion then you probably need to convert
other types to your class:


class YourClass
def initialize(x="foo"); @x = x; end
def coerce(x) [self.class.new( x ), self]; end
def +(o); self.class.new("addition(#{self.inspect}, #{o.inspect})"); end
def -(o); self.class.new("substraction(#{self.inspect}, #{o.inspect})");
end
end

irb(main):096:0> YourClass.new + 1
=> #<YourClass:0x1016bbd0 @x="addition(#<YourClass:0x1016bc78 @x=\"foo\">,
1)">
irb(main):097:0> 1 + YourClass.new
=> #<YourClass:0x10164940 @x="addition(#<YourClass:0x10164b98 @x=1>,
#<YourClass:0x10164ce8 @x=\"foo\">)">
irb(main):098:0> YourClass.new - 1
=> #<YourClass:0x100cda68 @x="substraction(#<YourClass:0x100cdb58
@x=\"foo\">, 1)">
irb(main):099:0> 1 - YourClass.new
=> #<YourClass:0x100c2a28 @x="substraction(#<YourClass:0x100c2ad0 @x=1>,
#<YourClass:0x100c2b48 @x=\"foo\">)">

Note: coerce might have to become more complex depending on the argument
types. Typically you do something like:

def coerce(o)
case o
when Fixnum, Bignum
...
when Float
...
when String
...
when SomeOtherType
...
else
raise ArgumentError, "Cannot coerce #{o.inspect}"
end
end

Regards

robert
 
G

gabriele renzi

il Fri, 30 Apr 2004 14:04:38 +0900, Jean-Hugues ROBERT
Is there something like
Class.implementers( meth)
that would return a list of all known classes that implement
some method ?

this?

ObjectSpace.each_object(Class){|x| p x if
x.instance_methods.member?'coerce'}
 
M

Mark Sparshatt

Jean-Hugues ROBERT said:
That link does not work for me :-(

I tried with Search (Namazu): coerce =>
Namazu: a Full-Text Search Engine
This index contains 0 documents and 0 keywords.
That's a pecularity of the search engine. If you scroll down the page
you'll see the list of results (there are 209 for coerce)
 
J

Jean-Hugues ROBERT

If you have ri installed (and if you don't, I'd definitely recommend doing
so), try something like

ri <method>

For example, "ri open" gives me

The method named `open' is not unique among Ruby's classes and modules:
Dir::eek:pen, File::eek:pen, Kernel::eek:pen

Cheers,

Harry O.

I tried.
Apparently something is broken.
It just complains about a bad path.

I had a look at ri.bat and apparently it references some weird
E:\Dev\RubyDev\rubyinstaller\....
I tried to fix the offending line. But then I get a msg
about missing ri documentation and some rdoc run to create
documentation.

I ran "rdoc" but it parsed only the files from the directory where
I invoked it (c:\ruby\bin in that case). So I ran it again, but
from c:\ruby this time. That took a while...

Then rdoc what about generate HTML but
"Directory doc already exists, but it looks like it isnt'..."
and rdoc asked me to re-run using some --op option.

I suppose this is when I gave up. What should have I done ?

This might be the Windows packaging issue mentioned in some msg
threads a while ago, I haven't read the msgs carefully.

Thanks for the suggestion anyways. Investigating "ri" is on my
"to do" list now.

Yours,

Jean-Hugues
 
J

Jean-Hugues ROBERT

il Fri, 30 Apr 2004 14:04:38 +0900, Jean-Hugues ROBERT


this?

ObjectSpace.each_object(Class){|x| p x if
x.instance_methods.member?'coerce'}

I haven't played with ObjectSpace yet. Now I can see that it is
an interesting thing ! Thanks.

What I need is almost what you propose. The difference is that
I need to know what classes do actually implement some method,
versus inherit an existing implementation.

i.e.
class X; def meth() end end
class Y < X; end
Class.implementers( :meth) # => X does implement :meth, Y merely inherit it.

It is easy to assert that X actually implements :meth, because
X.superclass does not. However I still don't know how to
filter out Y.

Next step will be to figure out "where" X defines :meth.
A refactoring editor could use that to point to :meth
implementations that may need some refactoring.

Or, regarding my initial need, I could know which file to look
at where :coerce is defined (so that I can understand how it
works, which is something I don't need to do anymore, thanks
to all the gentle answers to my initial question).

Yours,

Jean-Hugues
 
G

gabriele renzi

il Sat, 1 May 2004 01:12:05 +0900, Jean-Hugues ROBERT
I tried.
Apparently something is broken.
It just complains about a bad path.

I had a look at ri.bat and apparently it references some weird
E:\Dev\RubyDev\rubyinstaller\....

that was the broken ri pkg. see a thread appeared here later, try the
newer installer, or look at ruby-doc.org for link on where to download
ri data files and how to setup them
 
J

Jean-Hugues ROBERT

Will do. Thanks. EOM
il Sat, 1 May 2004 01:12:05 +0900, Jean-Hugues ROBERT


that was the broken ri pkg. see a thread appeared here later, try the
newer installer, or look at ruby-doc.org for link on where to download
ri data files and how to setup them
 

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

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,242
Latest member
KendrickKo

Latest Threads

Top