Hi David, dblack / wobblini.net wrote: > I think I'm not understanding what you mean by the context of an > object. Your example involves two different objects (I assume), so > I'm not clear on which object's context/type you're referring to. Or > do you mean the context in which a given method name appears? "Context" here is the knowledge that certain methods are not only defined, but that they have certain semantics -- i.e. Can#open and Connection#open have the same name, but they do different things. If an object were both a can and a connection (but not necessarily instances of Can or Connection), it could define #to_can and #to_conn methods. Then any method requiring a can could call #to_can on the argument, and expect the #open method on the object returned to behave as expected. #to_can could of course return a Can instance, but I just don't see the reason why that should be the only option; that would mean that all cans must inherit from Can, which in this case of course is reasonable, but it isn't always. If we require that the object be of a certain class, we make inheritance about type, and not just behavior. That's why I think we should (where it's not absolutely necessary) disregard the class of the return value of the #to_* methods, and instead just expect it to behave properly. >> str = "foo" >> str.object_id #=> 23456248224800 >> str.to_str.object_id #=> 23456248224800 > Well, at least it's not behaving differently :-) Okay, bad example :P > What I mean is: there's already a convention of using to_* for these > class-specific conversion methods, so if you want to create a system > of true type (as opposed to class) conversions, to_* might be a > misleading name. > > In other words, I'm not suggesting that how the core to_* methods work > should be a model for how everything should work, but only that > anything that doesn't work the way they work maybe shouldn't be called > to_*. You do have a very good point there -- the #to_* methods have been used in a rather non-quacky way. Perhaps #as_*? Basically, I think of objects as pieces of information. This information can have several different representations, but the type should always be about the information, not the representation. class DollarAmount def initialize(amount) @amount = amount.to_f end def to_str "$#{@amount}" end def to_f @amount end end Here the information is both the amount of dough, *and* the fact that it's in dollars, and not yen. There a two other representations of the instances of the class, a string and a float -- these representations contain less information than the original object would, but they can be manipulated in ways a DollarAmount can't, unless you define lots and lots of methods yourself. The reason I think we should use the #to_* methods on received arguments (I'm not sure whether or not you agree that it's a good idea) is that I believe it to be the responsibility of a method to decide which representation of an argument it wants. One method may convert the DollarAmount to euro, so it needs the numeric representation -- but it should be smart enough to be able to receive a DollarAmount instance, and get the relevant representation itself. def dollar_to_euro(dollars) dollars.to_f * D2E end The reason I think we should disregard the class of the objects returned by #to_* is that it decreases the flexibility of Ruby -- sometimes you want to create a completely different implementation of a "type", like, say, a hash, but with the exact same interface. An OrderedHash, perhaps. Should the author then be forced to have his class inherit from Hash, when really the implementation is completely different? #to_hash should return a hash, but must it be a Hash? Daniel