> Some people...I tell you. Hey, I know! How about I put in an RCR for 'alias e
> end'
>
>    class X
>      def whatever
>        if something
>          # ...
>    e e e
>
> Now that's progress! :))

:O)

I'll start changing all my "rescue SomeException => e" statements right
away!

> Oh, nice. And actually it can be easily done. We already have the ability to
> define a named layer of singleton via #extend. This actually creates a mixin
> on the singleton class. So the extending module remains a seperate entity
> from the singleton itself, and having a descernable name shoudl be fairly
> easy to manipulate/remove/replace. Good thinking, Peter! I like it.

It's a better idea than what was originally in the back of my head: if
wraps have the same indicator, they belong to one layer, and you can
remove/redefine them based on indicator... But the new idea makes layers
more explicit, that's good!

> Definitely wouldn't want to scarfice optimization. A singleton layer only
> needs to be separate when it is explictly told to be so (i.e. extend) or when
> antoher method of the same name already exists in the default singleton, at
> which point it bumps up to a next layer, and thus acts as a wrap upon a wrap.

Of course. But it would still bother me when there's one method that is a
very popular wrappee. Then you get a picture like this:

w |  |  |  |
r-m1-|--|--|--
a |  |  |  |
p-m2-m2-m2-m2-
p |  |  |  |
e-m3-|--|--|--
e |  |  |  |

So the wrappee has three methods m1, m2 and m3, and m2 is wrapped and each
time there is a new singleton layer. When calling m2, we need to go over
all the layers anyhow. But for m1 and m2 we wouldn't. Ideally we would
have this picture:

w          |
r----------m1-
a          |
p-m2-m2-m2-m2-
p          |
e----------m3-
e          |

So rather a chain of methods than a chain of layers. Unless of course you
would indicate shortcuts:

w |  |  |  |
r-m1----------
a |  |  |  |
p-m2-m2-m2-m2-
p |  |  |  |
e-m3----------
e |  |  |  |

Don't know if those pictures make sense.

> Ah, I think I see what you're saying: If I just use def without a super, how
> is the interpretor to know that this amounts to a redef and therefore should
> drop the old wraps? So either we have to use redef on all occassions of
> redefinition, or have some sort of wrap gc. Such a gc could work by
> eliminating wraps that fail to execute after the execution of their
> corresponding method, but such gc adds additional overhead, which I don't
> like. Yet this also plays heavy into the fact of when and when not to
> eliminate wraps. Consider what happens if we use a separate keyword like
> def:wrap for defining wraps. If I first define a wrap and then add a second
> wrap, but this time with no super, we have the same difficulty, but also we
> now have something specifically called a wrap posing as the actual method!
> Moreover (and this applies to my notation as well) what if there is a way to
> redefine the outer most wrap layer, or even eliminate it. Would those "free
> floating" wraps now come back into play?

Actually such a gc wouldn't make things much better. Wraps can
conditionally call super. Suppose a wrap does a security check and
decides you shouldn't call the method it wraps and raises an exception
instead.

As for your way vs. the explicit wrap/redef way, I think either way there
is some ambiguity. But the latter allows the programmer to specify his
intention, and the Ruby interpreter can act accordingly. The programmer
can specify it wrongly, but Ruby shouldn't worry about that.

> It's funny that this comes up, b/c I have started to think, in the back of my
> mind, that inner wraps --and by that I specifically means wraps that are
> flushed with the redefinition of the core method, are really of less use than
> I had orginally thought.

Sorry, that's me putting subliminal messages in my mails...

> Certainly a tangle to unravel here. I pray you can shed some clarity on this.

Hey, you finally spelled clarity correctly :-)

Anyway, I was thinking about the relation wrapping-subclassing. When we
make a subclass, and define a method that already exists, then there are
two cases: we call super in the new method or not. The same thing applies
to wrapping. So why is there no problem in the case of subclassing, and is
there a problem in the case of wrapping? Maybe it is because subclassing
explicitly adds a layer over the superclass, while wrapping currently does
not. So what if we look at wrapping as sneaking layers into the class
hierarchy? Such a layer would use the same syntax as subclassing. So if we
add a layer L to class C, that would be the same as adding a subclass L to
C, but everywhere where C is used, really L is used behind the scene. It
combines well with the new idea of layers.

> Hmm... Not sure if I completely understand yet, but as far as I can at this
> point imagine, I can only really see data private to the aspect as module
> varaibles (or local instance variables when we get those), psuedo code:
>
>   module MyAspect
>     wrap somemethod
>       @@myaspect_private_info = :testmode
>     end
>   end
>
> @@myaspect_private_info could have no effect on the class that this aspect is
> applied to even if it oddly had a class variable of the same name --at least
> I think it could work like that. Actually I'm not sure. Other wise I don't
> know how to go about it.

OK, but recall my example of a bidirectional link. We could have an aspect
do all the dirty work of maintaining the link and keeping it consistent.
We can't use module variables for this, because we need to keep the links
for a bunch of classes. Well, actually we can, but we'd need to keep
hashes that map objects onto their linked counterparts. But it's more
logical to store the links between two objects in those objects
themselves. But to understand that this is AOP, you'd need to let go of
the fact that AOP isn't all wrapping. Even AspectJ is not all about
wrapping. In AspectJ, it would look like this (don't count on my syntax
being correct though, I easily mix up syntax):

  class A {}
  class B {}

  aspect BiDirLinkAB {

    private A B.linkToA = null;
    // a link to B in class A, but class A can't see 'linkToA'
    private B A.linkToB = null;
    // a link to A in class B, but class B can't see 'linkToB'

    public void A.linkTo(B obj) {
      if (linkToB) linkToB.linkToA = null;
      // Note that although this method goes into class A, it can access
      // linkToA in class B
      linkToB = obj;
      if (obj.linkToA) obj.linkToA.linkToB = null;
      obj.linkToA = this;
    }

    public void B.linkTo(A obj) {
      // analogous
    }

    public B A.getLinkToB() {
      return linkToB;
    }

    public A B.getLinkToA() {
      return linkToA;
    }

  }

There's no wrapping, but it's cross-cutting since you have one entity (the
aspect) containing the code for maintaining data in two separate classes.

> Already started-in on it. Give me a day or two. I am facing one problem
> though. Our use of the words inner and outer, up til now, have been a bit
> loose, either meaning tightly-linked and loosely-linked, or meaning
> non-persistent and persistent upon redef, respectively. So I'm not sure in
> which manner I should to give them a specific definition.

I remember we had terms for both tightly/loosely linked and
(non-)persistence, but they never caught on. But if it really makes a
difference, we should try to consistently use different terms.

> Oh, that's interesting. How do you do AOP without wraps? I do see what you
> mean though. Certainly wraps can be used as merely a generic/implict means of
> subclassing. Something we can already in part do using singletons. I wonder
> how one defines AOP exactly then. Are you thinking that if a wrap becomes
> intrinsic that it is no longer AOP? Or does an advice have to cross-cut over
> more than a single method to be AOP? What if it does the former intrinsic
> behavior, but in fact cross-cuts across different methods and classes.
> Perhaps tricky but certainly possible. How does one draw the line? What the
> heck is this thing AOP we keep talking about anyway!? ;)

Well, the example above of the bidirectional link is an example that
doesn't involve wrapping. It's a bit like a mixin, but one that is
partially mixed into one class and partially mixed into another class.
Anyway, your last question is a research question, I can't answer those
off the top of my head.

> Wow. Can you tell me more about how that works? That's sounds quite
> interseting. What level of granularity do you mean? How does it work?

I'll try, it's been a long time and Google can't find the website anymore
(I remember then it was the first hit I got, weird). But if you think
about it, optimizations are program transformations, and so is adding
wraps. The latter is less obvious in a language like Ruby, but one way to
look at AspectJ is that you describe the classes and the different
aspects, and they are really woven together to produce the complete,
full-fledged application. So that's the general idea of AOP: describe the
different aspects and how a code weaver can weave them together. Using
wraps is really just the tip of the iceberg, but the fact that AspectJ
uses that concept means that it is probably well understood at this
moment.

But about optimizations... The example I remember is that of merging
loops. Suppose you have code like this:

  data.each do |d|
    # process d, produce some data2
  end
  # use data 2
  data.each do |d|
    # process d, produce some data3
  end
  # use data 3

So given some 'data', we use it in two subsequent calculations. We're
running over the data twice, but if we merge the loop, we do it just once:

  data.each do |d|
    # process d, produce some data2
    # process d, produce some data3
  end
  # use data 2
  # use data 3

However the two separate calculations are not entangled. For this simple
case, that may be less of a problem, but for realistic examples it can
turn out bad. What we then want to do is write the program as in the first
version, but indicate that the loops should be merged and write an aspect
that shows how to do it.

  data.each:merge do |d|
    # process d, produce some data2
  end
  # use data 2
  data.each:merge do |d|
    # process d, produce some data3
  end
  # use data 3

I used indicators by lack of something better :-) Showing how to merge the
loops is then done by giving a template:

  data.each:merge do |<it>|
    <code1>
  end
  data.each:merge do |<it>|
    <code2>
  end

produces

  data.each:merge do |<it>|
    <code1>
    <code2>
  end

This is just a rough idea, but what I read was such a rough idea. But also
this is a research area still. As for different in granularity; the above
is statement based, while wrapping is method based.

> Great. Although I'm not sure I understood what the misunderstanding is/was.
> But just the same, I certainly think we have come along way toward common
> understanding.

/me thinks so too.

Peter