tags:

views:

312

answers:

4

Why does ruby addition cannot coerce given string to fixnum and vice verca?

irb>fixnum = 1
=> 1
irb> fixnum.class
=> Fixnum
irb> string = "3"
=> "3"
irb> string.class
=> String
irb> string.to_i
=> 3
irb> fixnum + string
TypeError: String can't be coerced into Fixnum
    from (irb):96:in `+'
    from (irb):96
    from :0
irb(main):097:0>
+1  A: 

You need to set the variable back to itself, since doing a .to_i returns a integer value, but doesn't modify the original object.

>> string = "3"
=> "3"
>> string.class
=> String
>> string.to_i
=> 3
>> string
=> "3"
>> string.class
=> String
>> string = string.to_i
=> 3
>> string.class
=> Fixnum
Garrett
Well, that's clear.
Alex Nikolaenkov
Is that sarcasm?
Garrett
+4  A: 

Because ruby doesn't know whether you want to convert the string to int or the int to string. In e.g. java 1 + "3" is "13". Ruby prefers to raise an error, so that the user can decide whether to to_s the one operand or to_i the other, rather than doing the wrong thing automatically.

sepp2k
That's why I expect automatic coercion from ruby. BTW python says the same: "TypeError: unsupported operand type(s) for +: 'int' and 'str'"
Alex Nikolaenkov
+3  A: 

It spits out an error because ruby doesn't know what you want. String + fixnum is an ambiguous statement. It could either mean that you want to add the int value of the string and the number, or the string value of the int and the string. For example:

str = "5"
num =  3

puts str.to_i + num
=> 8

puts str + num.to_s
=> 53

Rather than taking a guess as to which of those you want, ruby just throws an exception.

Harpastum
+1  A: 

Fixnum#+ is just a method. Simplified it works like this:

class Fixnum
  def +(other)
    if Fixnum === other
      # normal operation
    else
      n1, n2= other.coerce(self)
      return n1+n2
    end
  end
end

coerce is used for automatic numerical type conversion. This is used for example if you do 42 + 3.141. A string in ruby cannot automaticly be converted into numerical values. You could enhance the String class to be able to do this. You just need to do

class String
  def coerce(other)
    coerced= case other
    when Integer
      self.to_i
    when
      self.to_f
    end
    return coerced, other
  end
end

Now you can do

23 + "42" # => 65
0.859 - "4" # => 3.141

This does not work the other way arround. coerce is only for numerical values "23" + 42 won't work. String#+ will not use coerce.

The simplified + is done in Fixnum and not in Integer on purpose. Fixnum and Bignum have their separate methods, because they work internally very different.

johannes