Hacker News new | ask | show | jobs
by premchai21 4960 days ago
Among other things, MyModule.camelize doesn't provide an extension point for polymorphism. Consider a serialization library in which different classes should be able to have separate freeze/thaw behavior. It's possible to do this in current-day Ruby without easy collisions, but it tends to involve cumbersome manual prefixing or emulating method dispatch yourself.

Methods being namespaced in packages separately from the class hierarchy is something CLOS has that I miss in almost all the more Smalltalk-y languages.

1 comments

MyModule.camelize doesn't support polymorphism (you could even call it procedural programming in a OOP shell), but something like Camelizable.new(my_string).camelize does. I'm still convinced that what Ruby needs is more object composition (and maybe a way to override default literal construction), and that refinements are a cannon to shoot a mosquito.
Camelizable.new(my_string).camelize does not support polymorphism, because it does not depend on the type of my_string.

Which means if I want to add camelizable to my AnnotatedString I have to monkey patch Camelizable#initialize.

No, it means that the implementation of Camelizable#camelize needs to be polymorphic w.r.t. my_string. Since Ruby is duck-typed, presumably the implementation of "camelize" would first check to see if the receiver responded to "gsub", then do the necessary substitutions. (Actually, a better implementation would be to first check if my_string responds to "camelize" and call that directly if it does.) In the end, this is a classic decorator pattern.
if you go the way of a decorator pattern, say, I write my `IntegerCamelDecorator` since your `Camelizable.new` does not support numbers, then what is the point of `Camelizable` to start with? You can just have a mirror hierarchy of `CamelAdapters`.

Of course, you can implement `Camelizable.new` so that it performs a dynamic dispatch itself, by looking up in a `CamelizableRegistry`. You can even have this built up magically with reflection.

You can, of course do everything, but a method call in ruby still only dispatches on self, and if that is fixed the method cannot be polymorphic, if you think otherwise we can agree to disagree.

You're focusing on the #new call. The actual #camelize call is still dispatching based on the Camelizable object which is parameterized with my_string. Ok, so it's not true call-site polymorphism...fine. I'd argue that's an unimportant implementation detail, but if you prefer (and what I frequently do in my own code) you could have something more like:

    Camelizable(my_string).camelize
where now Camelizable does the dynamic class lookup to choose the correct decorator for my_string. This turns #camelize into a truly call-site polymorphic call and, at least in my opinion, is far more readable/reasonable than either monkeypatching or refinements.