Method wrapping

Hi,

···

In message “Re: Method wrapping” on 03/11/27, Peter Peter.Vanbroekhoven@cs.kuleuven.ac.be writes:

… since they stack and since are anonymous in a sense. If there are 5
foo:pre methods, how can you indicate which one it is that you want to
redefine? Or is it just no redefining, wrap-and-live-with-it?

Basically. I think I will prepare method handling API to operate on
method combinations.

						matz.

Sounds like “wrap and live with it”, which is OK by me.

Gavin

···

On Thursday, November 27, 2003, 10:50:47 AM, Peter wrote:

Will it also be possible to redefine wrapper methods? (I believe that’s
what the original question intended) With primary methods it’s obvious
which primary method is being redefined, but not with wrapper methods
since they stack.

… since they stack and since are anonymous in a sense. If there are 5
foo:pre methods, how can you indicate which one it is that you want to
redefine? Or is it just no redefining, wrap-and-live-with-it?

I don’t think that’s a desirable behavior. See previous post.

Also, the hooks (wraps) of a method are generally dependent on how that method
works. If you redefine a method without removing the hooks, your new method
will have to conform to the expectations of those hooks, or things will
break.

-t0

···

On Thursday 27 November 2003 07:07 am, Yukihiro Matsumoto wrote:

  1. If the primary method is redefined, do the “secondary” methods
    get cancelled? Or do they simply remain in place?

They remain in place.

Right?

Nope. With your example, A#riot no longer calls back to X. This is not what I
am talking to. What I mean is that in the second class ‘A’, def doesn’t do
the same kind of thing as in in third class ‘B < A’, even though the syntax
is exactly the same
. You only know that they do something different because
you look up to top of class and see ‘< A’, then you have to say “Oh, that
means it all works different.”

class A < X
def riot
print “P”
end
end

class A
def riot # this redefines
super
print “R”
end
end

class B < A # this changes how def works if method is in superclass
def riot
super # this wraps
print “W”
end
end

It is important to understand that Ruby already has a wrapping facility
using subclasses. And singletons act similiarly. So it would be more
consistent to naturally extend that to in-class wraps. We can do this
without having to add special syntax sugar (the colon).

But how? I don’t see concrete syntax you are saying.

Read the following to fully understand and get syntax:

http://www.rubygarden.org/ruby?AspectOrientedRuby

But if you don’t have the time right now here is a quick full example. Notice
that the defs could be in superclass or not. It makes no difference to how
wrapping works.

def w # this could be in superclass
print “W”
end

def w # and so could this
print “Y”
super
print “Y”
end

def w
print “[”
superwrap
print “(”
super
print “)”
end
print “]”
end

w; puts # => [Y(W)Y]

This is join-point map to help you understand above:

def a_method
# ← pre join-point
print “<”
# ← super-pre join-point
super
# ← super-post join-point
print “>”
# ← post join-point
end

Note that, if you need to BOTH redefine AND access superclass method, then you
would use redef, not def. In all other ways, this is backward compatible with
Ruby1. Please ask me any questions if something dosen’t make sense.

Happy Thanksgiving, Matz,
-t0

(Do you have Thanksgiving in Japan?)

···

On Thursday 27 November 2003 10:28 am, Yukihiro Matsumoto wrote:

Yukihiro Matsumoto wrote:

  1. If the primary method is redefined, do the “secondary”
    methods get
    cancelled? Or do they simply remain in place?

They remain in place. They will be removed altogether when
you remove the method using “remove_method” method.

I have asked the same this question as well and I really wish
you could reconsider this(= leave the stack hook method
stack in place).

Firstly, you pretty much have to kill this stack if the signature
of the primary method was changed since the signatures of
hook methods written for the old primary method can’t be
used anymore.

Secondly, if I changed the primary leaving "pre’‘, "post’’ and
“wrap” conditions often makes little semantic sense so they
should always be killed.

Maybe a comprise of leaving the stack in place if the signature
didn’t change and killing it if the signature of primary method
Changed, or as you indicated, if the primary method was removed
altogether is most flexible approach?

/Christoph

Thanks again Mark!

···

-----Original Message-----
From: Mark J. Reed [mailto:markjreed@mail.com]
Sent: Wednesday, November 26, 2003 1:57 PM
To: ruby-talk ML
Subject: Re: defining a ‘puts’ or a ‘print’ for a class

On Thu, Nov 27, 2003 at 03:48:58AM +0900, Zach Dennis wrote:

But I would love to say:

puts Email

to get the same result.

Any ideas?

Define a ‘to_s’ method.

irb(main):001:0> class Point
irb(main):002:1>     def initialize(x, y)
irb(main):003:2>         @x, @y = x, y
irb(main):004:2>     end
irb(main):005:1> 
irb(main):006:1*     def to_s
irb(main):007:2>         '(' + @x.to_s + ', ' + @y.to_s + ')'
irb(main):008:2>     end
irb(main):009:1> end  
=> nil
irb(main):010:0> p = Point.new(3,4)
=> #<Point:0x400a3d00 @y=4, @x=3>
irb(main):011:0> puts p
(3, 4)
=> nil

If you also want your custom string form to show up in the irb inspection
(after the =>), define ‘inspect’, too:

irb(main):011:0> class Point
irb(main):012:1> def inspect; to_s; end
irb(main):013:1> end
=> nil
irb(main):014:0> p
=> (3, 4)
irb(main):015:0> 

-Mark

It would and it does. Thanks Dale for your response!

···

-----Original Message-----
From: Dale Martenson [mailto:dmartenson@multitech.com]
Sent: Wednesday, November 26, 2003 2:02 PM
To: ruby-talk ML
Subject: Re: defining a ‘puts’ or a ‘print’ for a class

On Wed, 2003-11-26 at 12:48, Zach Dennis wrote:
I’ve got a class called Email and right now i have:

class Email
	def initialize
		....stuff here....
	end

	def print
		return ....stuff here....
	end
end


e = Email.new( ...stuff here... )
puts Email.print



But I would love to say:

puts Email

to get the same result.

Any ideas?

You could do something like:

 class Email
    def initialize(email)
       @email = email
    end
    
    def to_s
      @email
    end
 end

 e = Email.new("bob@mail.com")

 puts e

Would that work for you?

If you are redefining your own method in development, you know what
you’re doing - you’re probably just mucking around - and can sort out
the wrappers yourself.

If you are redefining someone else’s method in production, on your own
head be it. You should be keeping to the same interface of the
original, or else other code is likely to break.

Wrappers don’t depend on the implementation of a method, as you
assert. They depend on the interface. The wrappers should have no
real side-effect anyway, I suspect, except for logging, asserting, …

It is certainly a curly area, though.

Gavin

···

On Thursday, November 27, 2003, 5:56:13 PM, T. wrote:

On Thursday 27 November 2003 07:07 am, Yukihiro Matsumoto wrote:

  1. If the primary method is redefined, do the “secondary” methods
    get cancelled? Or do they simply remain in place?

They remain in place.

I don’t think that’s a desirable behavior. See previous post.

Also, the hooks (wraps) of a method are generally dependent on how that method
works. If you redefine a method without removing the hooks, your new method
will have to conform to the expectations of those hooks, or things will
break.

Hi,

···

In message “Re: Method wrapping” on 03/11/27, “T. Onoma” transami@runbox.com writes:

They remain in place.

I don’t think that’s a desirable behavior. See previous post.

Which one? We have too many “previous” posts here.

						matz.

Basically. I think I will prepare method handling API to operate on
method combinations.

Just a stupid question, but wouldn’t it be cleanest conceptually if a
wrapper method was given a handle - say a name like any method - such that
you can refer to each wrapper by name? Something like foo:pre:bar (I suck
at inventing cool Ruby syntax). It would be optional, but if you don’t
specify it, you have to live with the wrap. And the the manipulation of
the wrapper methods is just like manipulation of the primary methods. Of
course this is my view since I can’t see yet how you can make a clean API
to manipulate method stackings. Since you don’t know what method on the
stack is yours, how can you identify it? Even if you are in a development
cycle and you know what you are doing… Maybe you could show the code for
the wrappers, or give line numbers where they were defined, but that’s a
bit weird and different from what you can do with primary methods
currently.

Peter

I have asked the same this question as well and I really wish
you could reconsider this(= leave the stack hook method
stack in place).

Firstly, you pretty much have to kill this stack if the signature
of the primary method was changed since the signatures of
hook methods written for the old primary method can’t be
used anymore.

Secondly, if I changed the primary leaving "pre’‘, "post’’ and
“wrap” conditions often makes little semantic sense so they
should always be killed.

Maybe a comprise of leaving the stack in place if the signature
didn’t change and killing it if the signature of primary method
Changed, or as you indicated, if the primary method was removed
altogether is most flexible approach?

I think Matz’s way is the best compromise. Besides, if you only add
arguments to methods or slightly change (duck) type of some arguments,
you’ll often be safe with an approach like this:

def foo:wrap(a1, a2, *a)
do_something_with(a1, a2)
p “logging call to foo(#{a1}, #{a2}, #{a.join(', ')})”
result = super
p “result = #{result}”
result
end

Removing hooks on change of a method means all hooks have to be added
again needlessly in some cases. And if a change is incompatible with the
hooks, you can remove or redefine them yourself once there’s an API to do
so. And my guess is it will be less work to remove hooks manually than to
add hooks manually because if you’re careful most hooks remain valid. And
besides, on large changes to signatures of methods, you’ll have a lot of
work to adapt the rest of the code anyhow.

Peter

If you are redefining your own method in development, you know what
you’re doing - you’re probably just mucking around - and can sort out
the wrappers yourself.

Since we are discussing the addition of AOP features to the core language, I
don’t think factors of what one would do just “mucking around” are good
criteria.

If you are redefining someone else’s method in production, on your own
head be it. You should be keeping to the same interface of the
original, or else other code is likely to break.

Exactly, and to do this requires understanding the interface. And more times
than not, if you are REDEFINING a method in this manner, your doing it in
totality. Juggling the hooks from before is not condusive. You’ll end up
remove_method’ing them all anyway.

Wrappers don’t depend on the implementation of a method, as you
assert. They depend on the interface. The wrappers should have no
real side-effect anyway, I suspect, except for logging, asserting, …

Of course, I was referreing to the interface of the implementation. Sorry, for
not being more specific.

As for real side effects. What do you mean? That we should only expect them to
be used for certain cases like logging, asserting, …? AOP has much more
potential than this.

It is certainly a curly area, though.

If you think of wraps as sort-of class defineable singletons, that is to say,
singletons that you define literally in your class definitions. It then makes
a whole lot more sense.

-t0

···

On Thursday 27 November 2003 08:22 am, Gavin Sinclair wrote:

Oh, and Gavin,

Happy Thanksgiving

-t0

the “right before this one” kind of previous :wink:

do i post too much?

-t0

···

On Thursday 27 November 2003 10:22 am, Yukihiro Matsumoto wrote:

Hi,

In message “Re: Method wrapping” > > on 03/11/27, “T. Onoma” transami@runbox.com writes:

They remain in place.

I don’t think that’s a desirable behavior. See previous post.

Which one? We have too many “previous” posts here.

Removing hooks on change of a method means all hooks have to
be added again needlessly in some cases. And if a change is
incompatible with the hooks, you can remove or redefine them
yourself once there’s an API to do so. And my guess is it
will be less work to remove hooks manually than to add hooks
manually because if you’re careful most hooks remain valid.
And besides, on large changes to signatures of methods,
you’ll have a lot of work to adapt the rest of the code anyhow.

This sound all good and well however this does not change the
fact that you CANNOT use "old hook methods’’ if the signature
(arity) of the primary method changed - in this case you have
to trash the current hook method stack in one way or the other.

I feel you gave only very weak argument why in this particular
circumstance the removal shouldn’t happen automatically.

/Christoph

···

From: Peter wrote:

I can understand where you’re coming from with this. You want the
functionality there just in case for those “some cases”. But by and large you
won’t want this behiavor (for many reasons), so it really should not be the
default. On the rare occassions one does want it, then there can be means
available to replace the method chain’s “heart” or even any particluar wrap
layer. But doing so should have its own special method, not the other way
around. Otherwise we’re going to end up with a lot of code heavily littered
with:

remove_method(:ameth)
def ameth
end

That’s certainly not the Ruby Way. I’m not sure what kind of method to use for
your case though, perhaps like

selective_def(index, :ameth, *args) do
end

To replace the “heart” you’d use index=0. Hey! Better yet, perhaps we can have
some array like sugar:

def ameth0
end

I think that would work nicely. You?

-t0

···

On Thursday 27 November 2003 02:55 pm, Peter wrote:

Removing hooks on change of a method means all hooks have to be added
again needlessly in some cases. And if a change is incompatible with the
hooks, you can remove or redefine them yourself once there’s an API to do
so. And my guess is it will be less work to remove hooks manually than to
add hooks manually because if you’re careful most hooks remain valid. And
besides, on large changes to signatures of methods, you’ll have a lot of
work to adapt the rest of the code anyhow.

OK, I’ll add my take from the perspective of someone that’s not played
with Ruby that long.

My first reaction to all this wrapper talk was ‘WTF?’, because it
seemed very odd to me. But I think I’ve gotten the idea now.

As I see it, wrapping is for those ‘I want this too’ situations, you
don’t really want to change the original method, but you need a bit of
extra code. Debugging statements and logging of use springs to
mind. The important part being that the extra code doesn’t muck with
the interface.

If you really want to change the how the method works, you do as you
do now, redefining the method itself, possibly renaming the old.

I might be wrong though.

···

On Thu, Nov 27, 2003 at 04:57:02PM +0900, T. Onoma wrote:

On Thursday 27 November 2003 08:22 am, Gavin Sinclair wrote:

If you are redefining someone else’s method in production, on your own
head be it. You should be keeping to the same interface of the
original, or else other code is likely to break.

Exactly, and to do this requires understanding the interface. And more times
than not, if you are REDEFINING a method in this manner, your doing it in
totality. Juggling the hooks from before is not condusive. You’ll end up
remove_method’ing them all anyway.


Thomas
beast@system-tnt.dk

I forgot to add that if you are not redefining, then your adding an additional
wrap, as you do with subclasses. Access to a particular indexed wrap just
won’t be useful except for “mucking around”, as Gavin put it. So if there’s
some tucked away method for doing it, that’s fine. But it shouldn’t be a
“common spec” or part of the def syntax in anyway.

-t0

···

On Thursday 27 November 2003 08:57 am, T. Onoma wrote:

If you are redefining someone else’s method in production, on your own
head be it. You should be keeping to the same interface of the
original, or else other code is likely to break.

Exactly, and to do this requires understanding the interface. And more
times than not, if you are REDEFINING a method in this manner, your doing
it in totality. Juggling the hooks from before is not condusive. You’ll end
up remove_method’ing them all anyway.

Thanks, but there ain’t no thanksgiving down here! :slight_smile:

···

On Thursday, November 27, 2003, 7:03:08 PM, T. wrote:

Oh, and Gavin,

Happy Thanksgiving

Hi,

···

In message “Re: Method wrapping” on 03/11/27, “T. Onoma” transami@runbox.com writes:

Which one? We have too many “previous” posts here.

the “right before this one” kind of previous :wink:

do i post too much?

No. But I prefer identity by X-Mail-Count (for example, I’m replying
to [ruby-talk:86484]).

						matz.