Markus, thanks for taking the time for the lengthy explanation. Although I have one or two open questions, it seems to me that we agree very much on this and previous misunderstandings were probably due to lack of complete understanding. "Markus" <markus / reality.com> schrieb im Newsbeitrag news:1097906211.21256.399.camel / lapdog.reality.com... <snip/> > > > > I don't think so. Name clashes and building proper components with > > > > clean and consistent interfaces (i.e. interfaces that can be used by > > > > humans who have to understand them) are not restricted to particular > > > > programming languages and I think in fact are quite fundamental to > > > > software engineering. > > > > > > With ruby you can have clean human visible interfaces which (at run > > > time) instantiate as name-clash free computer usable interfaces for a > > > small, one-time cost (defs become slightly slower). > > > > I'm not sure whether I understand you correctly here - apparently there is > > something missing: I understand that you generate unique symbols. It's > > not completely clear to me how you integrate that. Taking the former > > example of LibA and LibB which both want to add method "foo" to class > > String. How would that be done using this Symbol extension? > > I have a feeling this is a deeper subject than I can adequately > address in an e-mail tonight, but now is when I am, so: > > I have tackled this problem at least three times in ruby. Each > time I came up with a different solution (in fact, it wasn't until I was > well into the third solution that I recognized the pattern). In each > case, there were the following similarities (ignoring the many > differences): > > * I had started with a prototype that extended the core classes > * I wanted to build on the prototype but limit the extensions to > core classes for "local use" > * I wanted the extensions to be freely available to some body of > code > * I wanted hide them from outsiders, either to protect them, to > protect myself, or to prevent name clashes > * I wanted the extensions to appear as regular methods to the code > that was allowed to call them > * I wanted to be able to mostly forget about how it worked once I > had it working > > The three solutions I've tried that come to mind as follows were > roughly: > > 1) Use proxy/wrapper objects > > In this situation I had initially worried about name-space clashes, > so had used the convention that all methods added to class C by libX > would be named "C#libX_foo"; after I had done a bit of this I realized > that I could add method C#libX that returned a proxy/wrapper object that > "knew" all the methods foo_1,foo_2, etc. Then I could write c.libX.foo > instead of c.libX_foo, but get the same results. > After a bit of this I started having the wrapper objects handle all > the regular methods in the same way as it handled the extensions, so > that I wouldn't have to worry about when to add the libX. I wound up > always adding it, and then (as I refactored) "moving it up stream" so > that I never added it (IIRC, there was a brief chaos in my thinking at > this point where I was effectively calling things like > v.libX.libY.libX.foo because I was unclear about what I had or wanted). Do you mean by "moving it upstream" that you did the wrapping at the point where some instance entered the lib? Example for what I mean: public def foo(o) bar( o.libX ) end private def bar(o) # "o" is already libX proxy o.method_that_is_specific_to_libx() o.standard_method_of_o_s_class() end Interesting enough we seem to be able to use "::" instead of "." making thinks like this possible: "foo".libx::bar Where libx will return the proxy which then can handle bar. This looks like we had separated the method namespace of String methods. But I guess you discovered that already. > Once this settled out, I found that almost all of the libX/libY's > had vanished and I was left with code that mostly looked like what I had > started with. > > 2) Use a method dispatcher > > This was a more ambitious attempt to build on the above. I decided > to trap (using reflection) any attempts to add methods to the core > classes (and had some angst over defining what I meant by this). I then > used my symbol-uniqueification thing to rename the functions and built a > table for redirection via method missing. This was in part dictated by > an attempt to work with code that mimicked part of the semantics of > CLOS (the Common Lisp Object System) as well. > The dispatcher would route messages to the proper extension based > on the caller (as well as on other information); libX would see the libX > version, libY would see the libY version, and others would get normal > method_missing behaviour. So your dispatcher has some similarities with my map approach, probably the main difference being the better syntactic integration (i.e. methods directly callable vs. "map[o.class].call()"). > As I went on, the overhead began to seem > excessive/unneeded (although I don't recall doing any actual performance > testing) so I switched to only going the full dispatch route when there > was an actual conflict. > Most of the CLOS style dispatching melted under duck typing > (*smile* my version) as well. So you did a local extension to Duck Typing (TM) as well? *shudder* :-)) > 3) Extend the object as they come in > > The third situation already had a "taint-wall" of sorts, and so I > played with simply extending the individual objects as they came in. > Not a general solution, but it worked out nicely. > > -------------------------------------- > > The conclusion: in at least three cases I started out with a design > that relied on extending core classes in application specific ways. In > each case I managed to keep the semantics but partition them so as to > minimize/eliminate the effects on outside code. None of them was > perfect but they were all relatively easy to implement and none of them > resulted in cluttering up the application code (except as I was working > out what I wanted). In short, all of them were acceptable. Yes, that's true. When I spoke about std class extensions that should be done with care, then I meant exactly this: if it's done directly, then those methods better fit the class's semantics (and ultimately migrate into some kind of std lib - either Ruby std lib or extension.rb), if done via mechanisms like you describe, much more extensions are acceptable. > Thus my belief that it is safe to extend the core classes if that > seems like the right thing to do. In most cases it will turn out that > the extension is general enough to warrant inclusion in the > "extension.rb" trick bag, and most of the rest will never face the test > of libraryification. But if that awful fate befalls them, a little > meta-programming should be able to solve it. And it *should* be done to avoid nasty effects unless we're talking about small scale scripts. > Ultimately (especially if this comes up again, now that I'm on the > watch for it) I would like an uber-solution. But even not knowing what > that would look like, I am comfortable extending classes as needed, > secure in the knowledge that ruby won't fail me in my hour(s) of need. If this kind of situation (several libs extending core classes) was very common. I don't oversee all Ruby libs and thus don't know how common that is. From what I read here, I get the feeling that it's not too common. Hm... Kind regards robert