It is said that when we have a class Point
and knows how to perform point * 3
like the following:
class Point
def initialize(x,y)
@x, @y = x, y
end
def *(c)
Point.new(@x * c, @y * c)
end
end
point = Point.new(1,2)
p point
p point * 3
Output:
#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>
but then,
3 * point
is not understood:
Point
can't be coerced into Fixnum
(TypeError
)
So we need to further define an instance method coerce
:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Output:
#<Point:0x3c45a88 @x=3, @y=6>
So it is said that 3 * point
is the same as 3.*(point)
. That is, the instance method *
takes an argument point
and invoke on the object 3
.
Now, since this method *
doesn't know how to multiply a point, so
point.coerce(3)
will be called, and get back an array:
[point, 3]
and then *
is once again applied to it, is that true?
Now, this is understood and we now have a new Point
object, as performed by the instance method *
of the Point
class.
The question is:
Who invokes point.coerce(3)
? Is it Ruby automatically, or is it some code inside of *
method of Fixnum
by catching an exception? Or is it by case
statement that when it doesn't know one of the known types, then call coerce
?
Does coerce
always need to return an array of 2 elements? Can it be no array? Or can it be an array of 3 elements?
And is the rule that, the original operator (or method) *
will then be invoked on element 0, with the argument of element 1? (Element 0 and element 1 are the two elements in that array returned by coerce
.) Who does it? Is it done by Ruby or is it done by code in Fixnum
? If it is done by code in Fixnum
, then it is a "convention" that everybody follows when doing a coercion?
So could it be the code in *
of Fixnum
doing something like this:
class Fixnum
def *(something)
if (something.is_a? ...)
else if ... # other type / class
else if ... # other type / class
else
# it is not a type / class I know
array = something.coerce(self)
return array[0].*(array[1]) # or just return array[0] * array[1]
end
end
end
So it is really hard to add something to Fixnum
's instance method coerce
? It 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?)
The coerce
in the Point
class is quite generic and it works with *
or +
because they are transitive. What if it is not transitive, such as if we define Point minus Fixnum to be:
point = Point.new(100,100)
point - 20 #=> (80,80)
20 - point #=> (-80,-80)
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…