From: Ryan Pavlik Date: 2003-06-24T04:45:05+09:00 Subject: Standard type conversion mechanism OK, I'm posting this because a few people on #ruby-lang thought it should be posted, so here it is. A number of us desire a more standard, more scalable type conversion mechanism. By "type conversion", I mean you've got one thing (say, an Integer), and you want it to be another (say, a String). Currently we do this: x = 42 s = x.to_s The problem is this is both a loose convention and it doesn't scale well at all. A case-in-point of the former: x = nil s = "" s << x => TypeError: cannot convert nil into String Yet, there's a NilClass#to_s... but this looks for NilClass#to_str. There's not even internal consistency. This leads us to the second point; it doesn't scale. In order to provide a conversion from each type to another, it requires (type^2) methods in each class. (Granted, you're not likely to have a conversion from every type to every other, but this is a worst-case.) There isn't a good way of naming these methods, either. If I have A::B::C and want it as an A::B::D, should I have A::B::C#to_a_b_d? Should I start adding methods to classes that have little relation to other classes, just for quick conversions? This breaks a form of containership, and requires one class know about another one, even completely unrelated. To summarize, here are the cons of the current system: * No standardization. (#to_i, #to_a, #to_s, #to_str, what about other classes?) * Poor scaling. (Lots of #to_* methods) * Inelegant. (Requires one class "know" about another class) OK, enough complaining. I have a working solution: a ConversionTable. Currently, you can find the implementation here: http://mephle.org/conversion.rb Here's an example: require 'conversion' ConversionTable.add(Integer, String) { |i| i.to_s } ConversionTable.add(Integer, Boolean) { |i| i != 0 } ConversionTable.add(String, Integer) { |s| s.to_i } x = 42 p x.as(String) p x.as(Boolean) s = "42" p s.as(Integer) p s.can_convert?(Integer) p s.can_convert?(Array) This provides the following advantages: * Standard. There's no ambiguity about which method to call; things are based entirely on the class/module itself. * Scalable. Adding conversions adds them in one place; no "method pollution". * Elegant. Conversion "glue" is outside the class itself, requiring neither class to know about the others. * Easy to add new conversions. * Scalable part 2. Theoretically, "conversion inferencing" can be done. That is, given A => B, and B => C, you can ask for A => C and it'll figure out how. (Note the current implementation does not handle this.) * Conversions can be queried. * Fits over existing methods and doesn't break backward compatibility. I have an interest in developing some further dimensions to this, as I'm contemplating semantics/unit and context-sensitive programming in Ruby. (So you can say, "this is an integer in bytes, give me a string representation in kilobytes" and it does the magic for you.) I don't have any immediate plans to formalize this as a module (since it lacks the further functionality I'd like, and I don't have the time to finish it), but I know a number of people who are pressing to have (at least) the basic conversion mechanism as a more built-in standard. Thoughts, comments, etc, encouraged. -- Ryan Pavlik "Another *perfectly* calculated space-time splice-n-splice. Now to get back to... wait a second. I forgot to carry the TWO!" - 8BT