[ANN] [RCR] Cut-based AOP

Ed Howland wrote:

If so, then this could be a very powerful mechanism in Unit Testing:
(Again, forgive me if the syntax is wrong, here.)

Yep, since DI is a very powerful mechanism for Unit Testing.

Except the cut-style allows you to do RealObjects, MockObjects, and
PartialMocks.

Selon Peter Vanbroekhoven <calamitates@gmail.com>:

Great! One down, many more to go :wink:

LOL! Seriously, I really hope this RCR gets accepted. It is nicely
non-intrusive, and brings functionality that I'm sure many people would use if
it was there.

> - will one be able to subclass (are rather "subcut", one would say) cuts?

Cuts are not supposed to be subclassed (they can't be instantiated, so
what is the point?)

I agree, which is why I corrected myself and said "subcut" afterwards :slight_smile: .

We're not sure if we want to allow adding cuts to

cuts. I think the current implementation allows that, but I'm not sure,
it's been a while.

If you want to reuse the functionality of a cut somewhere else, you should
put it in a module and reuse it like that. In a way, allowing a cut to
apply to multiple classes or modules is akin to multiple-inheritance, and
we want to avoid that.

I find it a great idea to use modules for cuts to. A genial way to minimise the
amount of new features to implement and learn!

> - will cuts be as open as classes? (since they *are* classes, I'd expect
so, but
> I just want confirmation)

Yes, they will be open wide.

Good :slight_smile: .

> - how do cuts work with each other when you add more than one cut to the
same
> class?

They stack on top of each other, the last one added being on top:

   class A
     def m ; '1' ; end
   end

   cut B < A
     def m ; '[' + super + ']' ; end
   end

   cut C < A
     def m ; '{' + super + '}' ; end
   end

   p A.new.m # => "{[1]}"

So they will be stacked on top of each other, in a manner not dissimilar to how
mixins work. Nice and simple :slight_smile: .

> - will the subclass of a class also inherit its superclass's cuts? This is
a
> tricky point I think, as one might expect that the functionality provided
by
> cuts should be available to subclasses, but the implementation (which is
> basically like transparent subclasses) may forbid it. If they don't inherit
> cuts, this could lead to surprises, with a not overriden method behaving
> suddenly differently in the superclass and in the subclass. And what should
one
> expect of the behaviour of "super"?

Yes, subclasses inherit the cuts. This means that in the class hierarchy,
the cuts are in between the class and the superclass. To show this, some
example code:

   class A
     def m ; '1' ; end
   end

   class B < A
     def m ; '{' + super + '}' ; end
   end

   cut C < A
     def m ; '[' + super + ']' ; end
   end

   p A.new.m # => "[1]"
   p B.new.m # => "{[1]}"

So the methods of a class are seen the same from the outside and from the
subclasses.

So I don't have to worry that subclasses would suddenly behave differently from
their parents without explicit overriding. Cool.

That said, I'm suddenly thinking that sometimes one would rather want to
subclass the class as it stands without its aspects. If I'm thinking for
instance of the issue of logging. The way it stands now, if the parent class
gets a logger cut, the subclass suddenly gets logging functionality *in the
middle of its overridden methods*, when they use super, whereas one would
rather expect the logging aspect to be *external* to the implementation of the
subclass (if it is to be there at all). I hope I'm making myself clear. I have
difficulties to express my thoughts in words today...

What do you think? I like the way cuts act in a really transparent way, and that
when subclassing one needn't know about the existence of cuts, but I'm now
wondering whether it could be a problem in some cases when the aspect should
really stay external to the implementation of classes... Do you have a way to
solve that problem already?

Note: if you use the patch, the keyword 'cut' is named '__cut__'. This is
because the Tk library uses 'cut' as a method name and it fails the tests
otherwise.

I hope this small problem won't prevent the RCR to be accepted.

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Peter Vanbroekhoven explained:

Did you mean the following? (Otherwise the class definition of Bar is
useless.)

   Foo = Bar

Quite. Let's imagine that's what I said.

There are a few subtle differences:
* In the second snippet, Foo and Bar are the same. This means that code
  that changes Foo changes Bar as well. In the first snippet, Bar is
  invisible to someone using Foo. Changing Foo only changes Foo and leaves
  Bar alone.
* In the second snippet, existing instances of class Foo are not affected,
  while they are in the first case.

So the basic idea is the same, but a cut is designed to keep existing code
from breaking. It's supposed to be non-intrusive. That's what a cut does
and your second snippet does not.

Actually, in the beginning we considered something like your second code
snippet, but we decided it was not sufficient for our purposes as we
wanted to allow sneaking in a cut into an existing class hierarchy without
breaking the code that uses and changes these classes.

OK, great. I'm starting to see the usefulness of this. It occurs to me that
there is some similarity between this and selector namespaces (targeted for
Ruby 2) -- a lot of what I imagined namespaces being useful for (i.e. making
changes to a core class that only affects code within a particular limited
context) is accomplished easily using cuts.

I still don't know anything, really, about Ruby namespaces. Something else
to look forward too, and something complementary to this, I guess.

Last thing, a criticism: shouldn't cut be a module, but not a class, because
classes are instantiable?

Cheers,
Dave

···

On Wed, 19 Oct 2005 dave.burt@gmail.com wrote:

What's the reason for naming the cut? In all of the code I've
seen so far, the actual cut is never used. So instead of:

cut C < A ... end

why not:

cut < A ... end

or:

cut A ... end

The only thing I can think of off hand is that you might want
to remove the cut down the road.

Conceptually, I think it is easier to think of this cut
operating on the parent class (make it look like you're just
reopening the class). You could think of this "cut" as
something just like "class" except that the superclass is
treated as the previous definition of "class". This also would
extend well to making a cut on the meta class of an object:

cut <<obj ... end

From the syntax you proposed, it would be something like this I
believe:

cut A < <<obj ... end

I didn't see anything in the proposal about cutting the
meta-class of an object, but the above seems like what the
syntax would be. Naming the cut just seems unnecessarily
verbose and complicates the concept.

Here are 2 other ideas that accomplish the majority of what you
are talking about without being so grandiose:

- have another type of def/#define_method (maybe
redef/#redefine_method) that allows redefinition of a method
such that "super" is treated as the previous definition.

- have another keyword like "super" that means previous
definition.

Right now you can actually manually do something like the
above:

class C
  def foo;"foo";end
end

c = C.new

c.foo # => "foo"

class C
  foo1 = instance_method(:foo)
  define_method(:foo) { "{"+foo1.bind(self).call+"}" }
end

c.foo # => "{foo}"

class C
  foo1 = instance_method(:foo)
  define_method(:foo) { "["+foo1.bind(self).call+"]" }
end

c.foo # => "[{foo}]"

Pretty sweet, huh? I wasn't sure if foo1 kept the previous
definition until I tried it, but apparently it does.

Maybe just an easier way to do the above is all that is needed.
I think the above covers what is really needed for AOP - and
we already have it. Just simpler syntax is needed IMO. No
extra baggage needs to be added to the class data structures.

I'm not sure why you guys made such a lengthy RCR. I think I
have the concept now, but with all of the verbosity, it makes
it difficult to figure out what you are really proposing. I
think a much more concise RCR would have been better.

···

__________________________________
Yahoo! Music Unlimited
Access over 1 million songs. Try it free.
http://music.yahoo.com/unlimited/

Hi,

At Thu, 20 Oct 2005 18:10:55 +0900,
Sean O'Halpin wrote in [ruby-talk:161614]:

> How about introducing a new method
> e.g. "preclude" that works to intercept the methods in the target
> class/module, instead of cut. It would be a counterpart of "include"
> for mix-in.

I like the idea but may I point out that "preclude" has a meaning very
close to "exclude". How about "intercept"?

The method lookup order feels "post"clude rather than
"pre"clude. :wink:

···

--
Nobu Nakada

Eric Mahurin schrieb:

module Braces
  remove_method :foo
end

c.foo # => "[bar]"

I really like this. I don't see anything you couldn't do with
this that you can with the cut-based-AOP. And it seems so much
simpler.

I like it too, but instead of having to remove methods from modules, it would be better if we could "exclude" modules from the inheritance chain. Will this be possible in Ruby 2?

Another question: how would I "preclude" a module to an instance?

   Module: include <-> preclude
   Instance: extend <-> ?? (pretend :slight_smile:

Regards,
Pit

#: Christophe Grandsire changed the world a bit at a time by saying on 10/20/2005 2:20 PM :#

Selon Alexandru Popescu <the.mindstorm.mailinglist@gmail.com>:

While, maybe leaving alone all these aspects, may be a Ruby implementation
constraint, they are
valid AOP principles that will miss from the implementation.

From the rest of the discussion here about this RCR, I have understood (and

Trans or others can confirm it I think) that cuts are supposed to be a *basis*,
on which fully-fledged AOP can be built, in a way that fits with Ruby, since the
base itself fits with Ruby. It's a bottom-up approach, instead of the top-down
approach that for instance AspectJ took, and which makes it look very alien to
the rest of Java (in my opinion). I personally believe it's the best approach,
which is why I voted for this RCR: first built the tools you need, then use
those tools to create a framework. This is in my opinion the only way to get
something eventually stable and useful, without looking too much like an add-on
but really like part of the language.

In other words, the RCR isn't here to propose full AOP in one go. It is there to
provide what the authors think (and so far I agree) is the only construct
missing to built fully-fledged AOP, in a way that doesn't break with the rest
of Ruby, and is easy enough to implement. I guess (but never having used it I
might be wrong) that full AOP could be built then as a standard library, built
on cuts and ObjectSpace, and/or Ruby's already existing reflection capacities.

I am always in favour op bottom-up approaches. They may sometimes seem slower,
but on the long term they result in better code, especially when each tool and
addition is built as independently as possible. In a top-down approach, people
seem often far too tempted to look at their project as a single whole, and thus
to build it in such a way, resulting in code which after some time gets
unmanageable.

I am really in favour of cuts (or maybe some other way to implement the same
concept, using modules like matz suggested), because they provide a strong,
stable foundation, on which not only AOP can be built, but who knows what more?
Cuts extend the notion of modularity in a way different from simple modules, and
this could very well lead to plenty of other things besides AOP which is the
main issue they were built for. In other words, don't get too blinded by the
issue of fully-fledged AOP when you review this RCR. See it as foundation for
it, and maybe quite a few other things.
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

I would probably agree with you with some small exceptions:

1/ AspectJ was alien to java because it was meant to provide a full AOP implementation. Meanwhile, considering AJ alien is like saying cut-s are alien to ruby, just because they are trying to introduce a new idea.

2/ in a bottom-up approach as you are considering the cut-s approach, you can miss some of the top views. And this will result in the impossibility to have this top scenarios implemented or working.

3/ if you consider cut-s just an implementation aspect of AOP support in Ruby, than I will agree that it looks quite appealing. But I have read a full set of examples which are based directly on them. I wasn't able to see pointcut definitions, and without them - as I already pointed - there is no aop.

I am not trying to argue on the main idea or on the effort this RCR took. It is indeed a fantastic effort and I appreciate it. I just feel that some aspects should be considered right from the beginning instead of just ignoring them. We have the advantage that other solutions are pretty advanced at this moment and learning from their past is a good thing to do.

respectfully,

./alex

···

--
.w( the_mindstorm )p.

Trans/Peter,

What does a "cut" do that "preclude" doesn't? It seems like
you still can manage the AOP methods through modules instead of
cuts. And you can stack these. Can you give a scenario where
"preclude" doesn't "cut" it :slight_smile:

I still think something like "wrap" would be a better word.
"preclude" may describe the inheritance hierarchy kind of, but
not the functionality from a user's perspective.

···

--- Trans <transfire@gmail.com> wrote:

Hi Matz,

Yukihiro Matsumoto wrote:

> I understand the basic idea. How about introducing a new
method
> e.g. "preclude" that works to intercept the methods in the
target
> class/module, instead of cut. It would be a counterpart of
"include"
> for mix-in.

Yes, this is in the RCR:

Additionally, Cuts exist in proxy form to allow modules to be
"premixed". This is analogous to proxy classes which allow
modules to
mixin to the class hierarchy. So too does a proxy-cut include
a module,
albeit preclusive rather the inclusive in its effect. We
offer the
module command #preclude to serve as designator of this
purpose.

  module A
    def m ; "<#{super}>" ; end
  end
  Class T
    preclude A
    def m ; "okay" ; end
  end
  T.new.m #=> "<okay>"

__________________________________
Yahoo! Music Unlimited
Access over 1 million songs. Try it free.
http://music.yahoo.com/unlimited/

#: Trans changed the world a bit at a time by saying on 10/20/2005 6:36 PM :#

Hi--

Alexandru Popescu wrote:

Hi!

I would like to comment on this proposal. I can agree right from the beginning
that the points here will be presented from a not-so-rubiesc perspective.
the proposal looks like an method-interception framework, rather than a
full blown aop solution.

Yes, in a way you are correct. Ruby provides enough reflective
functionality that it is not neccessary. In other words there is no
need for what you call "full-blown" aop b/c the system already supports
all that is neccessary to achieve full blown aop. This is not to say
there are no areas in which it can be improved, indeed there are. Cuts
is one of those. Peter and I already have developed much of the rest
too: ways to strengthen the hooks and callbacks as well as better EAOP
(event-based AOP) than provided by set_trace_func. But these are for
another day, and more importantly, they do not cover the the majority
of AOP usecase, which Cuts squarely target.

Sorry that I have to say it: no it doesn't support. Should we talk about aspect instantion strategies? Should we talk about field read/write access? Should we talk about flow based pointcuts? Probably this will give raise of an answer that these can be achieved doing X or Y trick. I agree. But the principle behind a framework should be not to require another tricks for making it work.

The proposal is mixin together theoretical aspects of aop with Ruby specific
implementation problems.

I would disagree completely. The proposal narrows down the core of AOP
(namely the 2nd form of AOP implemention) to a formal OOP construct.
The Cut can be implemented in any OOP system, from Simula 67 to
Smalltalk. In fact this has been all along and still is Very Important
to me. I did not want a construct suitable only to Ruby, but something
generally applicable.

While this cannot be considered a real problem, what I have noticed is that one of > the main concepts of the AOP pointcuts are completely ignored.

Pointcuts are mentioned, and why there is no need to have a special
"pointcut thing". Pointcut is a concept of selecting joinpoints
--another abstract term, meaning the place in code to intercept. Well,
we have exactly that. You can specify on small scale of one class and
it's methods, or large scale using ObjectSpace. It is not neccessary to
make a pointcut thing when we already have those. But if you want you
could write your own extermely easily, probably in one line of code.

I have said it too many times: I am not a Ruby dev, so this one line of code pointcut support is not clear to me. Can you help me out? I want a pointcut that will match all calls to a db operation method so that I can crosscut the transaction concerns.

The main idea behind AOP is to be able to define crosscutting concerns. This looks > pieces of functionality that can be used disregarding the OO nature of the > project.

Sorry, I do not full understanding. We do address crosscutting, and OO
nature.

While at the first glance the method interception mechanism is one of most
important aspects of AOP, there are a few distinction that must be considered:
call vs execution, before, around, after pieces of advice.

before and after advice are subset of around advice. We decided it was
just easer to have the one than worry about three since before and
after are merely sugar of around. You are right about call vs.
execution though. In this case it a matter of the 80/20 rule. We make
80% of the need easy (though it's probably more like 99/1 in this
case). If you _must_ have call interception then there are more advance
techinuques to use, such as set_trace_func. We've also discussed
method_dispatching hook. But understand these have much greater impact
on performance than method interception. This is important reason call
interception is not specifically part of this RCR. (But who knows?
Maybe someone will come up with an easy idea for this too).

Before and after advices can be seen as subset of around, but (at least) theoretically speaking they are not. The around advice is the only one that can control the program flow; the others are not.

I am not aware of what are those percentages: 99/1 means that out of 100 pointcuts 99 are execution pointcuts?

While, maybe leaving alone all these aspects, may be a Ruby implementation
constraint, they are valid AOP principles that will miss from the implementation.

There are some. Because Ruby is not 100% OOP, though it is very close,
you can not cut conditionals for example. You've pointed out call
interception already and there are other marginal areas. Ruby itself
has some limitations that effect this. Combined with Ruby's ability to
reflect on itself, Cut-based AOP covers the vast majoirty of AOP needs.
And as Ruby improves so does the reach of cuts.

T.

You don't even want to expose conditionals, or some weird (and unstable) points in program execution.

./alex

···

--
.w( the_mindstorm )p.

Trans wrote:

Daniel Schierbeck wrote:

+1

I only have one concern: I think the syntax is kinda weird. It resembles
the class definition syntax, but I can't see why it should. When you
write "class A < B" you're saying "class A inherits from class B" or
"class A is affected by class B". If we apply the same to "cut A < B",
it would be "cut A is affected by class B", which isn't the case. Why
not use "cut A > B", "cut A affects class C". The singleton syntax could
be like this "cut >> obj".

Your .16 Yen is appreciated :slight_smile: In fact we have discussed similar
notation:

  class A > Ac
    # advice
  end

We decided against, mainly because it looks too much like normal
subclassing. though it is certaintly a possibility.

How I say 'cut Ac < A' is "cut Ac cuts A.", though I normally drop the
first 'cut', "Ac cuts A".

T.

I just don't think the lesser than character should be used when it already has a certain meaning. I think the cut keyword is a good idea, but the < should be a >. Again, that's just my opinion :wink:

   cut A > B; end
   cut >> obj; end

Cheers,
Daniel

"Trans" <transfire@gmail.com> writes:

Does this also provide a quite general form of dependency injection,
very different in nature and weight compared to something like Needle?

Had to give this some thought. Mu immediate conjecture is that it must
since DI and AOP are closely related. But I'm no DI expert like Jim
Weirich. But in giving it some conosideration, I imagine there's not
much more to it than:

  class App

    def logger
      nil # no logger available
    end

  end

  cut AppContainer < App

    def logger
      Logger.new
    end

  end

Which will make you want "multiple-inheritance" for cutpoints. :slight_smile:

It's not that trivial, but it certainly can be used to implement DI.

···

itsme213@hotmail.com wrote:

T.

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

No problem, just something I've been thinking about. Brian Button (and
others,) have said in testing, don't test the disk, and don't test the
I/O subsystem, don't test the OS, and don't test the built-in std
library. So, if the application is supposed to do these things, then
they are hard to test. Which has given rise to many diverse ways of
addressing this via mocking, using interfaces, dependancy injection
and so forth.

Ruby is nice, because you can mess about with its innards, and thus
get around some of these difficulties. Although, with TDDing and
refactoring, you shouldn't paint yourself into corners in the first
place. But for legacy code, this approach has some merit, IMO.

AOP strategies for unit testing are nothing new:
http://blogs.codehaus.org/people/vmassol/archives/000138_aop_unit_testing_example.html

Another potential use of AOP with testing is in fault injection:

cut IMHacker < Kernel
   def exec(command, *args)
       raise SecurityError "Caught ya, you lazy good-fer-nuthing low-life"
       ...
   end
end

def test_catchem
     assert_raise(SecurityError) { exec("cat /etc/passwd") }
end

... where the exec call would really be buried in another class somewhere.

Any is there any prototypes of this available anywhere, or is it too
soon to ask that yet?
I'd vote for this, but RCRchive won't let me in.

Ed

···

On 10/26/05, Trans <transfire@gmail.com> wrote:

> Is this acceptable in the RCR design?

Ed, you're right on the money, Right On The Money.

> Was it already implied?

I believe we make a passing mention of AOP in general having applicable
to testing. But we offer no details in how cuts may be used for this.
Now you've lighted the way --and an interesing way at that. Very cool.

Thanks for sharing this.

T.

Yep, since DI is a very powerful mechanism for Unit Testing.

So you are saying that AOP (or cuts here for example,) can be used to create
DI where you don't already have it. Hmm.

#contrived
require 'collaborator'

class A
   def initialize
      @collab = Collaborator.new
   end
   # ...
end

# in TestCase
def setup
      @mock_collab = MockCollab.new # or some DynamicMock library
end

cut NutherA < A
   def initialize
        @collab = @mock_collab # ignore super for the example
   end
end

def test_add_mock_collab
    a = A.new
    assert_equals("expected", a.do_something_with_collab)
end

Is this what you were refering to with DI and Unit testing with AOP?

Except the cut-style allows you to do RealObjects, MockObjects, and
PartialMocks.

Can you expand on this a little? What are RealObjects (other than the
obvious) and PartialMocks? Aren't RealObjects just real objects that
are being mocked? How does the cut-style help?

Thanks
Ed

···

On 10/27/05, itsme213@hotmail.com <itsme213@hotmail.com> wrote:

Peter Vanbroekhoven explained:

Did you mean the following? (Otherwise the class definition of Bar is
useless.)

   Foo = Bar

Quite. Let's imagine that's what I said.

Done :slight_smile:

OK, great. I'm starting to see the usefulness of this. It occurs to me that
there is some similarity between this and selector namespaces (targeted for
Ruby 2) -- a lot of what I imagined namespaces being useful for (i.e. making
changes to a core class that only affects code within a particular limited
context) is accomplished easily using cuts.

There are definitely parallels, but cuts are at this moment not context-aware by themselves. Something like namespaces can be done as follows:

   cut FileLogging < File
     def initialize(*args, &blk)
       do_some_logging if Thread.current[:LOGGING_ENABLED]
       super
       do_some_more_logging if Thread.current[:LOGGING_ENABLED]
     end
   end

   def with_logging
     old_logging = Thread.current[:LOGGING_ENABLED]
     Thread.current[:LOGGING_ENABLED] = true
     yield
     Thread.current[:LOGGING_ENABLED] = old_logging
   end

   f1 = File.new('some_file') # => no logging
   with_logging do
     f2 = File.new('some_other_file') # => with logging
   end

I think though that this is different from selector namespaces, as my example provides different behavior in a dynamic scope, and selector namespaces are about static scope. That's provided I understand about selector namespaces myself.

I still don't know anything, really, about Ruby namespaces. Something else
to look forward too, and something complementary to this, I guess.

Last thing, a criticism: shouldn't cut be a module, but not a class, because
classes are instantiable?

First things first: when you talk about cut, is that Cut, or an instance of Cut? If it's the latter, then the answer is that we had long discussions about whether Cut should be a subclass of Class or of Module. There are some features that make it class, and some features that make it a module:

* a module because:
   - Not instantiable (though singleton classes aren't instatiable either)
* a class because
   - Not includable (though neither is Class which is a subclass of Module
     too)
   - It has a superclass, namely the class it cuts

So you see, there are arguments for both, and in the end we settled on it being a class.

Peter

···

On Wed, 19 Oct 2005, Dave Burt wrote:

On Wed, 19 Oct 2005 dave.burt@gmail.com wrote:

What's the reason for naming the cut? In all of the code I've
seen so far, the actual cut is never used. So instead of:

cut C < A ... end

why not:

cut < A ... end

or:

cut A ... end

The only thing I can think of off hand is that you might want
to remove the cut down the road.

Well, naming the cut is interesting if you want to reopen the cut and change some methods. And in general for managing the cuts, like removing.

Just like with classes, if you don't want to store the cut or don't want to waste a constant on it, use the constructor:

   Cut.new(A) do ... end

Conceptually, I think it is easier to think of this cut
operating on the parent class (make it look like you're just
reopening the class). You could think of this "cut" as
something just like "class" except that the superclass is
treated as the previous definition of "class". This also would
extend well to making a cut on the meta class of an object:

cut <<obj ... end

Sure, and this is exactly how you can do it.

From the syntax you proposed, it would be something like this I

believe:

cut A < <<obj ... end

What makes you say so? Given that subclassing is done like "class A < B" it does not mean that opening the singleton class of B look like "class A < << B". Some here. If you want to give the cut a name, use "cut A < B", if you want to cut the metaclass, use "cut << B", and if you want an anonymous cut, use "Cut.new(A)".

I didn't see anything in the proposal about cutting the
meta-class of an object, but the above seems like what the
syntax would be. Naming the cut just seems unnecessarily
verbose and complicates the concept.

Then it must be missing from the proposal, it was certainly intended to be possible. As for the unnecessary complexity, I don't agree. Naming the cut allows you to access it again, and change methods ro remove them, etc. It gives you a handle on them.

Here are 2 other ideas that accomplish the majority of what you
are talking about without being so grandiose:

- have another type of def/#define_method (maybe
redef/#redefine_method) that allows redefinition of a method
such that "super" is treated as the previous definition.

In a way this is what Matz wanted to do with "def method:wrap", it's just a different syntax. A big problem with this is how to manage these redefinitions. How do you propose we redefine or remove a specific rededinition if there are many for the same method?

- have another keyword like "super" that means previous
definition.

We've been over that in our discussions before. One problem is that that keyword can be hidden inside an eval and all, which makes it impossible to determine whether the previous definition is actually called. This causes a problem with garbage collecting previous method definitions, as it is impossible to determine the dead ones automatically in all possible cases.

Also, this does still not allow the redefinitions to be managed in any sensible way.

Right now you can actually manually do something like the
above:

class C
def foo;"foo";end
end

c = C.new

c.foo # => "foo"

class C
foo1 = instance_method(:foo)
define_method(:foo) { "{"+foo1.bind(self).call+"}" }
end

c.foo # => "{foo}"

class C
foo1 = instance_method(:foo)
define_method(:foo) { "["+foo1.bind(self).call+"]" }
end

c.foo # => "[{foo}]"

Pretty sweet, huh? I wasn't sure if foo1 kept the previous
definition until I tried it, but apparently it does.

Maybe just an easier way to do the above is all that is needed.
I think the above covers what is really needed for AOP - and
we already have it. Just simpler syntax is needed IMO. No
extra baggage needs to be added to the class data structures.

I'm not sure why you guys made such a lengthy RCR. I think I
have the concept now, but with all of the verbosity, it makes
it difficult to figure out what you are really proposing. I
think a much more concise RCR would have been better.

The reason for cuts, for naming them, and the rest of the RCR, is simply because any other way proposed (using closures as above, and already shown in the past by Ara I think, or using method aliasing, and so on) makes it very hard to manage the stack of methods. Cuts give you the handle to do so. If you want the set of methods you've added, you can just grab the cut and you've got them.

We've been over all these things before in our discussions, including what you mention above, and none of it works well with Ruby's dynamic nature. Because cuts are based in inheritance, it does work with Ruby's dynamic nature.

Peter

···

On Wed, 19 Oct 2005, Eric Mahurin wrote:

I find it a great idea to use modules for cuts to. A genial way to minimise the
amount of new features to implement and learn!

Thank you for the kind words!

So I don't have to worry that subclasses would suddenly behave differently from
their parents without explicit overriding. Cool.

That said, I'm suddenly thinking that sometimes one would rather want to
subclass the class as it stands without its aspects. If I'm thinking for
instance of the issue of logging. The way it stands now, if the parent class
gets a logger cut, the subclass suddenly gets logging functionality *in the
middle of its overridden methods*, when they use super, whereas one would
rather expect the logging aspect to be *external* to the implementation of the
subclass (if it is to be there at all). I hope I'm making myself clear. I have
difficulties to express my thoughts in words today...

I think I know what you mean. I think this can be done with a bit of library support. The following should do what you want it to do, and it can surely be done automatically by a library:

   class A
     def do_transaction ; ... ; end
   end

   class B < A
     def do_transaction
       do_some_stuff
       super
     end
   end

   module CutMixin
     def do_transaction
       do_logging if Thread.current['InsideLogger']
       old = Thread.current['ACut::InsideLogger']
       Thread.current['ACut::InsideLogger'] = true
       super
       Thread.current['ACut::InsideLogger'] = old
       do_logging if Thread.current['InsideLogger']
     end
   end

   cut ACut < A
     include ACutMixin
   end

   cut BCut< B
     include ACutMixin
   end

What do you think? I like the way cuts act in a really transparent way, and that
when subclassing one needn't know about the existence of cuts, but I'm now
wondering whether it could be a problem in some cases when the aspect should
really stay external to the implementation of classes... Do you have a way to
solve that problem already?

Note: if you use the patch, the keyword 'cut' is named '__cut__'. This is
because the Tk library uses 'cut' as a method name and it fails the tests
otherwise.

I hope this small problem won't prevent the RCR to be accepted.
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Peter

···

On Wed, 19 Oct 2005, Christophe Grandsire wrote:

Eric Mahurin wrote:

Right now you can actually manually do something like the
above:

class C
  def foo;"foo";end
end

c = C.new

c.foo # => "foo"

class C
  foo1 = instance_method(:foo)
  define_method(:foo) { "{"+foo1.bind(self).call+"}" }
end

c.foo # => "{foo}"

class C
  foo1 = instance_method(:foo)
  define_method(:foo) { "["+foo1.bind(self).call+"]" }
end

c.foo # => "[{foo}]"

Pretty sweet, huh? I wasn't sure if foo1 kept the previous
definition until I tried it, but apparently it does.

Maybe just an easier way to do the above is all that is needed.
I think the above covers what is really needed for AOP - and
we already have it. Just simpler syntax is needed IMO. No
extra baggage needs to be added to the class data structures.

Peter covers this well in his repsonse. I just want to point out one
additional thing about using a Cut rather using simple method hooks:
*introduction*. Using Cuts will allow you to conveniently store
information (i.e. state) pertaining to that cut/aspect independent of
the class being cut --an important criterea for good AOP. Presently
class instance vars are what you'd use for this, but as Ruby comes to
support local instance varaibles and/or methods introduction will
become even better.

I'm not sure why you guys made such a lengthy RCR. I think I
have the concept now, but with all of the verbosity, it makes
it difficult to figure out what you are really proposing. I
think a much more concise RCR would have been better.

Granted there are some spots where we could have been a little more
concise. But we wanted to be thurough, much like a white paper, rather
then something just thought up of a few days ago and thrown up. We also
hope the brief overwiew of AOP would be of benefit to those not so
familar with AOP.

Thanks,
T.

Hi ... need help ...

Just had Ruby 1.8.3 installed on our HP-UX box, and it seems to be
really misbehaving... I'm trying to promote Ruby here but this is
giving it bad press.

For example, the following code

#!/usr/local/bin/ruby
p ARGV
ARGV.each do |fname |
     puts "File: #{fname}"
     loop do
         break
     end
end

gives .....

xdump param

["param"]
File: param
/home/fcux_dev/tadb/bin/xdump:6: [BUG] Bus Error
ruby 1.8.3 (2005-09-21) [hppa2.0w-hpux11.11]
Abort(coredump)

And in fact, this error ([BUG] Bus Error - what is it ?) seems to pop up
at the slightest provocation, although some relatively complex
ruby code seems to run ok.
I didn't do the installation myself, but I suspect that the HP-UX
bundled C compiler was used to do the installation.
Could that be the problem ?

This message and any attachments (the "message") is
intended solely for the addressees and is confidential.
If you receive this message in error, please delete it and
immediately notify the sender. Any use not in accord with
its purpose, any dissemination or disclosure, either whole
or partial, is prohibited except formal approval. The internet
can not guarantee the integrity of this message.
BNP PARIBAS (and its subsidiaries) shall (will) not
therefore be liable for the message if modified.

···

---------------------------------------------

Ce message et toutes les pieces jointes (ci-apres le
"message") sont etablis a l'intention exclusive de ses
destinataires et sont confidentiels. Si vous recevez ce
message par erreur, merci de le detruire et d'en avertir
immediatement l'expediteur. Toute utilisation de ce
message non conforme a sa destination, toute diffusion
ou toute publication, totale ou partielle, est interdite, sauf
autorisation expresse. L'internet ne permettant pas
d'assurer l'integrite de ce message, BNP PARIBAS (et ses
filiales) decline(nt) toute responsabilite au titre de ce
message, dans l'hypothese ou il aurait ete modifie.

Eric Mahurin schrieb:
> module Braces
> remove_method :foo
> end
>
> c.foo # => "[bar]"
>
> I really like this. I don't see anything you couldn't do
with
> this that you can with the cut-based-AOP. And it seems so
much
> simpler.

I like it too, but instead of having to remove methods from
modules, it
would be better if we could "exclude" modules from the
inheritance
chain. Will this be possible in Ruby 2?

Yep, that might be useful too. So you'd do this instead:

class C
  exclude Braces
end

Another end case you have to think about is if a class
precludes the same module twice. Does it wrap the method
twice? And if so, does "excluding" it remove both wraps.

Another question: how would I "preclude" a module to an
instance?

   Module: include <-> preclude
   Instance: extend <-> ?? (pretend :slight_smile:

Regards,
Pit

Yes, I was just thinking about that too. "pretend" was the
first that came to my mind too :slight_smile: But, you could also do it
manually:

class <<obj;preclude XXX;end

Another possiblity for a name instead of "preclude" would be
"wrap":

class C
  wrap Brackets
  wrap Braces
end

c.foo # => "{[foo]}"

class C
  unwrap Brackets
end

c.foo # => "{foo}"

···

--- Pit Capitain <pit@capitain.de> wrote:

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005

Eric Mahurin wrote:

What does a "cut" do that "preclude" doesn't? It seems like
you still can manage the AOP methods through modules instead of
cuts. And you can stack these. Can you give a scenario where
"preclude" doesn't "cut" it :slight_smile:

Given the implementation of proxy-cuts, it is basically the exact same
difference between class and module. If not familar with how a module
is inclued into the class hierarchy via a proxy-class, have a look in
Pickaxe. It explains it well. Also cuts aren't instatiable on their own
so they are even more like modules in that one respect. So the answer
is "very little".

There is one difference though that can in certain circumstance be
problematic if only a module solution were available: the Dynamic
Module Inclusion Problem. You can see this problem with normal modules.

  module X
    def x; 1; end
  end

  class C
    include X
  end

  c = C.new

  p c.x #=> 1

  module Z
    def z; "z"; end
  end

  module X ; include Z ; end

  p c.z #=> NoMethodError

We've include X into class C, then instatiated an instance of it. If
later we include another module Z into module X, it will not show up in
C. If we had included Z directly into class C instead we would not have
this problem. In the same manner this would effect precluded modules,
and while not an utter show stopper, it is more likely to occur for
cuts b/c of the nature of AOP, esspecially if were talking about making
them even more dynamic, including and extracting them on the fly.

Note, the Dynamic Module Problem is an ackowledged issue with Ruby and
has nothing to do with cuts themselves. From what I understand, it is
not a simple problem to solve and would take a great deal of effort and
a good bit of alteration to Ruby --if it is even efficenty possible.

Other then that I'm not sure if there is anything *undoable*. You can
program Ruby without every subclassing too; modules do everyhting you
need. But even so underthehood the subclass is still there in proxy
form. So I'm not sure if there is really anything to gain from not
providing the Cut to the end user given that it's already underneath
anway.

And actually it is nicer to use when your just need is to cut a
specific class:

  class C
    def x ; '1' ; end
  end

  cut A < C
    def s ; '{' + super + '}' ; end
  end

vs.

  class C
    def x ; '1' ; end
  end

  module A
    def s ; '{' + super + '}' ; end
  end

  class C
    preclude A
  end

There is no need to reopeon the class.

Ah, there is one more thing. Using Cuts could allow for cutting cuts, a
way to insert advice prior to other prexisting advice.

  class C
    def x ; '1' ; end
  end

  cut A < C
    def s ; '{' + super + '}' ; end
  end

  C.new.x #=> {1}

  cut B < A
    def s ; '[' + super + ']' ; end
  end

  C.new.x #=> {[1]}

Notice cuts stack inversely when cutting each other. We never decided
if this would be allowed or not, but it is a possibility. It would not
be as tidy if only modules were availble, moreovor it would definitely
come-up against the Dynamic Module Inclusion Problem.

I still think something like "wrap" would be a better word.
"preclude" may describe the inheritance hierarchy kind of, but
not the functionality from a user's perspective.

I didn't like #preclude at first, but nothing ever felt as right
considering its parallel to #include. There is #prepend, but I'm sure
that will be matched against #append_features which #include uses. But
if you think about it preclude means essentially "forgone conclusion".
That's pretty close.

T.