[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: Underpinnings of Method Wrapping

Peter

11/30/2003 12:02:00 AM

7 Answers

T. Onoma

11/30/2003 2:53:00 AM

0

On Sunday 30 November 2003 01:01 am, Peter wrote:
> Oh, I agree completely, Ruby will be able to specify the most general
> pointcuts possible since you can specify it in code. Any code. Wildcards
> is limiting. Though maybe I should mention that the AspectJ guys said
> nobody ever missed anything more general then wildcards in AspectJ.

How would they know? ;-)

> I agree we should support both scenarios. But I don't agree yet on
> discarding wrappers by default. Aspects are supposed to model orhtogonal
> aspects of an application and are supposed to be uncoupled. Which means
> wrapper and wrappee ought to be uncoupled. But maybe that's one of those
> cool ideas that are infeasible in practice. And the use of method
> combination may not be limited to AOP...

I'm had some varying thoughts on this. But I think I've come to a good
solution. First let me point out that, although unmentioned in my RCR, I'm
all for pre and post, and actually, I'm now in favor of dblack's suggestion
of simply using pre and post as keywords [yes, db, bygones &c.] b/c they are
ONLY for "meta-tasks" and can't get invovled in the underlying exectution; so
in this manner they are quite distinct from def. In other words, defs "get in
line" while pres and posts "hang about", if you take my meaning. In light of
this, it seems appropriate for redef to flush the wrap stack, but all pre's
and post's still remain. So we have proper seperation of these concerns: pre
and post for extrinsic, def wrap for intrinsic.

> > This reminds me of an RCR I put in once. I suggested that every object
> > have a keyword reference back to its "brith parent". So if I cerated
> > object foo = Foo.new from within object bar, foo#parent would reference
> > bar. In this case it seems their might be good reason for each method to
> > have a keyword method (Method#cflow) referenceing the method object it
> > was called from. In that way I think we could offer cflow support.
>
> Yes, except that Method#cflow will only allow looking back one step in the
> calling stack. In AspectJ, cflow looks back along the complete calling
> stack, which is quite expensive to do if you'd use a Method#cflow for
> looking at the calling method.

Is it very expensive? ameth.cflow.cflow... hmmm, I guess your right, if your
looking for a method that could be anywhere up the reference list. Although I
think in practice the list will only get so long. (And shortcutted for
recursive calls). But I definitely see your point (though I think
Method#cflow would still have its uses).

> Actually AspectJ's implementation can be
> done in Ruby using only wraps as follows:
>
> def foo
> # ...
> end
>
> def foo
> oldInFooCFlow = $inFooCFlow
> $inFooCFlow = true
> super
> $inFooCFlow = oldInFooCFlow
> end
>
> def bar
> # ...
> end
>
> def bar
> if $inFooCFlow
> # advice added to bar in the cflow of foo
> end
> super
> if $inFooCFlow
> # advice added to bar in the cflow of foo
> end
> end
>
> I used your RCR syntax. And forgive me the use of the global var, but that
> least clutters the point. So it is possible foo calls some foo2, which
> calls some foo3, ... until eventually bar is called, and it will work
> without the need for looking all the way back up the call stack.

I noticed right away that you were using my syntax :-) I've heard so many
people say how ridiculous such a syntax is (funny that they keep mentioning
it), but right now I can tell you it feels completely natural --seeing the
defs come in order parallels their stacking order. Anyway...

> As for the use of the global variable, in AspectJ this is easily done.
> Every aspect in AspectJ is a singleton, with its own data members, and all
> the advice from that aspect (read: wraps) can access these data members.
> As such it effectively provides global, yet private variables.

Definitely, and I'm certain we can achieve the same thing with module's and
their instance variables. Define a module containing aspect methods and the
module variables they need and apply as desired. Hell, we can even wrap the
aspect methods!

> > I think a lot of this is pointing to the simple fact that Ruby needs to
> > make its Methods more capable and introspective, much in the same way
> > that classes/objects are. For instance, cflow, and my suggestion for
> > built-in keywords for args, keys and block, plus a way to see what
> > class/module the method is in (from the inside out, we can already do
> > from the outside in), and so on. Having such capabilities helps exploit
> > the full potential of AOP.
>
> Agreed.

Have to add to RCR.

> > I cann see it to some degree: off the top of my head: If the method #kill
> > was called from method #hunt, then you might want to trigger the method
> > #excitement, but if the same method #kill was called from
> > #protect_thyself, then it should instead trigger #relief. Very abstract,
> > but I think it paints the proper idea.
>
> I understand. It offers a way to find out about the callers of a method -
> or more abstract: the context of a call. Though the abstract idea sounds
> mighty interesting, I'm not so crazy about using method names to find out
> about the context. But I don't know about a better way. Just weird that
> you have to name your method in a particular way to get the right behavior
> from a method you're calling.

Ah...but this is what the indicators are for.

def eat:eating
end

def gorge:eating
end

def stuffyourface:eating
end

We can aspect all the eating methods. ( no, i'm not hungry :-)

> Peter
>
> > P.S. So what did you think of the singleton foundation?
>
> Are you referring to the line "wraps act very much like singletons" from
> your RCR? In that case, I think I missed the point. But that's probably
> because it was the explanation to something that's so obvious to me :-)

I really need to go back and specify this. It was still a little fuzzy in my
mind when I started the RCR, but now it's quite clear --perfectly clear. So
much so that I pulled up the Ruby source and hacked it in. Changed about 6
lines of code and got it working, albiet it still needs TLC to fix-up and add
all the bells and whistles, but the proof is in pudding, and I got pudding.

But I'm getting ahead of myself. I need to explain the point. Lets take this
example:

class Classic
def x
print "X"
end
end
c = Classic.new
def c.x
print "("
super
print ")"
end
c.x; puts # => (X)

We have created an object c, that has a method x, then we defined a singleton
also called x. The singleton effectively wraps the original method. This is
explained well in the Pickaxe. Check out page 246, Fig. 19.3. The diagram is
describe a couple pages back under Object-Specific Class; or you can surf
here:

http://www.rubycentral.com/book/cl...

So then what happens if we try to add another singleton of the same name?

def c.x
print "["
super
print "]"
end
c.x; puts # => [X]

Why didn't it wrap again? Let me quote the end of that Pickaxe section:

"Ruby performs a slight optimization with these singleton classes. If an
object's klass reference already points to a singleton class, a new one will
not be created."

Obviously when Matz created this, it didn't occur to him that unlimted
wrapping may be useful. He just saw that it was wasted overhead to have a
separate singleton class for each and every singleton method. But his
optimization had the above side effect --you can only define one layer. So I
went back and changed the optimization such that when a method of the same
name already exists in the singleton class, a new singleton class (metaclass
in the code) is created. So my version of Ruby instead does the following:

def c.x
print "["
super
print "]"
end
c.x; puts # => [(X)]

That's all there was to it, and it serves as the perfect starting point/basis
for building all the rest of the AOP features.

-t0



Mauricio Fernández

11/30/2003 9:38:00 AM

0

On Sun, Nov 30, 2003 at 11:53:25AM +0900, T. Onoma wrote:
> But I'm getting ahead of myself. I need to explain the point. Lets take this
> example:
>
> class Classic
> def x
> print "X"
> end
> end
> c = Classic.new
> def c.x
> print "("
> super
> print ")"
> end
> c.x; puts # => (X)

This works fine with singletons, but there is a problem if you want to
do it for "normal" instance methods (i.e. wrapping the method for all
objects of a given class), see [ruby-talk:75334] for an explanation and
[ruby-talk:75290] for an implementation using an explicit indication
"plz make this method available with super after redefinition".

--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Never make any mistaeks.
-- Anonymous, in a mail discussion about to a kernel bug report


T. Onoma

11/30/2003 12:41:00 PM

0

On Sunday 30 November 2003 10:38 am, Mauricio Fernández wrote:
> This works fine with singletons, but there is a problem if you want to
> do it for "normal" instance methods (i.e. wrapping the method for all
> objects of a given class), see [ruby-talk:75334] for an explanation and
> [ruby-talk:75290] for an implementation using an explicit indication
> "plz make this method available with super after redefinition".

Thank you very much for pointing this post out. It contains a very interesting
point that requires some close analysis. In doing so we will discover that it
is actually a little short sighted, but nonetheless hints at a very
interesting direction for extending the capabilites of mixins, that is
subequently the basis for class definable singletons. Let me explain.

The first thing to notice is that Matz' solution only solves the proposed
problem to one degree, i.e. for one layer of wrapping. Beyond that you run
into the same issue --if Array had also defined each:before (now each:pre)
then the order of occurance would again be the only available criteria for
determining the wrap stack order. So even with Matz's solution the problem is
not solved for the general case.

One could also argue, that the problem itself is a bit dubious, b/c #each is
not defined by Enumerabe, but rather defines methods that utilize #each as
given by other classes that mixin Enumerable. Moreover, by their very nature
Mixins do not prepend, like singletons, but rather append, like superclasses.
So the notion is a bit misconceived from the get-go. But I am not one to
nitpick such "excuses"! It is understandable that one might think of doing
something like this as a convenient way to aspect classes that partake of
Enumerability. So...

To accomplish this means we must somehow overcome the fact that Mixins only
append. Take this working "hack-up":

class Array
def testing
print "T"
end
end

enum = Enumerable.dup
enum.module_eval {
def each
print "before"
super
end
def testing
print "("
super
print ")"
end
}

a = [1,2]
a.extend(enum)
a.each { |i| print i }; puts # => before12
a.testing; puts $ => (T)

This achieves the desired result becasue we have prepended the Enumerable
module to the Array using extend, as opposed to the "under the hood"
appending that occurs with 'include Enumerable'.

So what would it take to generalize this in a class definable way? It is of
course, essentially the same means that is required to generalize singleton
definitions in a class defineable way. In the particluar case of Mixins, an
interesting possibility comes to mind:

module Enumerable
prepend # <= keyword
def each
puts "before"
super
end
# prepend :each # <= maybe an additional way to do it
end

So everything following the prepend keyword would be prepended as "mixin
singleton class" rather then appended as "mixin superclasss". And so are
problem is solved in all cases.

---

On a side note, it is interesting to consider the fact that we don't have the
keyword super for classes and modules.

class Array
super
...
end

So that without super we could completely redefine Array, while with super, it
would mean we are adding to it, as we currently do. As it stands, I wonder,
is there even a way to wholly and easily redefine any built-in class or
module? A way does not immediately come to mind, albiet this is something
that would be extremely rare anyway.

---

It should be apparent enough by now that much of what I'm proposing is greater
uniformity in the way methods, classes, and modules are defined and
manipulated. There is no doubt in my mind that this would make Ruby a better
language. Take a look at the general criteria of a good language as given by
Lambda the Ultimate and you will see Uniformity as second on the list under
Simplicity.

1. Simplicity
2. Uniformity
3. Orthogonality
...

from http://lambda.weblogs.co...

-t0














nobu.nokada

12/1/2003 12:55:00 AM

0

Hi,

At Sun, 30 Nov 2003 21:40:52 +0900,
T. Onoma wrote:
> On a side note, it is interesting to consider the fact that we don't have the
> keyword super for classes and modules.
>
> class Array
> super
> ...
> end
>
> So that without super we could completely redefine Array, while with super, it
> would mean we are adding to it, as we currently do. As it stands, I wonder,
> is there even a way to wholly and easily redefine any built-in class or
> module? A way does not immediately come to mind, albiet this is something
> that would be extremely rare anyway.

Different super class makes different class.

class Array < Class.new
end

--
Nobu Nakada


T. Onoma

12/1/2003 2:11:00 PM

0

On Monday 01 December 2003 01:55 am, nobu.nokada@softhome.net wrote:
>
> Different super class makes different class.
>
> class Array < Class.new
> end

Thanks nobu. That makes sense.

BTW does this mean if I redefine Array the literal form will use my new class?

class Array < MyArray
end

[1,2,3].kind_of?(MyArray) # => true




nobu.nokada

12/2/2003 5:21:00 AM

0

Hi,

At Mon, 1 Dec 2003 23:10:44 +0900,
T. Onoma wrote:
> BTW does this mean if I redefine Array the literal form will use my new class?
>
> class Array < MyArray
> end
>
> [1,2,3].kind_of?(MyArray) # => true

No. The interpreter current implementation uses rb_cArray
directly, and redefinition of built-in classes causes crash at
GC.

--
Nobu Nakada


nobu.nokada

12/2/2003 5:28:00 AM

0

Hi,

At Tue, 2 Dec 2003 14:21:06 +0900,
nobu.nokada@softhome.net wrote:
> > BTW does this mean if I redefine Array the literal form will use my new class?
> >
> > class Array < MyArray
> > end
> >
> > [1,2,3].kind_of?(MyArray) # => true
>
> No. The interpreter current implementation uses rb_cArray
> directly, and redefinition of built-in classes causes crash at
> GC.

Sorry, built-ins are referred by class_tbl, so it won't cause
crash even if you were redefine them.

We had a discussion about it once, but I forgot it and was
confused a bit.

--
Nobu Nakada