Coerción

Numeric types define a conversion method named coerce.

Numeric operators are written so that if they don’t know the type of the righthand operand, they invoke the coerce method of the righthand operand, passing the lefthand operand as an argument.

Consideremos este código:

$ cat coerce.rb 
require 'point'

p = Point.new(2,3)
q = 2*p
puts q

Puesto que Fixnum no sabe como multiplicarse por un Point, invoca al método coerce de su argumento (véase el código de point.rb en la sección 14.1.6).

    33	  def coerce(other)
    34	    [self, other]
    35	  end
    36
    28	  def *(scalar)        # Define * to perform scalar multiplication
    29	    return Point.new(@x*scalar,   @y*scalar) if scalar.is_a? Numeric
    30	    return Point.new(@x*scalar.x, @y*scalar.y) if scalar.is_a? Point
    31	  end
Transformando la llamada 2*p = 2.*(p) en p*2 = p.*(2).

Veamos una sesión paso a paso con el depurador:

[09:01]$ ruby -rdebug -I. coerce.rb 
coerce.rb:1:require 'point'
(rdb:1) n
coerce.rb:3:p = Point.new(2,3)
(rdb:1) 
n
coerce.rb:4:q = 2*p
(rdb:1) p p
(2,3)
En vez de saltar sobre la llamada n(ext) entremos en el método usando s(tep):
(rdb:1) s
point.rb:34:    [self, other]
¡Aja! estamos en el código de coerce:
(rdb:1) where
--> #1 point.rb:34:in `coerce'
    #2 coerce.rb:4
(rdb:1) self
(2,3)
(rdb:1) other
2
(rdb:1) s
point.rb:29:  return Point.new(@x*scalar,   @y*scalar) if scalar.is_a? Numeric
(rdb:1) n
coerce.rb:5:puts q
(rdb:1) 
n
(4,6)

The intent of the coerce method is to convert the argument to the same type as the object on which the method is invoked, or to convert both objects to some more general compatible type.

[13] pry(main)> 2.coerce(1.1)
=> [1.1, 2.0]
[14] pry(main)> 2+1.1
=> 3.1
[8] pry(main)> require 'rational'
=> true
[19] pry(main)> z = Rational(1,3)
=> (1/3)
[20] pry(main)> 2.coerce(z)
=> [0.3333333333333333, 2.0]
[21] pry(main)> z.coerce(2)
=> [(2/1), (1/3)]
[22] pry(main)> 2+z
=> (7/3)

Casiano Rodriguez León 2015-01-07