Underpinnings of Method Wrapping

Sorry, source of confusion: I used ‘wrap’ here to mean a combined pre and
post. (Mentally, I’m still repeating def for an intrinsic wraps). Perhaps
better terms are: inner wrap and outer wrap? So with the above I was pointing
out that we could just have a separate keyword for outer wrap definitions.

I know. Inner wrap and outer wrap are fine BTW.

You’re a man after my own heart! :slight_smile: I don’t know if you noticed the research
notes at the bottom of the RCR, but this is essentially one of the notations
I was playing with and very much liked. Perfect for outer wraps. And pdef
actually works pretty well too, once you think about it for a sec. Short and
to the point.

:slight_smile: (And yeah, I noticed :slight_smile:

That would be great, I have some other work to do too. So afterward, I take it
you want to collaborate on a finalized joint RCR on the matter? That would be
excellent. And yes, some good examples are quite important.

I’d love to collaborate on it. (Funny how I tumbled into this just by
defending Matz’s proposal.)

I think so. Could you clearify these two different syntaxes and where they
apply, so that I can be sure that I’m understanding you correctly?

I kept it deliberately vague because I think we still need to make a final
decision on the syntax. But if we use your syntax, wrapping would look
like this:

class X
def y
print “X”
end
end

c = X.new

def c.y
print “(”
super
print “)”
end

def c.y
print “[”
super
print “]”
end

c.y # [(X)]

And the old behavior would look like this:

class X
def y
print “X”
end
end

c = X.new

redef c.y
print “(”
super
print “)”
end

redef c.y
print “[”
super
print “]”
end

c.y #

But then we’d need to make clear that conceptually c gets its own, new
singleton class at creation, and the redefs are in that class. Also the
first redef could have been a def… BTW, singleton classes are equal
to their superclasses currently, so a singleton class isn’t really visible
except for the extra methods (unless I’m overlooking something).

class X
def y
print “X”
end
end

a = X.new
b = X.new
c = X.new

def b.y
print “(”
super
print “)”
end

def c.y
print “[”
super
print “]”
end

a.y # => X
b.y # => (X)
c.y # =>
a.class # => X
b.class # => X
c.class # => X
a.instance_of? X # => true
b.instance_of? X # => true
c.instance_of? X # => true
a.class == b.class && b.class == c.class # => true
a.class.superclass # => Object
b.class.superclass # => Object
c.class.superclass # => Object

Peter

I was thinking about the terms. To really distinguish these two types of wraps
for what they do I think the following terms, or similiar terms, would really
nail them down and prevent any confusion, and also help us think about them
better:

watch -or- monitor = extrinsic/outer wrap
submethod = intrinsic/inner wrap

Does this work for you?

Watch is OK. Monitor sounds too much like the concurrency concept. I’m not
sure about submethod, since that sounds like the opposite of a wrap. Those
intrinsic wraps reminded me of the adapter pattern, although that does not
capture the full idea. But nonetheless, how about adapter for intrinsic?

Also I was wondering, how do we get the results of super with the following
code?

watch foo(*args)
pre
a = something
# blah
post
print a
# blah2
end
end

Same way it happens in Matz’s slides? Sorry, that’s not nice. But I just
combined the pre and post from his slides, and never noticed that you
can’t access the result there either, or at least it wasn’t indicated how.
I think we can either replace post with post(res), or else use a global
variable. Or else reintroduce super.

Note that I used watch instead of pdef just to see what it would look like. Of
course this notation also has the sticking point of what happens if pre and
post aren’t given (error?) and also how does super wrapping fit into this?

I know, I was thinking that same thing. But it’s a complicated matter.
Suppose we’d reintroduce pseudo_super, then what would a call to
pseudo_super do in another context? Oh, anda… If neither pre nor post
are given, that could perfectly be a syntax error provided we have a
separate keyword for a watcher (pdef, or wrap). And what do you mean by
“super wrapping”?

Also, in thinking about the purpose of a “watch wrap” it occurs to me that it
could still potentially effect the object (eg. @a = x) which I’m thinking
should probably be beyond its “write scope”. So perhaps it should be enclosed
in some sort of object-specifc read-only namespace? Kind of like Matz’
proposal:

http://www.rubyist.net/~matz/slides/rc2003/mgp00029.html

But applicable to methods. In other words a “watch” should have access to the
global namespace, but only read access to the local object’s namespace. So a
“watch wrap” is equivalent to a “submethod” using this kind of namespace
restriction:

(using my repeated definition syntax from the RCR)

def foo(*args)
namespace self
a = something
# blah
r = escape { super }
print a
# blah2
end
end

Where ‘namespace self’ means that any effects on the object self, made within
the namespace are “rolled back” at the end. But ‘escape’ means the namespace
doesn’t apply to what happens in its block.

This is a bit confusing, I know. Really just thinking out-loud here. I hope
the concepts I’m trying to convey make sense though.

I think I understand the concept. This “trick” won’t prevent everything,
e.g.,

self.name.gsub!(/^.*$/, “gotcha!”)

Unless ‘namespace self’ extends beyond self to all self refers to directly
or indirectly. Isn’t it needed also for args and r?

But now I’m wondering. Ruby assumes the programmer is the smarter one,
right? So why don’t we just give the programmer the ability to decide what
happens with his wraps on redefinition and have him in good faith do the
correct thing? I’m OK with making it a bit harder to do the wrong thing,
but introducing overhead to make him do the right thing is stretching it.
So I’m definitely for making sure he doesn’t meddle with the arguments to
super or the result of super, since that can be done syntax-wise. But
creating a complete sandbox for the watcher is too much.

So it’s up to the programmer to declare his wrap intrinsic or extrinsic,
and have it respectively removed or kept in case of primary method
redefinition, and if it goes wrong, it’s his fault. That’s how Ruby works:
don’t argue with the programmer, just let him hang himself.

Peter

I was thinking about the terms. To really distinguish these two types of
wraps for what they do I think the following terms, or similiar terms,
would really nail them down and prevent any confusion, and also help us
think about them better:

watch -or- monitor = extrinsic/outer wrap
submethod = intrinsic/inner wrap

Does this work for you?

Watch is OK. Monitor sounds too much like the concurrency concept. I’m not
sure about submethod, since that sounds like the opposite of a wrap. Those
intrinsic wraps reminded me of the adapter pattern, although that does not
capture the full idea. But nonetheless, how about adapter for intrinsic?

OK as in so-so, or OK as in yes? If just so-so we’ll find something better. I
thought of using ‘sub’ to convey the use of super within the method, and its
kinship to subclassing. But I see what you’re saying b/c it’s sort of like
subroutine. In fact submethod would be synonymous with wrap. But I’m not too
keen on adapter either because the method’s interface doesn’t have to change.
Also, in that case we’d probably call a watch, an observer. I don’t know if
we want to refer to them in terms of similiar design patterns. Here’s some
alternatives I came up with for inner wraps: touch, affect, yoke. By the way
I searched google a bit, but I couldn’t find anything in the way of these
terms (except for a number of ruby-talk posts ;-).

Also I was wondering, how do we get the results of super with the
following code?

watch foo(*args)
pre
a = something
# blah
post
print a
# blah2
end
end

Same way it happens in Matz’s slides? Sorry, that’s not nice. But I just
combined the pre and post from his slides, and never noticed that you
can’t access the result there either, or at least it wasn’t indicated how.
I think we can either replace post with post(res), or else use a global
variable. Or else reintroduce super.

:-)) matz posted earlier that he had not good “structure” for doing so.

Note that I used watch instead of pdef just to see what it would look
like. Of course this notation also has the sticking point of what happens
if pre and post aren’t given (error?) and also how does super wrapping
fit into this?

I know, I was thinking that same thing. But it’s a complicated matter.
Suppose we’d reintroduce pseudo_super, then what would a call to
pseudo_super do in another context? Oh, anda… If neither pre nor post
are given, that could perfectly be a syntax error provided we have a
separate keyword for a watcher (pdef, or wrap). And what do you mean by
“super wrapping”?

This is thorny and I’m starting to think there’s no way around using super.
super just can’t take variant args in an outer wrap.

A super wrap is the join points up against super in the method being wrapped.
I like to call it a shrink wrap :slight_smile:

def x
print “X”
end

def x
print “<”
super
print “>”
end

def x
superwrap
print “(”
super
print “)”
end
end

x; puts # => <(X)>

I discoverd the need for this when working on my gui interface. It alows you
to keep the super in the method being wrapped wherever it needs to be
relative to the advices.

Also, in thinking about the purpose of a “watch wrap” it occurs to me
that it could still potentially effect the object (eg. @a = x) which I’m
thinking should probably be beyond its “write scope”. So perhaps it
should be enclosed in some sort of object-specifc read-only namespace?
Kind of like Matz’ proposal:
[snip]

I think I understand the concept. This “trick” won’t prevent everything,
e.g.,

self.name.gsub!(/^.*$/, “gotcha!”)

Unless ‘namespace self’ extends beyond self to all self refers to directly
or indirectly. Isn’t it needed also for args and r?

But now I’m wondering. Ruby assumes the programmer is the smarter one,
right? So why don’t we just give the programmer the ability to decide what
happens with his wraps on redefinition and have him in good faith do the
correct thing? I’m OK with making it a bit harder to do the wrong thing,
but introducing overhead to make him do the right thing is stretching it.
So I’m definitely for making sure he doesn’t meddle with the arguments to
super or the result of super, since that can be done syntax-wise. But
creating a complete sandbox for the watcher is too much.

So it’s up to the programmer to declare his wrap intrinsic or extrinsic,
and have it respectively removed or kept in case of primary method
redefinition, and if it goes wrong, it’s his fault. That’s how Ruby works:
don’t argue with the programmer, just let him hang himself.

You’re right. We can’t save the world with one outer wrap. We need not be
concerned with such “over” protection, just so long as we protect the method
interface.

BTW: did you read ruby-talk:86785?

-t0

···

On Monday 01 December 2003 06:58 pm, Peter wrote:

OK as in so-so, or OK as in yes? If just so-so we’ll find something better. I
thought of using ‘sub’ to convey the use of super within the method, and its
kinship to subclassing. But I see what you’re saying b/c it’s sort of like
subroutine. In fact submethod would be synonymous with wrap. But I’m not too
keen on adapter either because the method’s interface doesn’t have to change.
Also, in that case we’d probably call a watch, an observer. I don’t know if
we want to refer to them in terms of similiar design patterns. Here’s some
alternatives I came up with for inner wraps: touch, affect, yoke. By the way
I searched google a bit, but I couldn’t find anything in the way of these
terms (except for a number of ruby-talk posts ;-).

I’m in the so-so zone. I’ve been looking for suggestions myself, but my
mind is currently a blank. And tired.

:-)) matz posted earlier that he had not good “structure” for doing so.

That proves that it’s a toughie :slight_smile:

This is thorny and I’m starting to think there’s no way around using super.
super just can’t take variant args in an outer wrap.

Yeah, but:

class X
def x(a)
print a
end
end

class Y < X
def x(a)
a += 1
super
end
end

Y.new.x(5) # => 6

We’d need yet another semantics for super in this case.

A super wrap is the join points up against super in the method being wrapped.
I like to call it a shrink wrap :slight_smile:

If you had called it that, I’d have known what you meant right away!

def x
print “X”
end

def x
print “<”
super
print “>”
end

def x
superwrap
print “(”
super
print “)”
end
end

x; puts # => <(X)>

I discoverd the need for this when working on my gui interface. It alows you
to keep the super in the method being wrapped wherever it needs to be
relative to the advices.

Everything seems to point towards super needing to be explicit, except our
wanting to protect the programmer from making mistakes… See below.

You’re right. We can’t save the world with one outer wrap. We need not be
concerned with such “over” protection, just so long as we protect the method
interface.

But to go a bit further, I’m wondering whether we should make a difference
at all and just have the programmer indicate whether the wrap should be
removed on primary redefinition or not - I mentioned the basic idea once
upon a time in [ruby-talk:86551]. Even wraps that are intrinsic can be
generic, e.g.,

def foo(*args)
args.map! { |a|
if a.respond_to? :unpack
a.unpack
else
a
end
}
result = super
if result.respond_to? :pack
result.pack
else
result
end
end

I realize pack and unpack clash with the methods of String and Array, but
that’s what those method indicators are for. But point is that the method
above not necessarily needs discarding, even if it does influence the
arguments and result. So maybe that too is up to the programmer to decide.
If he says the wrap should be kept, and it breaks things, then he should
fix it. If he was right, all’s well.

I know that that’s a break from Matz’s proposal for pre and post where the
call to super is implicit and it is (apparently) easily enforced that the
arguments to super and its results are left alone. I never questioned that
that was a Good Thing. But it’s only skin deep since the internal state of
the arguments and results can be changed anyhow (String#gsub! being an
example); also the internal state of self can be changed, and all
practical protection against that is again only skin deep. We can protect
the interface - or the skin if you want - but to what avail?

So my conclusion is that maybe we should have two separate keywords for
“submethod wraps” and “watch wraps”, and be done with it. Ruby will
discard the former when the primary method is redefined, and will keep the
latter. And the rest is the programmer’s responsibility. What do you
think?

Hmmmm, thinking about it some more, it seems that maybe we’re unifying two
concepts that are really orthogonal. On one hand there’s whether a wrap
should go when the primary method goes or whether it should stay. On the
other hand there’s whether the interface (args + result) is meddled with
or not. I think the former is a functional difference, while the latter
feels like a documentation issue because nothing happens with it at run
time.

BTW: did you read ruby-talk:86785?

I read it, but not thoroughly. It’s on my toread pile for when my mind
unblanks.

Peter

Peter:

You won’t beleive what I just did! I am working on a program for
distrowatch.com and I decided I wanted to change how a method worked in a
standard class, I speedily typed:

class NilClass
def split(*args)
super ||
end
end

Thinking split already existed. Of course then it dawned on me that NilClass
doesn’t have a split function yet, so the super would not work anyway despite
the fact that I’m also not making a subclass. All of which is very telling,
because as a programmer (or as the Ruby interpretor) the following descision
tree must be figured out:

subclassing → exists → def → call super => outer class wrap
subclassing → exists → def → no super => redefine
subclassing → exists → wrap → call super => same as def or ERROR?
subclassing → exists → wrap → no super => same as redef or ERROR?
subclassing → not exists → def → call super => ERROR
subclassing → not exists → def → no super => define
subclassing → not exists → wrap → call super => ERROR
subclassing → not exists → wrap → no super => same as define or ERROR?
not subclassing → exists → def → call super => redefine w/ out. class wrap
not subclassing → exists → def → no super => redefine
not subclassing → exists → wrap → call super => wrap
not subclassing → exists → wrap → no super => same as redef or ERROR or?
not subclassing → not exists → def → call super => ERROR
not subclassing → not exists → def → no super => define
not subclassing → not exists → wrap → call super => ERROR
not subclassing → not exists → wrap → no super => same as define or ERROR?

Lots of fun, huh? :wink: And this doesn’t take into account the descisions of
outer vs. inner wrapping. (Note: class wrap means the effective wrapping of
the superclass’ method; it is necessarily outer) So if you ask me the above
descision tree is too complex, and is really on the verge of becoming too
unwieldy for any ordinary programmer.

If you had called it that, I’d have known what you meant right away!

I will keep calling it that then. :slight_smile: For clearity I’m going to start using
the following terms:

outer - a wrap that remians even when the prime method is redefined
inner - a wrap the is removed when the prime method is redefined
passive - a wrap that does not alter functionality in any way
active - a wrap that alters functionality in some way
shrink - same as superwrap

But to go a bit further, I’m wondering whether we should make a difference
at all and just have the programmer indicate whether the wrap should be
removed on primary redefinition or not - I mentioned the basic idea once
upon a time in [ruby-talk:86551]. Even wraps that are intrinsic can be
generic, e.g.,

def foo(*args)
args.map! { |a|
if a.respond_to? :unpack
a.unpack
else
a
end
}
result = super
if result.respond_to? :pack
result.pack
else
result
end
end

I realize pack and unpack clash with the methods of String and Array, but
that’s what those method indicators are for. But point is that the method
above not necessarily needs discarding, even if it does influence the
arguments and result. So maybe that too is up to the programmer to decide.
If he says the wrap should be kept, and it breaks things, then he should
fix it. If he was right, all’s well.

That’s a good point. And I am leaning in its favor. But to be sure about this
I think we will need to carefully think about how code gets reused. With
patient thinking on this matter, we’ll come to a certain conclusion on how
this should work.

I know that that’s a break from Matz’s proposal for pre and post where the
call to super is implicit and it is (apparently) easily enforced that the
arguments to super and its results are left alone. I never questioned that
that was a Good Thing. But it’s only skin deep since the internal state of
the arguments and results can be changed anyhow (String#gsub! being an
example); also the internal state of self can be changed, and all
practical protection against that is again only skin deep. We can protect
the interface - or the skin if you want - but to what avail?

Well, it dosen’t matter so much what Matz has previously thought, if we find
something better for good reasons. And if we explain it well enough he’ll
understand (I hope).

So my conclusion is that maybe we should have two separate keywords for
“submethod wraps” and “watch wraps”, and be done with it. Ruby will
discard the former when the primary method is redefined, and will keep the
latter. And the rest is the programmer’s responsibility. What do you
think?

I like the simplicty of it. But I’m not sure yet. Something tells me that
having a safe way to wrap, even if its only method interface safe, is
important.

Hmmmm, thinking about it some more, it seems that maybe we’re unifying two
concepts that are really orthogonal. On one hand there’s whether a wrap
should go when the primary method goes or whether it should stay. On the
other hand there’s whether the interface (args + result) is meddled with
or not. I think the former is a functional difference, while the latter
feels like a documentation issue because nothing happens with it at run
time.

Yep, pretty close to right on.

BTW: did you read ruby-talk:86785?

I read it, but not thoroughly. It’s on my toread pile for when my mind
unblanks.

Get to it when you can. I think there’s a good idea in there that’s related,
basically the idea of singleton mixins.

-t0

P.S. By the way, I think we’ve certainly come along way already!

[snip]

subclassing → exists → def → call super => outer class wrap
subclassing → exists → def → no super => redefine
subclassing → exists → wrap → call super => same as def or ERROR?
subclassing → exists → wrap → no super => same as redef or ERROR?
subclassing → not exists → def → call super => ERROR
subclassing → not exists → def → no super => define
subclassing → not exists → wrap → call super => ERROR
subclassing → not exists → wrap → no super => same as define or ERROR?
not subclassing → exists → def → call super => redefine w/ out. class wrap
not subclassing → exists → def → no super => redefine
not subclassing → exists → wrap → call super => wrap
not subclassing → exists → wrap → no super => same as redef or ERROR or?
not subclassing → not exists → def → call super => ERROR
not subclassing → not exists → def → no super => define
not subclassing → not exists → wrap → call super => ERROR
not subclassing → not exists → wrap → no super => same as define or ERROR?

Lots of fun, huh? :wink: And this doesn’t take into account the descisions of
outer vs. inner wrapping. (Note: class wrap means the effective wrapping of
the superclass’ method; it is necessarily outer) So if you ask me the above
descision tree is too complex, and is really on the verge of becoming too
unwieldy for any ordinary programmer.

Actually it doesn’t take into account either that if a method already
exists, it can be in the superclass or in the subclass (that’s what
confused me about the first 4 lines. There you mean that the method exists
in the superclass but not in the subclass, right?) Just trying to grow
some more fun branches :wink:

Maybe an attempt to fill in the blanks in the decision tree… A wrap that
doesn’t call super seems invalid, though I have no idea how to enforce
that super is actually called (except maybe by having the super call set
some hidden variable and check that at the end of the wrap). I don’t know
what Matz had in mind there, he faces the same problem the way he did
wrapping in his slides.

Of course in practice only some branches of the tree will be useful. And
reordering the tests might locally decrease the depth of the tree, etc
etc.

Something else that occurred to me just now…

def foo
print “(”
eval(“repus”.reverse)
print “)”
end

This clearly calls super. But in general, it’s impossible to know if some
method calls super. What I just mean to say is that the tree above can
only be evaluated at run time, not statically. That’s not only hell to
those reading the code, but the idea of prime method is no longer so
well-defined. But I wonder whether that needs to be. I’ll have to think
that through later on.

I think it would be necessary to make the decision tree above more
consistent. It would be nice if what wrap did was independent of the other
tests, and same for def. I know you wanted to do both wrapping, defining
and redefining with only def, but I’m just trying a different path, seeh
ow that looks.

I will keep calling it that then. :slight_smile: For clearity I’m going to start using
the following terms:

outer - a wrap that remians even when the prime method is redefined
inner - a wrap the is removed when the prime method is redefined
passive - a wrap that does not alter functionality in any way
active - a wrap that alters functionality in some way
shrink - same as superwrap

Perfectly OK (no so-so :slight_smile:

Well, it dosen’t matter so much what Matz has previously thought, if we find
something better for good reasons. And if we explain it well enough he’ll
understand (I hope).

I hope so too.

I like the simplicty of it. But I’m not sure yet. Something tells me that
having a safe way to wrap, even if its only method interface safe, is
important.

I know, that idea hasn’t left my head yet. But it’s really a hard thing to
do in Ruby because interfaces are hardly fixed. Unless we’d use interface
checking as Sean proposed, but that needs lots of careful thought as well.

Get to it when you can. I think there’s a good idea in there that’s related,
basically the idea of singleton mixins.

I’ll get to it this weekend probably. I’ll get back to you then. I’ll
probably retire from ruby-talk until then.

Peter

P.S. By the way, I think we’ve certainly come along way already!

We’ve got a lot on the table, but we still have got lots of sorting out to
do I think. Also I have the feeling we’ve almost run in a circle, but it
doesn’t feel like a lost effort.

Hi Peter,

Hope your week has gone well. The couple of days off has helped me clearify
some things. Indeed, I’ve been doing a good bit of work on my project Baker
and it is becoming increasing clear how AOP ideas could be put to good use
there. Moreover, it’s becoming clear where Ruby prevents me from doing any
such thing, or at least makes it difficult.

So here’s the overall conception that’s developing in my mind: Given a class/
object there are three ways to augment it. For lack of more better terms I’ll
call these: subclass, redefine and superclass. In Ruby we do these in quite a
number of very different ways, for example:

subclassing on a class:

  # B is the subclass of A
  class B < A
    ...
  end

subclassing on an specific object:

  # an anonymous singleton (sub)class
  class << a
    ...
  end

  # as above but for one particular method
  def a.ameth
    ...
  end

  # extending an object with a module
  a.extend(B)

redefining on a class:

  # repeat class statement
  class A
    def x ...
  end
  class A
    def x ...
  end

  # class eval
  A.class_eval ...

  # for a specific method
  A.define_method ...

superclassing on a class:

  # A is the superclass of B
  class B < A
    ...
  end

  # module mixins
  class B
    include A
  end

And I’m sure the are other ways I haven’t considered.

What this boils down to, if one were to generalize it completely is a means to
do all three: subclass, redefine and superclass, either explicitly, giving a
new name to the altered entity, or inplace (implicit/anonymous), and with any
number of subclass/superclass layers. Finding a very consitent means of doing
all this and also adding the extra features for AOP generics, like
indicators, while yet remaining as close to current Ruby as possible, is the
real trick.

So where do wraps fit into this? You’ve probably already guessed from my
previosu use of the term submethod: they are forms of subclassings.

So let me try to say this in another way. Here is a list of the different
possibilities I mention from above with one example for each of how it can be
currently done. In the following list ‘class define’ means to define it for
an entire class, so that all objects of that class will inherit the behavior.
‘obj define’ means define it only for a particular object.

class define explict subclass: class B < A
class define implicit subclass: don't have
class redefine explict: B = A.dup; B.class_eval ...
class redefine implicit: class A; class A
class define explicit superclass: class A < B ... include Mixin
class define implicit superclass: A.class_eval {include Mixin}

obj define explict subclass: b=a.dup; b.extend(B) (limited to 1 layer)
obj define implicit subclass: a.extend(B) (limited to 1 layer)
obj redefine explict (don't have, simulate w/ dup; singleton no super)
obj redefine implicit (don't have, simulate w/ singleton no super)
obj define explicit superclass (don't have)
obj define implicit superclass (don't have)

Of course there may be ways to do some of these that I am unaware. Also I fail
to mention the opposite destruction of these, i.e. undefining a method.

Aside to this, is the related fact that if you define a class and then create
an object of that class, and then subsequently implicitly redefine the class,
the object will also be changed. This can be useful, but also the opposite,
and it might be nice to have a way to “freeze in” an objects class
definition. But I digress…

I know this is somewhat confusing. It is hard to find the right words. I find
myself wanting to draw pictures. I think the simplist way to think about it
is: given a class with a method, how do I wish to augment it in relation to
the super call? Do I want my augmentation’s super call to access the given
class’ method; or do I want the given class’ method’s super call to access my
augmentation; or do I want to replace the method altogether? Do I want an
explict new class/object or can I just augment inplace. Then take this and
apply it to outside programmers who might want to do the same to this code,
and then someone else wanting to do it to their code. And so on. Pulling it
all together we can draw a complete picture.

Hope I’ve managed to explain this well enough. Its harder than I tought. :slight_smile:

-t0

···

On Wednesday 03 December 2003 03:21 am, Peter wrote:

We’ve got a lot on the table, but we still have got lots of sorting out to
do I think. Also I have the feeling we’ve almost run in a circle, but it
doesn’t feel like a lost effort.

Here is an intereseting problem that I’m currently facing and which is related
to method wrapping.

I have a superclass called Recipe which has functionality for compiling and
installing programs from source. I create “specialty” recipes by subclassing
this.

In a subclass I define the compile method to handle the details of compiling
but all of the compiling must be “wrapped” in a chroot environment setup by
routines in the superclass. How do I generically wrap the subclass method
from the superclass?

Here’s a basic example of the NON-GENERIC way to do it, to help explain:

class Recipe
def construct_chroot … end
def deconstruct_chroot … end
end

class SpecialRecipe < Recipe
def compile
construct_chroot()
… # special compile code
deconstruct_chroot()
end
end

The problem with the above is that I do not wish for the subclass to have to
explictly call the construct and deconstruct methods. It needs to be GENERIC,
b/c if these calls were accidently forgotten it could potentially damage the
root file system.

As always, I may be over looking the obvious. But if anyone has a current
workable solution I would be most grateful.

···

For those interested in method wrapping prorposals this has some interesting
solutions. First there is “shrink wrapping” i.e. super-wrapping:

class Recipe
def construct_chroot … end
def deconstruct_chroot … end
def compile
construct_chroot()
super # must work like: super if defined?(super)
deconstruct_chroot()
end
end

class SpecialRecipe < Recipe
def compile
superwrap
… # special compile code
super
… # and/or special compile code
end
end
end

Secondly, It is a perfect example of outer wraps – wraps that remain intact
even when the primary method is redefined (opposed to inner wraps which do
not)

class Recipe
def construct_chroot … end
def deconstruct_chroot … end
def compile … end
def:outerwrap compile
construct_chroot
super
deconstruct_chroot
end
end

class SpecialRecipe < Recipe
def compile
… # special compile code
end
end

Interseting stuff!
T.

As always, I may be over looking the obvious. But if anyone has a current
workable solution I would be most grateful.

class Recipe
def construct_chroot … end
def deconstruct_chroot … end
def compile
construct_chroot()
real_compile
deconstruct_chroot()
end
end

class SpecialRecipe < Recipe
def real_compile
… # special compile code
end
end

Just a simple template design pattern. The only catch is that you can’t
put the special compile code in a method called compile. (I mentioned
before that the kind of wrapping you use here can be done using
subroutines, but wrapping allows it to do in-place, remember that?)

I’m busy catching up now with reading/understanding the wrapping stuff. I
still need to find that example for cflow, but that’s for tomorrow when I
pickup the notes (if I don’t forget). When I find something interesting,
I’ll let you know.

Peter

As always, I may be over looking the obvious. But if anyone has a current
workable solution I would be most grateful.

class Recipe
def construct_chroot … end
def deconstruct_chroot … end
def compile
construct_chroot()
real_compile
deconstruct_chroot()
end
end

class SpecialRecipe < Recipe
def real_compile
… # special compile code
end
end

Just a simple template design pattern. The only catch is that you can’t
put the special compile code in a method called compile. (I mentioned
before that the kind of wrapping you use here can be done using
subroutines, but wrapping allows it to do in-place, remember that?)

Yep. I recall. Template Design Pattern. Didn’t know that it had a name. Well,
it’s what I did, as I don’t think there is better way to do it at this point.
Thanks Peter.

I’m busy catching up now with reading/understanding the wrapping stuff. I
still need to find that example for cflow, but that’s for tomorrow when I
pickup the notes (if I don’t forget). When I find something interesting,
I’ll let you know.

It’s all good. I have lots of stuff on my plate right now. I’ll just keep
touching back on this as it developes in my mind. Have started some rework on
the wiki page.

T.

···

On Sunday 07 December 2003 08:02 pm, Peter wrote:

Hi Tom,

Yep. I recall. Template Design Pattern. Didn’t know that it had a name. Well,
it’s what I did, as I don’t think there is better way to do it at this point.
Thanks Peter.

Actually your example above could prove to be a useful application of
cflow. Suppose during compilation you’d have to do a number of tasks, and
each has a number of subtasks, and each of those too, etc. Suppose that
some of the tasks need to be run in a chroot environment, but you’d want
to run as little as possible in a chroot environment (say because that
minimizes security risks). You’d then have a main method called compile,
and a bunch of methods that either need to be wrapped in a chroot or not,
but you can’t make a clear separation in the call tree between the two
kinds of methods. I’ll give a simple example:

def compile # needs no chroot
task1
task2
end

def task1 # needs a chroot
task2
task3
end

def task2 # needs a chroot

end

def task3 # needs no chroot

end

For task1 we’d need to create the chroot environment. For task2 too, but
not when it is called from a method that already created a chroot env. For
task3 we need not create a chroot env, but we need to break one down when
called from a chrooted method. If we have a number of tasks like this, it
becomes cumbersome to code this in a consistent way. And thus we can use
AOP techniques. And to see whether we are in a chroot environment already,
we can use cflow. But we need to be able to limit it to the direct caller.
But suppose we have some real small tasks that don’t require a chroot
environment, but are too insignificant to break down and rebuild the
environment for. I remember saying that cflow is technically not required,
you can do the same with just wrappers. So maybe we should do it
explicitly in Ruby then. The only point of cflow is that if it’s a
recurring concept, it might be worth to have a separate notion of that in
Ruby. But what I don’t like about cflow is that unless we can find a much
completer notion to describe conditions based on control flow, using it
may cause troubles too. If the condition becomes more complex, then it’s
possible we can’t describe it anymore in cflow logic and thus we need to
make it explicit anyway. If it was explicit in the first place, we’d need
to change less. I don’t know if you’re still with me…

It’s all good. I have lots of stuff on my plate right now. I’ll just keep
touching back on this as it developes in my mind. Have started some rework on
the wiki page.

BTW, about [ruby-talk:86785]… Correct me if I’m wrong, but the idea
there is that you want to have a wrap that is always the outermost wrap,
even if methods are redefined in subclasses, much like the problem of the
chroot environment you presented. But I think I still need to be convinced
of the use of wraps in such cases because it can easily be done with
current Ruby (using template patterns and alike). Granted, wrapping can do
the same thing in-place. But is that necessary? If a wrap is supposed to
stay in place, like in case of your chroot creation, it seems natural to
use something template-like. OK, that exposes more methods, like the
real_compile in my previous post, but that’s a more general problem of
visibility that’s cross-cutting. It’s the problem C++ tries to solve in a
clumsy way using friend classes, and what Matz tries to solve using
namespaces (but implementation-wise this is hard to do efficiently in a
dynamic language as Matz mentions on his slides). And actually with wraps
there’s the problem that wraps are anonymous (unless we use indicators, or
allow to index the wrap stack, which is definitely yuck), but using
template-like things and subroutines, that’s not a problem for all has a
name.

I’m just thinking aloud now, after reading up on AspectJ stuff. What
bothers me is that while reading the AspectJ stuff again, my reaction is
continually “so what, we can do that in Ruby already”. (I learned Ruby
after I learned about AspectJ.) The first picture they show in their talks
is a schematic representation of the lines of code in the different source
files of the apache web server code. They show that XML parsing code is
located in 1 file. They show that URL pattern matching code is only in two
files (because it’s a class and it’s subclass). But then logging is spread
all over the place. But then I say to myself that we in Ruby don’t have
such a problem. We are not forced to place the code of one class or module
by itself in one file. So we can perfectly move all the code concerned
with logging into one file. We can even make it generic so we don’t need
to write the same code over and over again. This shows again that Matz
wasn’t born yesterday. A class-based source file system forces you to put
all the code related to manipulating the same bit of data in one file,
even if code has parts that are about completely different concerns. Ruby
allows you to also split code based on the different concerns. If you
combine that thought with the idea of mix-ins (which the AspectJ guys try
to do using inter-class declarations, aka “open classes”), Ruby seems to
have more advantages than AspectJ will ever have.

I don’t know if you see where I’m going. I don’t know if I see where I’m
going. But my conclusion seems to be that for internal wraps, we make
things more complicated by using wraps, rather than solve things. What we
seem to struggle with are things like the order of the wraps, removing
wraps, redefining wraps, etc, and these seem really hard when there are
wraps, but not when we’d use things like templates and subroutines etc
because then we already have explicit power to manipulate all of that.

However I do believe there is use in what we call external wraps, which
are really just hooks that get called when another method gets called, but
are not part of the intrinsic functionality of the method they wrap. The
gain there is that they are really invisible to the rest of the program,
only they know they are there. And for those wraps, things like the order
of the wraps in the stack doesn’t matter or removal on prime method
redefinition. But I feel that for intrinsic wraps we seem to be using the
wrong mechanism. It seems to take away explicit control, and we are now
looking for a way to give it back. If you know what I mean.

Maybe a final note… There are some features AspectJ has that we don’t
have in Ruby (or at least that I know of). In AspectJ a method can be
called when a variable is read/assigned (easy for a compiled language).
Also exception handlers are join points. Note that we could add hooks
there, but not without the hooked ones explicitly providing that
possiblity.

Peter

PS: I’m not crawling back, I’m just waiting for you to refute all I’ve
been saying :slight_smile:

PPS: Sorry for rambling on for so many kilobytes

PS: I’m not crawling back, I’m just waiting for you to refute all I’ve
been saying :slight_smile:

Haha! Y9ou knew I would :wink:

Actually your example above could prove to be a useful application of
cflow. Suppose during compilation you’d have to do a number of tasks, and
each has a number of subtasks, and each of those too, etc. Suppose that
some of the tasks need to be run in a chroot environment, but you’d want
to run as little as possible in a chroot environment (say because that
minimizes security risks). You’d then have a main method called compile,
and a bunch of methods that either need to be wrapped in a chroot or not,
but you can’t make a clear separation in the call tree between the two
kinds of methods. I’ll give a simple example:

Sure, I think that can work. Try something like:

class Compiler
def compile
task1
task2
end

def task1:chroot
  task2
  task3
end

def task2:chroot
  ...
end

def task3
  ...
end

end

Compiler.aspect do |meth|
if meth.tag==:chroot
meth.wrap do
if doer.cflow_ancestors.any? {|a| a.tag==:chroot}
super
else
chroot_evironment do
super
end
end
end
end
end

QUICK SIDE NOTE: might be nice to have something for all those dang ends. How
about end_____end, with as many underscore (2 or more) as needed:

Compiler.aspect do |meth|
if meth.tag==:chroot
meth.wrap do
if doer.cflow_ancestors.any? {|a| a.tag==:chroot}
super
else
chroot_evironment do
super
end_____end

Anyway, ‘doer’ references the current method being executed. I think this
psuedo-notation is about right. Of course, the same effect, could also be
achieved explictly by setting a flag within the code itelf, say @chroot =
true, and coding each method to act on that flag. But then we miss the point
of AOP: seperation of concerns. Using AOP we can make the aspect independent
of class Compiler. So not only is it more flexible, but we could define it
sepatately and then reuse it on other classes.

For task1 we’d need to create the chroot environment. For task2 too, but
not when it is called from a method that already created a chroot env. For
task3 we need not create a chroot env, but we need to break one down when
called from a chrooted method. If we have a number of tasks like this, it
becomes cumbersome to code this in a consistent way. And thus we can use
AOP techniques. And to see whether we are in a chroot environment already,
we can use cflow. But we need to be able to limit it to the direct caller.
But suppose we have some real small tasks that don’t require a chroot
environment, but are too insignificant to break down and rebuild the
environment for. I remember saying that cflow is technically not required,
you can do the same with just wrappers. So maybe we should do it
explicitly in Ruby then. The only point of cflow is that if it’s a
recurring concept, it might be worth to have a separate notion of that in
Ruby. But what I don’t like about cflow is that unless we can find a much
completer notion to describe conditions based on control flow, using it
may cause troubles too. If the condition becomes more complex, then it’s
possible we can’t describe it anymore in cflow logic and thus we need to
make it explicit anyway. If it was explicit in the first place, we’d need
to change less. I don’t know if you’re still with me…

cflow logic is certainly a different way of thinking, but I don’t really see
how cflow logic would become so complex that you’d want to go back to
explicts. It’s essentially the same logic, actually, just applied
differently. In fact I think one will find that the ease of managing such
aspects are on an order of magnitude greater than explictly doing so. I don;t
see cflow being the the essential part of AOP, but it is another useful part
of it.

BTW, about [ruby-talk:86785]… Correct me if I’m wrong, but the idea
there is that you want to have a wrap that is always the outermost wrap,
even if methods are redefined in subclasses, much like the problem of the
chroot environment you presented. But I think I still need to be convinced
of the use of wraps in such cases because it can easily be done with
current Ruby (using template patterns and alike). Granted, wrapping can do
the same thing in-place. But is that necessary? If a wrap is supposed to
stay in place, like in case of your chroot creation, it seems natural to
use something template-like. OK, that exposes more methods, like the
real_compile in my previous post, but that’s a more general problem of
visibility that’s cross-cutting. It’s the problem C++ tries to solve in a
clumsy way using friend classes, and what Matz tries to solve using
namespaces (but implementation-wise this is hard to do efficiently in a
dynamic language as Matz mentions on his slides). And actually with wraps
there’s the problem that wraps are anonymous (unless we use indicators, or
allow to index the wrap stack, which is definitely yuck), but using
template-like things and subroutines, that’s not a problem for all has a
name.

Well, sort-of, in 86785 I was showing how we might mixin singletons in a class
definable way, which is the essence of wrapping. Its not that they are outer
or inner per se; I wasn’t thinking about that at the time, and is something
additional to take into consideration (currently they would indeed be outer
though). We can already define mixin singletons in an object definable way
using #extend. You may have read about that in a couple of my recent posts on
the matter (87408 is good starting point in the thread).

I’m not sure what your refering to with c friend classes and namespaces. Do
you mean instance variable visibility? Sorry if I’m just forgetting something
here. If this the case though, my take is that since wraps are implict they
should have access as if they were the same class. Perhaps outer wraps should
not and inner wraps should. Its just something that needs to be thought
through to come up with the best way. I think indicators are awesome by the
way :), but wrap indexing I agree is yuk, and I can only imagine it can do
nothing but cause weird ugly code hacks --nothing of really great use. But if
the indexing functionality is there it doesn’t so much matter as long as its
not something one HAS to deal with. I doubt it would get much use.

As for “neccessity”, this consideration is the very distinction between
explict/specific and implict/generic. It some cases the former is a must. In
fact the core of any program is explict, but the more one can generalize, the
more flexible, powerful and reusable are the solutions. I think one can see
it clearly if they’ve seen something like my GUI wrapper (despit the hack
that it is to achieve a current effect of wrapping) It really is quite
something to take an existing class and without touching a lick of code in
it, give it a GUI interface.

I’m just thinking aloud now, after reading up on AspectJ stuff. What
bothers me is that while reading the AspectJ stuff again, my reaction is
continually “so what, we can do that in Ruby already”. (I learned Ruby
after I learned about AspectJ.) The first picture they show in their talks
is a schematic representation of the lines of code in the different source
files of the apache web server code. They show that XML parsing code is
located in 1 file. They show that URL pattern matching code is only in two
files (because it’s a class and it’s subclass). But then logging is spread
all over the place. But then I say to myself that we in Ruby don’t have
such a problem. We are not forced to place the code of one class or module
by itself in one file. So we can perfectly move all the code concerned
with logging into one file. We can even make it generic so we don’t need
to write the same code over and over again. This shows again that Matz
wasn’t born yesterday. A class-based source file system forces you to put
all the code related to manipulating the same bit of data in one file,
even if code has parts that are about completely different concerns. Ruby
allows you to also split code based on the different concerns. If you
combine that thought with the idea of mix-ins (which the AspectJ guys try
to do using inter-class declarations, aka “open classes”), Ruby seems to
have more advantages than AspectJ will ever have.

I agree, Ruby has it over Java by a long shot – I am sometimes dumb-struck
that Java is so popular. Although I understand too, because I think Ruby
still has some maturing to do. But nonethess we can do many things with Ruby
that runs circles around Java. But to me that also means, with the addition
of good AOP fetures, Ruby will be even that much more beyond anything else
out there. In fact I think that the features we are now discussing, are so
fundementally mind-blowing once you start to understand thier power, that
combined with already amazing abilities of Ruby, this could become the one of
the things that really distinguishes Ruby from the the rest of pack --the
thing that will draw people to Ruby over other language choices. Do you know
what I mean? I mean really, AOP as easy as this? My god, It’s frig’n
incredible!!! Okay, I’m getting a little excited :wink:

I don’t know if you see where I’m going. I don’t know if I see where I’m
going. But my conclusion seems to be that for internal wraps, we make
things more complicated by using wraps, rather than solve things. What we
seem to struggle with are things like the order of the wraps, removing
wraps, redefining wraps, etc, and these seem really hard when there are
wraps, but not when we’d use things like templates and subroutines etc
because then we already have explicit power to manipulate all of that.

I don’t think these are so much a struggle. For the most part I see how it can
be done. Much of the difficulty I think is fitting it into Ruby’s syntax
without too much “back breaking”. Also, you think its a tough coming up with
a good notation for removing wraps? Try removing 10,000 explict wraps from a
1,000,000 lines of code. :wink:

However I do believe there is use in what we call external wraps, which
are really just hooks that get called when another method gets called, but
are not part of the intrinsic functionality of the method they wrap. The
gain there is that they are really invisible to the rest of the program,
only they know they are there. And for those wraps, things like the order
of the wraps in the stack doesn’t matter or removal on prime method
redefinition. But I feel that for intrinsic wraps we seem to be using the
wrong mechanism. It seems to take away explicit control, and we are now
looking for a way to give it back. If you know what I mean.

On the contrary, but I think I’ve explained it enough in the above. Right off
the bat my GUI interface would not be possible, and truth be told, we are
already doing this kind of thing quite often by aliasing methods and then
redefining them with call backs to the original. Worse, when this is done
genericlly we end up with a bunch of was_meth clutter. So inner wraps are
already here, but we need a clean and powerful way to do it right.

Maybe a final note… There are some features AspectJ has that we don’t
have in Ruby (or at least that I know of). In AspectJ a method can be
called when a variable is read/assigned (easy for a compiled language).
Also exception handlers are join points. Note that we could add hooks
there, but not without the hooked ones explicitly providing that
possiblity.

I think the latter can already be done actually. Well, I may be wrong about
that, but its close b/c error classes are/can be instantiated, which means
you can hook into the initialize method. As for the former, you’re right,
ther are no callback methods for assignment, there has been talk of this and
we may one day see it, but it would probably be too much overhead. I think
localizing instance variables will be good enough though, because you can
wrap accessor methods.

T.

PPS: Sorry for rambling on for so many kilobytes

Don’t be silly, that’s what kilobytes were made for! :slight_smile:

···

On Tuesday 09 December 2003 01:05 am, Peter wrote:

QUICK SIDE NOTE: might be nice to have something for all those dang ends. How
about end_____end, with as many underscore (2 or more) as needed:

Compiler.aspect do |meth|
if meth.tag==:chroot
meth.wrap do
if doer.cflow_ancestors.any? {|a| a.tag==:chroot}
super
else
chroot_evironment do
super
end_____end

Actually counting consecutive underscores is hard. On my screen they make
up one long line.

cflow logic is certainly a different way of thinking, but I don’t really see
how cflow logic would become so complex that you’d want to go back to
explicts. It’s essentially the same logic, actually, just applied
differently. In fact I think one will find that the ease of managing such
aspects are on an order of magnitude greater than explictly doing so. I don;t
see cflow being the the essential part of AOP, but it is another useful part
of it.

Agreed, cflow is not the essential part of AOP. But I think I should
make my example more concrete to show the complexity of cflow logic
(although maybe it may be a bit contrived, but I hope you get the point).

class Compiler
def compile:no_chroot
task1
task2
end
def task1:chroot
# do some stuff
task2
task3
# some other stuff
end
def task2:chroot
end
def task3:no_chroot
# do some stuff
task4
# do some other stuff
end
def task4:whatever
task5
end
def task5:chroot
end
end

I used indicators to indicate whether a method needs chroot, or doesn’t
want it, or it doesn’t matter (if the task it performs is so small that
it is overhead to change the environment and then put it back).

What your wrapper then needs to do is create a chroot env for methods with
a ‘chroot’ indicator if it wasn’t there yet, and destroy the chroot env
for a method with the ‘no_chroot’ indicator, if the env was there. So
basically, in terms of cflow, you need to create the chroot env when you
are in the cflow of a method m1 with the chroot indicator, and when there
is no other method m2 in the cflow of m1, unless there’s another method m3
in the cflow of m2 that has the chroot indicator, etc. OK, I’m
exagerating, but you need to be able to say that there exists a method
m1:chroot in whose cflow we are and for which no method m2:no_chroot
exists in its cflow and in whose cflow we are too. You can’t even say that
in AspectJ BTW. But if I do it myself explicitly, I’d just add a variable
to the Compile class that keeps track of whether we are in a chroot
environment or not, and have my wraps use that information and change
environment if needed and set that variable accordingly. Much easier, and
much harder to break by extensions.

The alternative is to change the subdivision of the tasks so that we can
group chrooted tasked, but since chrooting is another concern, we don’t
want that governing the way we subdivide the tasks.

Well, sort-of, in 86785 I was showing how we might mixin singletons in a class
definable way, which is the essence of wrapping. Its not that they are outer
or inner per se; I wasn’t thinking about that at the time, and is something
additional to take into consideration (currently they would indeed be outer
though). We can already define mixin singletons in an object definable way
using #extend. You may have read about that in a couple of my recent posts on
the matter (87408 is good starting point in the thread).

I agree that there are already features present in the Ruby language that
allow us to do wrapping-like things. But if I understand well that’s
mostly based on subclassing (defining singletons comes down to defining
singleton classes implicitly).

As for my mention of friend classes and namespaces… Currently in Ruby
there is class-based access control for methods and variables. For the
former it can be changed (public/private/protected), for the latter it is
fixed (until @var and @_var come along that give some control). But since
aspects are cutting across classes, we’d naturally need something more
than class-based access control. Actually a simple example of a
cross-cutting concern is a bidirectional link between two objects. Keeping
those links consistent is one concern, but if the objects involved are of
different class, this concern involves multiple classes. An aspect could
keep track of the links and make sure they are always consistent. In
AspectJ you could sneak in some variables to keep the links in the
respective classes, but they are private to the aspect and can’t be
accessed from those classes. That’s logical because then any operations
involving the link are performed by methods that the aspect offers and
consistency is guaranteed. Of course the notion of friend classes is too
limited in this case, but it allows to change access control. Namespaces
do that too in a sense.

But to get back to my previous mail; I think the point I was trying to
convey is as follows. When we look at outer wraps, we see that they are
really just hooks. You can hook em, unhook em, and nobody will ever notice
a thing except you yourself. That’s clean separation of concerns, and
that’s what AspectJ does best. Examples are security, profiling, tracing,
logging, GUI observers, pre/postcondition checking, keeping dirty bits
when objects on screen are moved, error logging, invariant checking…
They are best done by using hooks (or outer wraps).

But then inner wraps. As you’ve pointed out, a primary method and its
wraps are really one whole. If a wrap is removed, then the method will
change functionality. Adding an inner wrap or removing one is much more
noticeable. Actually they kind of behave like a method and the helper
methods (or what I called subroutines by lack of a better term) it calls,
or like a method calling the same method in a super class. They add layers
of extra functionality. To me it feels like that is what class hierarchies
were invented for, or the hierarchy in a method call graph.

Maybe what I need is a simple example of inner wraps (that I could in no
way classify as an outer wrap, because I think our notions of that are
slightly different) and that clearly demonstrates its importance in
separating concerns in the sense that it does it better than current Ruby.
Then we could discuss something more concrete. Or maybe I should wait
until your RCR has been updated. I know I am overly sceptical now, but
maybe that’s because some things are getting clearer, and other things
getting fuzzier.

Peter

QUICK SIDE NOTE: might be nice to have something for all those dang ends.
How about end_____end, with as many underscore (2 or more) as needed:

Compiler.aspect do |meth|
if meth.tag==:chroot
meth.wrap do
if doer.cflow_ancestors.any? {|a| a.tag==:chroot}
super
else
chroot_evironment do
super
end_____end

Actually counting consecutive underscores is hard. On my screen they make
up one long line.

Mine too! But I was joking :slight_smile: Well, half way. It would be nice to have a good
way, I was just throwing an psuedo example out there.

cflow logic is certainly a different way of thinking, but I don’t really
see how cflow logic would become so complex that you’d want to go back to
explicts. It’s essentially the same logic, actually, just applied
differently. In fact I think one will find that the ease of managing such
aspects are on an order of magnitude greater than explictly doing so. I
don;t see cflow being the the essential part of AOP, but it is another
useful part of it.

Agreed, cflow is not the essential part of AOP. But I think I should
make my example more concrete to show the complexity of cflow logic
(although maybe it may be a bit contrived, but I hope you get the point).

class Compiler
def compile:no_chroot
task1
task2
end
def task1:chroot
# do some stuff
task2
task3
# some other stuff
end
def task2:chroot
end
def task3:no_chroot
# do some stuff
task4
# do some other stuff
end
def task4:whatever
task5
end
def task5:chroot
end
end

I used indicators to indicate whether a method needs chroot, or doesn’t
want it, or it doesn’t matter (if the task it performs is so small that
it is overhead to change the environment and then put it back).

What your wrapper then needs to do is create a chroot env for methods with
a ‘chroot’ indicator if it wasn’t there yet, and destroy the chroot env
for a method with the ‘no_chroot’ indicator, if the env was there. So
basically, in terms of cflow, you need to create the chroot env when you
are in the cflow of a method m1 with the chroot indicator, and when there
is no other method m2 in the cflow of m1, unless there’s another method m3
in the cflow of m2 that has the chroot indicator, etc. OK, I’m
exagerating, but you need to be able to say that there exists a method
m1:chroot in whose cflow we are and for which no method m2:no_chroot
exists in its cflow and in whose cflow we are too. You can’t even say that
in AspectJ BTW. But if I do it myself explicitly, I’d just add a variable
to the Compile class that keeps track of whether we are in a chroot
environment or not, and have my wraps use that information and change
environment if needed and set that variable accordingly. Much easier, and
much harder to break by extensions.

Actually I disagree. Only harder b/c one is not used to it. Once “mastered” an
AOP programmer could have the same job done in the same or less time, and
could certainly make adjustment’s much faster. While you’d have to do through
every method to adjust. Also, anything is breakable by extension very easily.

 def Compiler.compile:no_chroops
   ...
 end

broken.

The alternative is to change the subdivision of the tasks so that we can
group chrooted tasked, but since chrooting is another concern, we don’t
want that governing the way we subdivide the tasks.

Well, sort-of, in 86785 I was showing how we might mixin singletons in a
class definable way, which is the essence of wrapping. Its not that they
are outer or inner per se; I wasn’t thinking about that at the time, and
is something additional to take into consideration (currently they would
indeed be outer though). We can already define mixin singletons in an
object definable way using #extend. You may have read about that in a
couple of my recent posts on the matter (87408 is good starting point in
the thread).

I agree that there are already features present in the Ruby language that
allow us to do wrapping-like things. But if I understand well that’s
mostly based on subclassing (defining singletons comes down to defining
singleton classes implicitly).

But it dosen’t need to be. These aren’t just “wrapping-like things”. It is
wrapping. And with only some basic modifications, i.e. meta-singletons and
class defineable singletons, can be the very foundation of AOP for Ruby.

As for my mention of friend classes and namespaces… Currently in Ruby
there is class-based access control for methods and variables. For the
former it can be changed (public/private/protected), for the latter it is
fixed (until @var and @_var come along that give some control). But since
aspects are cutting across classes, we’d naturally need something more
than class-based access control. Actually a simple example of a
cross-cutting concern is a bidirectional link between two objects. Keeping
those links consistent is one concern, but if the objects involved are of
different class, this concern involves multiple classes. An aspect could
keep track of the links and make sure they are always consistent. In
AspectJ you could sneak in some variables to keep the links in the
respective classes, but they are private to the aspect and can’t be
accessed from those classes. That’s logical because then any operations
involving the link are performed by methods that the aspect offers and
consistency is guaranteed. Of course the notion of friend classes is too
limited in this case, but it allows to change access control. Namespaces
do that too in a sense.

Hmm, I think I may understand what your refering to now. I don’t think of it
that way. For me all varaibles are of the same scope as they are now
–aspects don’t have some special cross-cutting scope. For that you’d use a
class/module variable as we do now. Certainly one could invision such a
cross-cutting scope, but such a scope is not neccessary; and whether it
should be able to interact with the regular scope is again the difference
between passive and active wraps…

But to get back to my previous mail; I think the point I was trying to
convey is as follows. When we look at outer wraps, we see that they are
really just hooks. You can hook em, unhook em, and nobody will ever notice
a thing except you yourself. That’s clean separation of concerns, and
that’s what AspectJ does best. Examples are security, profiling, tracing,
logging, GUI observers, pre/postcondition checking, keeping dirty bits
when objects on screen are moved, error logging, invariant checking…
They are best done by using hooks (or outer wraps).

But then inner wraps. As you’ve pointed out, a primary method and its
wraps are really one whole. If a wrap is removed, then the method will
change functionality. Adding an inner wrap or removing one is much more
noticeable. Actually they kind of behave like a method and the helper
methods (or what I called subroutines by lack of a better term) it calls,
or like a method calling the same method in a super class. They add layers
of extra functionality. To me it feels like that is what class hierarchies
were invented for, or the hierarchy in a method call graph.

Maybe what I need is a simple example of inner wraps (that I could in no
way classify as an outer wrap, because I think our notions of that are
slightly different) and that clearly demonstrates its importance in
separating concerns in the sense that it does it better than current Ruby.
Then we could discuss something more concrete. Or maybe I should wait
until your RCR has been updated. I know I am overly sceptical now, but
maybe that’s because some things are getting clearer, and other things
getting fuzzier.

Active wraps need not alter functionality. Certainly they can, and even here
they have their advantages: easily removed and so serve as testing code
variations, they can be reused like mixins in appropriate contexts, etc. But
more significantly they can also be used to interact in a way consistant with
a class, injecting and extracting information without “augmenting” behavior.
I think this is the option you are not witnessing.

Your response has got me digging up my old GUI code. Its been a while, and I’d
be hard pressed to tell you how everything works off the top of my head, but
I thought you might like to look at some of its heart. What better example
than a real one. If only I had a real AOP way to do, rather then the mind
boggling terseness of what follows. Have fun :wink:

module Controller

module Listener

def ___listen___(meth=nil, *args, &block)
  meth = meth.to_s
  if @___events___
    if @___events___.has_key?(meth)
      @___events___[meth].each { |p|
        p.call(*args, &block)
      }
    end
  end
  if @___procs___ and @___state___
    if self != @___state___
      @___state___ = self.clone
      @___procs___.each { |p|
        p.call(self)
      }
    end
  end
end

def ___automate___
  $___trigger___ = [] unless $___trigger___
  self.methods.each { |meth|
    if not Object.instance_methods(true).include?(meth)
      if not ['when', 'event', 'bind', 'change', '___listen___', 

automate’, ‘action’].include?(meth)
action(meth)
end
end
}
end

def ___action___(meth)
  ali = "___#{meth.hash.to_s.gsub('-', '_')}___"
  if not self.respond_to? ali
    self.instance_eval <<-EOS
      class << self
        alias_method :#{ali}, :#{meth}
        def #{meth}(*args, &block)
          return if $___trigger___.include?([self, :#{meth}])
          $___trigger___ << [self, :#{meth}]
          r = send(:#{ali}, *args, &block)
          ___listen___(:#{meth}, *args, &block)
          $___trigger___.pop
          return r
        end
      end
    EOS
  end
end

end

module Methods

include Listener

def when(meth, prc=nil, &block)
  meth = meth.to_s
  ___automate___ unless @___events___
  @___events___ = {} unless @___events___
  prc = block unless prc
  @___events___[meth] = [] unless @___events___[meth]
  @___events___[meth] << prc
end

end

module Variables

 include Listener

def bind(prc=nil, &block)
  ___automate___ unless @___procs___ and @___state___
  @___state___ = self.clone unless @___state___
  @___procs___ = [] unless @___procs___
  prc = block unless prc
  @___procs___ << prc
end    

end

end

class Object
include Controller::Methods
end

class Numeric
include Controller::Variables
end

class String
include Controller::Variables
end

class Array
include Controller::Variables
end

class Hash
include Controller::Variables
end

···

On Wednesday 10 December 2003 05:16 am, Peter wrote:

But if I do it myself explicitly, I’d just add a

variable to the Compile class that keeps track of whether we are in a
chroot environment or not, and have my wraps use that information and
change environment if needed and set that variable accordingly. Much
easier, and much harder to break by extensions.

Actually I disagree. Only harder b/c one is not used to it. Once “mastered”
an AOP programmer could have the same job done in the same or less time,
and could certainly make adjustment’s much faster. While you’d have to do
through every method to adjust.

Sorry, Peter. I just noticed that I may have misread this. You still mean to
use wraps just not cflow. Is that what you mean? I thought by explict you
meant not using wraps at all.

I can see that cflow may not be easier as compared to just keeping a varaible
status, although they are practically interchangable – once you determine
the algorthim that relates the cflow to such a variable. But if cflow causes
too much interpretor overhead to maintain, then variable is quite reasonable.

T.

···

On Wednesday 10 December 2003 07:08 am, T. Onoma wrote:

Mine too! But I was joking :slight_smile: Well, half way. It would be nice to have a good
way, I was just throwing an psuedo example out there.

OK, but even if you’d write end*5 or so, or end(5), then to see whether it
is correct you’d need to count, while otherwise - if well indented - you
can do it at a glance.

But it dosen’t need to be. These aren’t just “wrapping-like things”. It is
wrapping. And with only some basic modifications, i.e. meta-singletons and
class defineable singletons, can be the very foundation of AOP for Ruby.

OK, that’s what I wanted to know. I probably prefer to have a separate
mechanism as foundation, although I can see how your singleton approach
can work too.

Hmm, I think I may understand what your refering to now. I don’t think of it
that way. For me all varaibles are of the same scope as they are now
–aspects don’t have some special cross-cutting scope. For that you’d use a
class/module variable as we do now. Certainly one could invision such a
cross-cutting scope, but such a scope is not neccessary; and whether it
should be able to interact with the regular scope is again the difference
between passive and active wraps…

I disagree. As I see it, there are two reasons for access control. One is
encapsulation, to prevent other code to meddle with your data. The other
is name clashes. The first is maybe a matter of self-control: you can
meddle, but you shouldn’t. The second is different, especially since we
are talking about aspects that are rather snuck in without anyone knowing,
but it might interfere still due to name clashes.

Active wraps need not alter functionality. Certainly they can, and even here
they have their advantages: easily removed and so serve as testing code
variations, they can be reused like mixins in appropriate contexts, etc. But
more significantly they can also be used to interact in a way consistant with
a class, injecting and extracting information without “augmenting” behavior.
I think this is the option you are not witnessing.

Well, I think we seem to have a bunch of misunderstandings because of
different interpretations of certain terms. To me what you just said
rather proves what I said, and that’s usually a bad sign… I think we’re
on the same frequency, we’re only talking a different dialect. In the RCR,
things like inner/outer wrap, passive/active wrap and such should be
defined well, right? In case you’d be working on that, give me sign when
that part’s finished and then I’ll take a look at it.

Your response has got me digging up my old GUI code. Its been a while, and I’d
be hard pressed to tell you how everything works off the top of my head, but
I thought you might like to look at some of its heart. What better example
than a real one. If only I had a real AOP way to do, rather then the mind
boggling terseness of what follows. Have fun :wink:

Mind-boggling is definitely the right word. But I’m not exactly an adept
code reader… But I’ll figure it out sooner or later.

I’ll get working on those examples from the AspectJ website. It’s a matter
of translating them to Ruby, and to adapt some things to the fact that
Ruby is dynamic. I’ll use your syntax, and indicators where necessary.
That’s still OK, right?

Peter

Sorry, Peter. I just noticed that I may have misread this. You still mean to
use wraps just not cflow. Is that what you mean? I thought by explict you
meant not using wraps at all.

I can see that cflow may not be easier as compared to just keeping a varaible
status, although they are practically interchangable – once you determine
the algorthim that relates the cflow to such a variable. But if cflow causes
too much interpretor overhead to maintain, then variable is quite reasonable.

Yep, that’s the point. Writing it using cflow is a different way of
thinking - which isn’t necessarily bad - but it may not match the fact
that the code you’re looking at is procedural. There is a reason why
program analyses - which look at the program in a mathematical way - are
limited in their possibilities. It is even hard to do it oneself in the
general case. And I used the term explicit probably because cflow is what
AspectJ offers and it implements it using a wrap and a avriable, so the
former is a higher level description of the latter which is then explicit.
They can do that in AspectJ by limiting the combinations of cflow (&&, ||,
!), and because it’s a static language. Also they don’t have to deal with
method redefinition. As such, I’m sure they can handle more complex
expressions involving cflow and have little overhead at runtime.

Peter

Mine too! But I was joking :slight_smile: Well, half way. It would be nice to have a
good way, I was just throwing an psuedo example out there.

OK, but even if you’d write end*5 or so, or end(5), then to see whether it
is correct you’d need to count, while otherwise - if well indented - you
can do it at a glance.

Well, I thought of using the underscores to allow one to indent as needed to
line up the first and the last end with the rest of the code. But now I see
that it is really a problem since the interpretor would have to depend on
that indentation (or number of underscores) to make sense of it, which is
yuk. But I did notice that if I switch to four spaces for indention (rather
than the two spaces I normally use) this, in fact, works:

class X
def whatever
if something
# …
end end end

Too bad I don’t like four space indentions :wink:

OK, that’s what I wanted to know. I probably prefer to have a separate
mechanism as foundation, although I can see how your singleton approach
can work too.

What I don’t like about a seperate mechinism is A) it will be essentially the
same kind of mechinism, so you have two separate components of ruby doing
essentially the same things. B) You now have to mange these two compenents
separately and take into account all the considerations in which they may
interact and/or conflict. C) Under the hood it looks pretty much the same:
adding a wrap is adding an anonymous subclass of some sort and linking it
into the class. And D) a seperate mechinism means much more code refactoring
of the interpretor, more code, more overhead, and consequently more potential
for bugs.

One might argure that there’s something wrong with singletons being able to
have there own singletons (i.e. meta-singletons), but I don;t see what that
would be since, as long as one can still undef/redef the singleton layers as
a whole (which one can), then meta-singletons are a complete logical superset
on regular singletons.

Hmm, I think I may understand what your refering to now. I don’t think of
it that way. For me all varaibles are of the same scope as they are now
–aspects don’t have some special cross-cutting scope. For that you’d use
a class/module variable as we do now. Certainly one could invision such a
cross-cutting scope, but such a scope is not neccessary; and whether it
should be able to interact with the regular scope is again the difference
between passive and active wraps…

I disagree. As I see it, there are two reasons for access control. One is
encapsulation, to prevent other code to meddle with your data. The other
is name clashes. The first is maybe a matter of self-control: you can
meddle, but you shouldn’t. The second is different, especially since we
are talking about aspects that are rather snuck in without anyone knowing,
but it might interfere still due to name clashes.

So you’re thinking of advices not being in the same scope as the methods they
wrap? But rather tha same advice shares a common scope across the
cross-cutting concerns? So then you’re talking about a whole different set of
constructs to do AOP, since just using Ruby’s current dynamicism could not
facilitate this. Lets look at a psuedo example:

aspect HereAdivces
advice(tag == :here) do
super
puts @x, self.x
end
end

class A
apply HereAdivces
attr_reader :x
def a:here
@x = “X”
end
end

a = A.new
a.a

What would this produce? If the advice had its completely own namespace:

nil
nil

The only thing you are left to “monitor” then is the return value of super. If
on the other hand you suggest a partial namespace giving:

nil
X

Then this is really no different than having instance variables local to their
class, which is, in some way or another, already on Ruby’s drawing board. So
the only difference would be forcing non-local instance variables to be local
in advices, or making them illegal altogether (that is of course if we even
end up any non-local instance variables).

Unless, I’m misunderstanding you (and seeing that this a complex subject we
know that is quite possible :slight_smile: I’m not seeing how this would work.

Active wraps need not alter functionality. Certainly they can, and even
here they have their advantages: easily removed and so serve as testing
code variations, they can be reused like mixins in appropriate contexts,
etc. But more significantly they can also be used to interact in a way
consistant with a class, injecting and extracting information without
“augmenting” behavior. I think this is the option you are not witnessing.

Well, I think we seem to have a bunch of misunderstandings because of
different interpretations of certain terms. To me what you just said
rather proves what I said, and that’s usually a bad sign… I think we’re
on the same frequency, we’re only talking a different dialect. In the RCR,
things like inner/outer wrap, passive/active wrap and such should be
defined well, right? In case you’d be working on that, give me sign when
that part’s finished and then I’ll take a look at it.

Good idea, I’ll put those terms in the RCR tonight, and work on ading
indicators, since I think we both agree on the utility of those. Yes?

I think the main differences in our appraoches, please correct me if I’m
wrong, is that you’re coming at it with a more formal understanding derived
from AspectJ, while I’m coming at it more from having implemented wraps by
hacking at Ruby. This is good, b/c it means we are attacking it from both
ends.

Your response has got me digging up my old GUI code. Its been a while,
and I’d be hard pressed to tell you how everything works off the top of
my head, but I thought you might like to look at some of its heart. What
better example than a real one. If only I had a real AOP way to do,
rather then the mind boggling terseness of what follows. Have fun :wink:

Mind-boggling is definitely the right word. But I’m not exactly an adept
code reader… But I’ll figure it out sooner or later.

:slight_smile: It actually looks harder than it is. What makes it so ugly is having to use
method to (hopefully) circumvent name clashes. If I remember correctly
it essentially wraps every method of a class to which #when has been applied,
or in the case of #bind, every single method of a class in order to hook on
instance variable changes. I can give you an end result example of a GUI wrap
if you’d like.

I’ll get working on those examples from the AspectJ website. It’s a matter
of translating them to Ruby, and to adapt some things to the fact that
Ruby is dynamic. I’ll use your syntax, and indicators where necessary.
That’s still OK, right?

Great! And the syntax is fine, easy enough to go back and make syntactical
modifications when need be.

T.

···

On Wednesday 10 December 2003 05:55 pm, Peter wrote:

Well, I thought of using the underscores to allow one to indent as needed to
line up the first and the last end with the rest of the code. But now I see
that it is really a problem since the interpretor would have to depend on
that indentation (or number of underscores) to make sense of it, which is
yuk.

Ever programmed Haskell? There indentation is significant, and that kinda
sucks. And you can get it right when writing the first version of the
code, if you use no tabs, but changing code possibly changes indentation
and then you get a preview of hell.

But I did notice that if I switch to four spaces for indention (rather
than the two spaces I normally use) this, in fact, works:

class X
def whatever
if something
# …
end end end

Too bad I don’t like four space indentions :wink:

Me neither. I know some people that do though.

What I don’t like about a seperate mechinism is A) it will be essentially the
same kind of mechinism, so you have two separate components of ruby doing
essentially the same things. B) You now have to mange these two compenents
separately and take into account all the considerations in which they may
interact and/or conflict. C) Under the hood it looks pretty much the same:
adding a wrap is adding an anonymous subclass of some sort and linking it
into the class. And D) a seperate mechinism means much more code refactoring
of the interpretor, more code, more overhead, and consequently more potential
for bugs.

One might argure that there’s something wrong with singletons being able to
have there own singletons (i.e. meta-singletons), but I don;t see what that
would be since, as long as one can still undef/redef the singleton layers as
a whole (which one can), then meta-singletons are a complete logical superset
on regular singletons.

I think you just may have just changed my mind partway. What I do like is
the idea of layering wraps. E.g., a logging layer that contains all wraps
related to logging, a profiling layer that contains all wraps related to
profiling, a GUI layer that contains all callbacks to the GUI, etc. But
I’m wondering; do you plan to give explicit control over the singleton
layers? So you can take out a layer, place it back or redefine it. That
creates some nice possiblities.

My initial impression was BTW that for each wrap you’d add a singleton
class, and although I don’t know the Ruby internals, my guess is that
would give overhead proportional to the number of singletons because for a
call to a method in the worst case you’d need to walk over the complete
chain of singleton classes.

BTW, I don’t know if you’ve changed your mind about this (my guess is you
haven’t), but your idea was to just use def for wrapping, and have ‘super’
call either a previous wrap or the method in a superclass (and that fits
nicely with singletons, I know). If you’d ‘def’ a method that does not
call ‘super’, you’d automatically have a redef. Only when you want to do a
redef with calling the method in the superclass that you’d need an extra
keyword ‘redef’. But the one practical problem I see with that is the
issue of how to actually remove wraps that are redefined in the
implementation. You’d want to do that because if you have a very dynamic
application, say one that operates in a number of modes and that often
changes mode and that uses wraps to conveniently model the changing
behavior, then wraps will accumulate if you don’t clean them up. But you
can’t because you have no way of determining whether a method calls super
because of eval.

Unless, I’m misunderstanding you (and seeing that this a complex subject we
know that is quite possible :slight_smile: I’m not seeing how this would work.

I think I see the problem… An aspect can access the internal state of
the classes it adds advice to. Generally that is OK because the underlying
system should be independent from the aspect, but not the other way around
(although that would be cool too, but there must be some linking one way
or the other). Also it depends on the kind of wrap. My feeling is that
inner wraps can freely access all variables in a class, outer wraps should
rather refrain from that to prevent tight linking. An exception is of
course when variables are part of the interface of the class and thus
deliberately exposed.

What I meant though is that an aspect can have its own data, and those
data are private even if the aspect stores those data in another object
(because the data naturally belongs to that object, but managing that data
is the responsibility of the aspect). Actually the aspect controls the
access to the data, and can choose to make its data visible to everyone,
or just visible to itself, and anything in between if we’d think that’d be
necessary. Of course if we’d aspect and aspect, the former would be able
to see the latter’s data too, etc. But maybe in practice this access
control is unnecessary

Good idea, I’ll put those terms in the RCR tonight, and work on ading
indicators, since I think we both agree on the utility of those. Yes?

Yes, definitely.

I think the main differences in our appraoches, please correct me if I’m
wrong, is that you’re coming at it with a more formal understanding derived
from AspectJ, while I’m coming at it more from having implemented wraps by
hacking at Ruby. This is good, b/c it means we are attacking it from both
ends.

I’m indeed coming at it from a different point than you, definitely. But
my point of attack is not so much “more formal understanding derived from
AspectJ”. In my mind, AspectJ is one implementation of AOP. And it
contains many useful ideas, and those are easy to convey (well, mostly).
But although wraps are part of AspectJ, the use of wraps is not limited to
AOP. Wraps are also convenient in other places. But using wraps does not
ensure that you are doing AOP. Neither does doing AOP imply that you need
to use wraps. I never contested the use of wraps. I was just asking myself
(and you) the question whether inner wraps - or at least my interpretation
of it - are really AOP or that it is really just an OOP thing that’s more
naturally done using wraps.

Actually I should tell you that my first contact with AOP had nothing to
do with wraps. The example I was presented was that of code optimization.
That’s a really good example of a cross-cutting concern, but at a finer
granularity than method wrapping. It’s a known fact that good design and
efficiency sometimes don’t agree. And highly optimized code is very hard
to maintain (I have that problem currently at work, and I am forced to
create a new version that will be less efficient, but at least it will
be maintainable and debuggable). But AOP makes it possible to write nicely
designed code and describe the optimizations as aspects.

:slight_smile: It actually looks harder than it is. What makes it so ugly is having to use
method to (hopefully) circumvent name clashes. If I remember correctly
it essentially wraps every method of a class to which #when has been applied,
or in the case of #bind, every single method of a class in order to hook on
instance variable changes. I can give you an end result example of a GUI wrap
if you’d like.

No, it’s not necessary. I still think a GUI is a really good example of
AOP, and I’ve got a pretty good idea what it will look like. And I thing I
know where the misunderstanding comes from, and I can content myself with
that for now.

Great! And the syntax is fine, easy enough to go back and make syntactical
modifications when need be.

True. :slight_smile:

Peter

Ever programmed Haskell? There indentation is significant, and that kinda
sucks. And you can get it right when writing the first version of the
code, if you use no tabs, but changing code possibly changes indentation
and then you get a preview of hell.

scratch scratch scratch. Okay, that eliminates Haskell from the list :slight_smile:

Too bad I don’t like four space indentions :wink:

Me neither. I know some people that do though.

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! :))

I think you just may have just changed my mind partway. What I do like is
the idea of layering wraps. E.g., a logging layer that contains all wraps
related to logging, a profiling layer that contains all wraps related to
profiling, a GUI layer that contains all callbacks to the GUI, etc. But
I’m wondering; do you plan to give explicit control over the singleton
layers? So you can take out a layer, place it back or redefine it. That
creates some nice possiblities.

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.

My initial impression was BTW that for each wrap you’d add a singleton
class, and although I don’t know the Ruby internals, my guess is that
would give overhead proportional to the number of singletons because for a
call to a method in the worst case you’d need to walk over the complete
chain of singleton classes.

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.

BTW, I don’t know if you’ve changed your mind about this (my guess is you
haven’t), but your idea was to just use def for wrapping, and have ‘super’
call either a previous wrap or the method in a superclass (and that fits
nicely with singletons, I know). If you’d ‘def’ a method that does not
call ‘super’, you’d automatically have a redef. Only when you want to do a
redef with calling the method in the superclass that you’d need an extra
keyword ‘redef’. But the one practical problem I see with that is the
issue of how to actually remove wraps that are redefined in the
implementation. You’d want to do that because if you have a very dynamic
application, say one that operates in a number of modes and that often
changes mode and that uses wraps to conveniently model the changing
behavior, then wraps will accumulate if you don’t clean them up. But you
can’t because you have no way of determining whether a method calls super
because of eval.

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?

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.

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

Unless, I’m misunderstanding you (and seeing that this a complex subject
we know that is quite possible :slight_smile: I’m not seeing how this would work.

I think I see the problem… An aspect can access the internal state of
the classes it adds advice to. Generally that is OK because the underlying
system should be independent from the aspect, but not the other way around
(although that would be cool too, but there must be some linking one way
or the other). Also it depends on the kind of wrap. My feeling is that
inner wraps can freely access all variables in a class, outer wraps should
rather refrain from that to prevent tight linking. An exception is of
course when variables are part of the interface of the class and thus
deliberately exposed.

What I meant though is that an aspect can have its own data, and those
data are private even if the aspect stores those data in another object
(because the data naturally belongs to that object, but managing that data
is the responsibility of the aspect). Actually the aspect controls the
access to the data, and can choose to make its data visible to everyone,
or just visible to itself, and anything in between if we’d think that’d be
necessary. Of course if we’d aspect and aspect, the former would be able
to see the latter’s data too, etc. But maybe in practice this access
control is unnecessary

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.

Good idea, I’ll put those terms in the RCR tonight, and work on ading
indicators, since I think we both agree on the utility of those. Yes?

Yes, definitely.

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’m indeed coming at it from a different point than you, definitely. But
my point of attack is not so much “more formal understanding derived from
AspectJ”. In my mind, AspectJ is one implementation of AOP. And it
contains many useful ideas, and those are easy to convey (well, mostly).
But although wraps are part of AspectJ, the use of wraps is not limited to
AOP. Wraps are also convenient in other places. But using wraps does not
ensure that you are doing AOP. Neither does doing AOP imply that you need
to use wraps. I never contested the use of wraps. I was just asking myself
(and you) the question whether inner wraps - or at least my interpretation
of it - are really AOP or that it is really just an OOP thing that’s more
naturally done using wraps.

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!? :wink:

Actually I should tell you that my first contact with AOP had nothing to
do with wraps. The example I was presented was that of code optimization.
That’s a really good example of a cross-cutting concern, but at a finer
granularity than method wrapping. It’s a known fact that good design and
efficiency sometimes don’t agree. And highly optimized code is very hard
to maintain (I have that problem currently at work, and I am forced to
create a new version that will be less efficient, but at least it will
be maintainable and debuggable). But AOP makes it possible to write nicely
designed code and describe the optimizations as aspects.

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?

No, it’s not necessary. I still think a GUI is a really good example of
AOP, and I’ve got a pretty good idea what it will look like. And I thing I
know where the misunderstanding comes from, and I can content myself with
that for now.

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.

T.

···

On Thursday 11 December 2003 04:04 am, Peter wrote: