Coerce(), what protocol to implement it

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

···

Web: http://hdl.handle.net/1030.37/1.1
Phone: +33 (0) 4 92 27 74 17

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 :wink:

···

At 03:45 30/04/2004 +0900, you wrote:

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


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

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)>

/Christoph

“Jean-Hugues ROBERT” jean_hugues_robert@yahoo.com schrieb im Newsbeitrag
news:6.0.1.1.0.20040429190535.01cd1ef0@pop.mail.yahoo.com

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.

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

  2. 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

Jean-Hugues ROBERT wrote:

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

  2. 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 ?

this works for me

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

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

end

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

Google “ruby coerce” provided little help.

You can find some information by searching the ruby-talk archives
http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml for coerce.

HTH

···

At 03:45 30/04/2004 +0900, you wrote:


Mark Sparshatt

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

···

At 08:39 30/04/2004 +0900, you wrote:

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)>

/Christoph


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

Jean-Hugues ROBERT wrote:

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

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 !

Google “ruby coerce” provided little help.
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 :frowning:

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 :wink:

Thanks anyway.

Jean-Hugues

···

At 04:45 30/04/2004 +0900, you wrote:

At 03:45 30/04/2004 +0900, you wrote:

HTH


Mark Sparshatt


Web: http://hdl.handle.net/1030.37/1.1
Phone: +33 (0) 4 92 27 74 17

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

For example, “ri open” gives me

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

Cheers,

Harry O.

this?

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

···

il Fri, 30 Apr 2004 14:04:38 +0900, Jean-Hugues ROBERT jean_hugues_robert@yahoo.com ha scritto::

Is there something like
Class.implementers( meth)
that would return a list of all known classes that implement
some method ?

Jean-Hugues ROBERT wrote:

That link does not work for me :frowning:

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)

···

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 :wink:

Thanks anyway.

Jean-Hugues

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

···

At 14:14 30/04/2004 +0900, you wrote:

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

For example, “ri open” gives me

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

Cheers,

Harry O.


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

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

···

At 17:54 30/04/2004 +0900, you wrote:

il Fri, 30 Apr 2004 14:04:38 +0900, Jean-Hugues ROBERT >jean_hugues_robert@yahoo.com ha scritto::

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’}


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

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

···

il Sat, 1 May 2004 01:12:05 +0900, Jean-Hugues ROBERT jean_hugues_robert@yahoo.com ha scritto::

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.…

Will do. Thanks. EOM

···

At 03:44 01/05/2004 +0900, you wrote:

il Sat, 1 May 2004 01:12:05 +0900, Jean-Hugues ROBERT >jean_hugues_robert@yahoo.com ha scritto::

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


Web: http://hdl.handle.net/1030.37/1.1
Phone: +33 (0) 4 92 27 74 17