“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:
-
What happens if coerce() cannot produce such a result when there
are no such “compatible” objects ? Apparently coerce() raises an
ArgumentError exception. Right ?
-
How can I make MyClass instances coercible ? That must be possible,
because 1.coerce( “2”) works, yet “2”.coerce( 1) does not…
-
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.
-
Can I redefine coerce() ? If so, when is it called ? (I tried,
it is not called…)
-
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