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
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.
···
A::C and want it as an A::D, should I have A::C#to_a_b_d?
–
Ryan Pavlik rpav@users.sf.net
“Another perfectly calculated space-time
splice-n-splice. Now to get back to… wait a second.
I forgot to carry the TWO!” - 8BT