Too Cool for Internet Explorer

Wednesday, February 6, 2008

Ruby duck typing effect


Last week we could see questions about to_s and to_str on the forum.

Some assumptions too:

  • Appears to be to_str an alias for to_s
  • Most classes implement to_s, just a few implement to_str.
  • There existences just confirm Ruby immaturity.
  • Some are automatic casting others are a "do it yourself" version.

So why are these to_str, to_int, to_ary methods there ?

There is a pragmatic example to help us with Roman numbers.

A Roman number is a representation of an integer, it is not a number it self, but has an integer behavior. So, for practical purposes, if we have a Roman number object, acting like an integer one.

To achieve that, Ruby has the "conversion protocols" concept, which means, an object may convert itself to an object of another class, and there are three ways to do that.

  1. Non strict methods like to_s and to_i. They may be applied on objects that are just near the desired behavior. On the Roman number example, we probably need the to_s method to get a string representation of a number.
  2. Strict methods like to_str and to_int. That are only used on objects that naturally have this kind of behavior. On the Roman number example, they are clearly representing integers, so, they should implement to_int.
  3. Numeric coercion, which let methods deal with different numeric parameters, based on the coerce method. It takes two numbers (a receiver, and a parameter), it returns a two-element array with representations of these numbers, but with parameter on the first element and receiver on the second. These two array elements are objects of the same class, so, therefore they can be added, subtracted, multiplied, compared, etc.

That is an evolving process, for example: the Pathname object responds different to to_str method on Ruby 1.8.6 and 1.9.0. It was considered a regular string behavior in the past, not any more.

A code to think:

require 'pathname'
path = Pathname.new("/tmp/myfile")
puts '================ strict methods'
name = path.to_s # not needed on 1.8.6
puts path.class
puts path.respond_to?(:to_str)
puts path.respond_to?(:to_s)
heading = "File name is " + name
puts heading
heading = "File name is " + path
puts heading # will not work on 1.9.0
puts '================ non strict methods'
def evaluate(parm_object)
if parm_object.respond_to?(:to_str)
puts parm_object + '2'
else
puts parm_object + 2
end
end
evaluate('2')
evaluate(2)
puts '================ numeric coersion'
puts (3.8).coerce(9).inspect
puts 8.coerce(3.5).inspect
puts (7.2).coerce(4.1).inspect
puts 4.coerce(7).inspect
puts '================ end of examples'

No comments: