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