2010/5/10 Jian Lin <blueskybreeze / gmail.com>:
> It is said that when we have a class Point and knows how to perform
> point * 3 like the following:
>
> =A0 =A0class Point
>
> =A0 =A0 =A0def initialize(x,y)
> =A0 =A0 =A0 =A0@x, @y =3D x, y
> =A0 =A0 =A0end
>
> =A0 =A0 =A0def *(c)
> =A0 =A0 =A0 =A0Point.new(@x * c, @y * c)
> =A0 =A0 =A0end
>
> =A0 =A0end
>
> =A0 =A0point =3D Point.new(1,2)
> =A0 =A0p point
> =A0 =A0p point * 3
>
> Output:
>
> =A0 =A0#<Point:0x336094 @x=3D1, @y=3D2>
> =A0 =A0#<Point:0x335fa4 @x=3D3, @y=3D6>
>
> but then,
>
> =A0 =A03 * point
>
> is not understood:
>
> =A0 =A0Point can't be coerced into Fixnum (TypeError)
>
> So we need to further define an instance method `coerce`:
>
>
> =A0 =A0class Point
> =A0 =A0 =A0def coerce(something)
> =A0 =A0 =A0 =A0[self, something]
> =A0 =A0 =A0end
> =A0 =A0end

There is no type checking and conversion.  Normally you would also
return the argument first but in this case exchanging the order seems
OK since the multiplication is not really symmetric.

The std lib does it like this:

irb(main):001:0> 1.2.coerce 3
=3D> [3.0, 1.2]
irb(main):002:0>


Please see my blog post for a complete example:
http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.=
html

> The question is:
>
> 1) who invokes point.coerce(3) ? =A0Is it Ruby automatically, or is it
> some code inside of `*` method of Fixnum by catching an exception? =A0Or
> is it by case statement that when it doesn't know one of the known
> types, then call coerce?

The latter.

> 2) Does coerce always need to return an array of 2 elements? =A0Can it be
> no array? =A0Or can it be an array of 3 elements?

It always needs to return an array of two elements - that's the contract.

> 3) And is the rule that, the original operator (or method) `*` will then
> be invoked on element 0, with the argument of element 1? =A0(element 0 an=
d
> element 1 are the two elements in that array returned by coerce)

Exactly.

>=A0 Who does it?=A0Is it done by Ruby or is it done by code in Fixnum?

The latter.

> =A0If it is
> done by code in Fixnum, then it is a "convention" that everybody follows
> when doing a coerce?

I'd rather say it's the contract of #coerce but you can call it a
"convention" as well.

> 4) So it is really hard to add something to Fixnum's instance method
> `coerce`? =A0It already has a lot of code in it and we can't just add a
> few lines to enhance it (but will we ever want to?)

You do not need to add to Fixnum's coerce.  Rather you add to your
operator implementation.

> 5) The coerce() in the Point class is quite generic and it works with
> `*` or `+` because they are transitive. =A0What if it is not transitive,
> such as if we define Point minus Fixnum to be:
>
> =A0 =A0point =3D Point.new(100,100)
> =A0 =A0point - 20 =A0 =A0 =A0 # =3D=3D> to give =A0(80,80)
> =A0 =A080 - point =A0 =A0 =A0 # =3D=3D> to give =A0(-80,-80)

I think you confused "transitive" and "commutative".
http://en.wikipedia.org/wiki/Commutative
http://en.wikipedia.org/wiki/Transitive_relation

Commutativity is the exact reason for having #coerce.  Please see the blog =
post.

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/