From: Robert Klemme Date: 2004-04-30T16:54:02+09:00 Subject: Re: coerce(), what protocol to implement it "Jean-Hugues ROBERT" 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. > 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 => #, 1)"> irb(main):097:0> 1 + YourClass.new => #)"> irb(main):098:0> YourClass.new - 1 => #, 1)"> irb(main):099:0> 1 - YourClass.new => #)"> 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