1

Lets say I create an object a and give it a method .to_i, why can't this object be added to an Integer?

>> a = Object.new
=> #<Object:0x0000000006cfa9d0>
?> def a.to_int
?>   42
>> end
=> :to_int
>> 3 + a
(irb):5:in `+': Object can't be coerced into Integer (TypeError)
        from (irb):5:in `<main>'

Thanks to Stefan it works!

irb(main):010:1* def a.coerce other
irb(main):011:1*   [other, 42]
irb(main):012:0> end
=> :coerce
irb(main):013:0> 1 + a
=> 43
Eric
  • 163
  • 10
  • It looks like you are looking for `coerce` method: https://stackoverflow.com/questions/2799571/in-ruby-how-does-coerce-actually-work However this would only work for `3 + a`, for `a + 3` you would need to override `+` method. – BroiSatse Oct 15 '21 at 08:38

2 Answers2

2

Adding an integer to an object can be achieved by implementing +, e.g.:

class Foo
  def initialize(value)
    @value = value
  end

  def to_i
    @value
  end

  def +(other)
    Foo.new(to_i + other.to_i)
  end
end

Foo.new(5) + 4
#=> #<Foo:0x00007fbd22050640 @value=9>

In order to add an instance of Foo to an integer, you have to also implement coerce which takes the left-hand value as an argument and returns an array with both values converted to Foo instances, e.g.:

class Foo
  # ...

  def coerce(other)
    [Foo.new(other.to_i), self]
  end
end

This gives you:

4 + Foo.new(5)
#=> #<Foo:0x00007fba600e3e28 @value=9>

The docs for Numeric contain another example.

Internally, coerce is called by Integer#+ if the argument is not an integer: (C code)

VALUE
rb_int_plus(VALUE x, VALUE y)
{
    if (FIXNUM_P(x)) {
        return fix_plus(x, y);
    }
    else if (RB_TYPE_P(x, T_BIGNUM)) {
        return rb_big_plus(x, y);
    }
    return rb_num_coerce_bin(x, y, '+');
}

rb_num_coerce_bin calls coerce and then invokes the binary operator + on the returned values.

In Ruby this would be: (simplified)

class Integer
  def +(other)
    if other.is_a?(Integer)
      # ...
    else
      x, y = other.coerce(self)
      x + y
    end
  end
end
Stefan
  • 109,145
  • 14
  • 143
  • 218
1

You still need to call the to_int method how else does the interpreter know what you want to do?

>> 3 + a.to_int

Ruby doesn't do automatic conversion.

>> 3 + "5" 

This will give the same error, even though "5" has a perfectly fine to_i method. Btw. to int methods in ruby are ususally called to_i in case you want to keep the consistency.

Max
  • 361
  • 4
  • 16
  • Yes, you're right with `to_i` – Eric Oct 15 '21 at 11:07
  • 1
    `to_int` and `to_i` have very precisely defined meanings, and precisely defined use cases when it is correct to use `to_int` and when it is correct to use `to_i`. Saying "to int methods in ruby are ususally called `to_i` in case you want to keep the consistency" is very misleading because it has nothing to do with consistency and all to do with which method is the correct one. – Jörg W Mittag Oct 15 '21 at 11:55