Have you run into this problem before? Maybe an example of 'conversion method explosion' can help? I've never encountered the need to add many (if any) to_* methods besides to_s for debugging purposes, and the need for many sounds almost like a problem with too much casting/converting rather than a utility missing from the language. How is your implementation of 'conversion.rb' different from defining each classes' to_* methods in 'conversion.rb'? But, it has gotten me thinking: Assuming an A object (a) is being converted to a B object (b), like so: #OPTION 1: a.to_b or #OPTION 2: B.new(a) # or appropriate factory method When would one prefer using 1 to 2 (to_b)? Unless you needed polymorphic behavior, the constructor seems like a common idiom for exactly this purpose. The end result of the call is generating an object of type B and the data about constructing a new B object is what goes in B's constructors, right? I guess it has to do with how many different classes will be converted to type B, how many different classes A can be converted to and how comfortable you are coupling A or B to the other. Why don't we care about the coupling of all of our objects to String? Why not add a constructor to String for each of our new objects? OBVIOUSLY this would be a nightmare if only because we can't overload methods based on parameter type, but is there a solid design principle that deals with this at a higher level? I feel like I'm missing a very obvious conclusion to this. Thanks for listening, Josh I've got a sell order at 34 on my IQ -----Original Message----- From: Ryan Pavlik [mailto:rpav / nwlink.com] Sent: Tuesday, June 24, 2003 2:33 PM To: ruby-talk / ruby-lang.org Subject: Re: Standard type conversion mechanism On Wed, 25 Jun 2003 00:27:40 +0900 John Platte <john.platte / nikaconsulting.com> wrote: > Kent Beck's book Smalltalk Best Practice Patterns covers this topic. It > discusses two gotchas to the practice of adding conversion methods to > the source class: > > * No limit to number of methods to be added -- the protocol tends to > grow and grow. > > * It couples the source class to another class it wouldn't otherwise > have knowledge of. These are two exact problems this design solves. Having a separate table means 1) No additional methods added to the class, and 2) Neither class needs to know about the other. > He says there should only be a conversion method when either the 1) > source and destination classes have the same protocol, or 2) there's > only one reasonable way to implement the conversion. His solution for > conversions when neither of these conditions are true is to add a > "Converter Constructor Method" on the destination class. So instead of > string.as(Date), you'd say Date.from_string(string). This keeps the > logic on Date, which is where it belongs. This is a bit of a grey area. I'd still tend toward the side which says two types shouldn't have to know about each other. Any parsers or the like should be external to both, because otherwise, as above, the number of parsers and methods in your class can grow without bound. > As for the proposed code, what would a conversion table buy me? > Wouldn't it just create a third place to put code that knows about my > class? IMHO, the problem Ryan mentioned with NilClass#to_str not > existing should be resolved in accordance with the POLS, but a > conversion table smells like bad design to me. If I'm wrong, I'd like > to better understand the benefits of the conversion table proposal. It _is_ a third place to put code. It just so happens that this is where the code belongs, due to both of the above problems. Given types A and B, there is no reason A or B necessarily know about each other, or are even in the same package/module/etc. They may be totally unrelated, yet it may become desireable for one type to be converted to the other. Thus you need a place for the "intermediary" or "glue" code to be placed. Should it go in A? Or B? It's best that it be in neither A nor B, but in the middle somewhere. That's what the ConversionTable is: the middle. In addition, you get further advantages, in that you can query whether one type can be converted to another, you can make allowances for the class hierarchy (allowing subtypes to be converted to other subtypes when only supertype conversions are available), and you can have the table figure out how to go from A -> D when only A -> B, B -> C, and C -> D conversions exist. (The current implementation doesn't handle that yet, but it could be made to with a little work. In fact, once it figures out a conversion, it could write code and drop it in A -> D that follows the process so it doesn't have to figure it out again.) Further but more abstract discussion: You always have a major advantage when things are programmatic and not purely semantic/convention. For instance, the method #to_s means nothing to ruby, really, other than "a method called to_s". Any further meaning is purely convention... typically it converts to a string. If you have actual programmatic meaning... "this is a ConversionTable, it does this thing", then you gain the ability for your code to do more work for you. This is where the chain conversion mechanism comes in. Instead of writing every conversion yourself (adding a method to every class to go to every other class it needs), ruby can fill in the rest. It also allows your code to do things like ask "what can this object be treated as?" Your code can pick the preferable form, or be satisfied with a less-preferable form (but still be able to function). For instance, if you're printing a result, you may want a String, but you'll be happy with an Integer, too. This opens the door for further semantic processing of values... making semantics "programmatic" themselves. Adding metadata about what an object _is_ ties right into the conversion system. Tagging things with semantics would deepen the conversion chaining greatly. For instance, you could have an Integer tagged as "Unix Time", and ask for an "ISO Date" String. The idea of well-defined semantics, along with context-sensitivity, is an area I've been interested in... the ConversionTable is a (very) simple beginning, but it's a fundamental piece of functionality that solves the problems you've listed above in a fairly elegant manner (even if my code isn't the most elegant implementation). -- Ryan Pavlik <rpav / users.sf.net> "And I'm 10,000 feet up in the air. Dang it." - 8BT