How I'd like method-wrapping to work

OK, I read Matz’s blog entries as well as I could.

One thing that occurs to me is that I don’t really
want to distinguish between “before” and "after"
in an artificial way.

Here’s my tentative dream of how I’d like to do
this kind of thing in Ruby. (I’m using the keyword
’prior’ for lack of anything better right now.)

class String

define_hook(:length) do
puts "I’m calculating the length"
prior
end

define_hook(:upcase) do
val = prior
puts "I just did an upcase operation"
val
end

define_hook(:reverse) do
puts "I’m about to do a reverse operation"
val = prior
puts "I just did a reverse"
val
end

end

str = “abcde”

x = str.length
puts x

y = str.upcase
puts y

z = str.reverse
puts z

Output:

I’m calculating the length

5

I just did an upcase operation

ABCDE

I’m about to do a reverse operation

I just did a reverse

edcba

Notes:

  1. Obviously parameter-passing needs to be addressed.
  2. It would be nice to get around the return-value
    problem (where have I seen this before? ‘ensure’?)
  3. I’ve probably overlooked a lot of issues. Please
    educate me (Matz, Guy, Dave, David, others…)

“)”.intern

Hal

“)”.intern

def lol!
“-D”.intern
end

Bill

···

From: “Hal E. Fulton” hal9000@hypermetrics.com

In article 096701c31fc3$4f78b3a0$0300a8c0@austin.rr.com,

···

Hal E. Fulton hal9000@hypermetrics.com wrote:

OK, I read Matz’s blog entries as well as I could.

Where are Matz’s blog?

Phil

“Hal E. Fulton” hal9000@hypermetrics.com schrieb im Newsbeitrag
news:096701c31fc3$4f78b3a0$0300a8c0@austin.rr.com

OK, I read Matz’s blog entries as well as I could.

One thing that occurs to me is that I don’t really
want to distinguish between “before” and “after”
in an artificial way.

Here’s my tentative dream of how I’d like to do
this kind of thing in Ruby. (I’m using the keyword
‘prior’ for lack of anything better right now.)

class String

define_hook(:length) do
puts “I’m calculating the length”
prior
end

define_hook(:upcase) do
val = prior
puts “I just did an upcase operation”
val
end

define_hook(:reverse) do
puts “I’m about to do a reverse operation”
val = prior
puts “I just did a reverse”
val
end

end

str = “abcde”

x = str.length
puts x

y = str.upcase
puts y

z = str.reverse
puts z

Output:

I’m calculating the length

5

I just did an upcase operation

ABCDE

I’m about to do a reverse operation

I just did a reverse

edcba

That’s no fantasy. Dreams come true…

Notes:

  1. Obviously parameter-passing needs to be addressed.
  2. It would be nice to get around the return-value
    problem (where have I seen this before? ‘ensure’?)
  3. I’ve probably overlooked a lot of issues. Please
    educate me (Matz, Guy, Dave, David, others…)

Although not listed this bugged me. See what I came up with (attached).

Cons:

  • ‘this’ had to be introduced since I found no way to
    rebind the proc (even if there were a way we had
    to deal with multiple concurrent invocations). The
    deeper reason for this problem is, that the block
    comes into existence in class context but should be
    evaluated in instance context later.

  • Argument lists have to be doubled for the block

Pros:

  • It nearly looks like what you asked for.
  • Return values work reasonably.
  • It even works for nested invocations, i.e. recursion
  • It works with several instances in the call chain
  • You can exactly do what you put in

Regards

robert

hook_test.rb (442 Bytes)

hooks.rb (1.35 KB)

this kind of thing in Ruby. (I'm using the keyword
'prior' for lack of anything better right now.)

gsub(/\wprior\w/, "next_mwrapper")

See [ruby-talk:53186]

I've *NEVER* liked it

Guy Decoux

“Hal E. Fulton” hal9000@hypermetrics.com writes:

define_hook(:length) do
puts “I’m calculating the length”
prior
end

[ …etc… ]

I’d rather see something like this:

def foometh(arg)
puts “I got #{arg}”
end

foometh.before = proc { |arg| puts “This is before, with #{arg}” }
foometh.around = proc { |arg, this|
puts “This is before, with #{arg}”
this.call(arg)
puts “This is after, with #{arg}”
end
foometh.after = proc { |arg| puts “This is after, with #{arg}” }

That way advice is just an attribute of a method that can be
introspected like anything else, instead of invisible, magical
attributes of a method. This is especially important with advice,
because advised methods/functions can cause a program to behave in
ways the casual reader wouldn’t expect. Not being able to query a
method about its advice from e.g. irb would make problems with advised
methods really hard to debug.

The “before”, “around”, etc. accessors to foometh should probably be
arrays, so that there can be multiple pieces of advice.

Of course, the above is utterly invalid Ruby, because functions are
not first-class objects, and is thus even more of a fantasy than your
example ;). But a guy can dream.

It would also be nice if Ruby’s methods implicitly defined ‘this’ to
point to the current method; otherwise as in the above example it has
to be added as an additional argument to the “around” advice.

Dan

···


/^Dan Debertin$/ |
airboss@nodewarrior.org |
www.nodewarrior.org |

In article 096701c31fc3$4f78b3a0$0300a8c0@austin.rr.com,

OK, I read Matz’s blog entries as well as I could.

Where are Matz’s blog?

He gave the links about 15-20 messages ago.
It’s in Japanese, but I ran it through
Babelfish and it was semi-comprehensible.

Hal

···

----- Original Message -----
From: “Phil Tomson” ptkwt@shell1.aracnet.com
Newsgroups: comp.lang.ruby
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, May 21, 2003 2:38 PM
Subject: Re: How I’d like method-wrapping to work…

Hal E. Fulton hal9000@hypermetrics.com wrote:

That’s no fantasy. Dreams come true…

The plane, the plane! (Sorry, showing my age…)
(For non-Americans and/or people under 35: A reference
to the TV show Fantasy Island.)

Thanks for this fascinating reply…

  1. I’ve probably overlooked a lot of issues. Please
    educate me (Matz, Guy, Dave, David, others…)

Although not listed this bugged me. See what I came up with (attached).

Not listed? :slight_smile:

others.include?(Robert) # true

Cons:

  • ‘this’ had to be introduced since I found no way to
    rebind the proc (even if there were a way we had
    to deal with multiple concurrent invocations). The
    deeper reason for this problem is, that the block
    comes into existence in class context but should be
    evaluated in instance context later.

I was having a hard time with that, too, and I wasn’t
even worrying about threads.

  • Argument lists have to be doubled for the block

Hmm, not sure what you mean here. But I am going to
play with this a little.

Pros:

  • It nearly looks like what you asked for.
  • Return values work reasonably.

By “reasonably” do you mean that the return value of
prior will be used even if that call is not the last
one inside the hook block? Ideally we shouldn’t have
to do the “val” stuff that I do. I’ll test.

  • It even works for nested invocations, i.e. recursion
  • It works with several instances in the call chain

This is essential if I understand you rightly. It’s also
one reason we need this – methods are sometimes aliased
more than once, e.g., in a library and in your own code.

  • You can exactly do what you put in

So I can! I see that the code in hook_test is line for line
the same as mine.

However, on 1.7.3 on Windows I got a syntax error on
line 40 of hooks.rb (had to remove the “proc &”). Not
sure what the issue is here – assume it worked for you
that way?

Thanks for a quick reply… this is another bit of code
that I’ll certainly save.

I do hope the discussion continues, though…

Cheers,
Hal

···

----- Original Message -----
From: “Robert” bob.news@gmx.net
Newsgroups: comp.lang.ruby
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, May 21, 2003 5:39 PM
Subject: Re: How I’d like method-wrapping to work…

gsub(/\wprior\w/, "next_mwrapper")

Well, it was (Thanks David Alan Black)

  gsub(/\bprior\b/, "next_mwrapper")

regexp are stupid, this is one more proof :-)))

Guy Decoux

You mean, you do not like the idea of a
sequence of wrapped methods?

Can you explain a little? (I know English
is difficult for you, but not as difficult
as my French, which is nonexistent.)

Thanks,
Hal

···

----- Original Message -----
From: “ts” decoux@moulon.inra.fr
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Cc: ruby-talk@ruby-lang.org
Sent: Thursday, May 22, 2003 5:06 AM
Subject: Re: How I’d like method-wrapping to work…

this kind of thing in Ruby. (I’m using the keyword
‘prior’ for lack of anything better right now.)

gsub(/\wprior\w/, “next_mwrapper”)

See [ruby-talk:53186]

I’ve NEVER liked it

foometh.before = proc { |arg| puts "This is before, with #{arg}" }

You can have more than an advice for a method, how do you do in this
case ?

Guy Decoux

“Hal E. Fulton” hal9000@hypermetrics.com schrieb im Newsbeitrag
news:0d1f01c31fed$2b5978e0$0300a8c0@austin.rr.com

From: “Robert” bob.news@gmx.net
Newsgroups: comp.lang.ruby
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, May 21, 2003 5:39 PM
Subject: Re: How I’d like method-wrapping to work…

That’s no fantasy. Dreams come true…

The plane, the plane! (Sorry, showing my age…)
(For non-Americans and/or people under 35: A reference
to the TV show Fantasy Island.)

I just know a hit by, I think it was “Tight Fit” with the same name. The
80’s…

Thanks for this fascinating reply…

You’re welcome! I’m always amazed what can be done with Ruby, it’s really
a jewel. Sometimes it’s even scaring…

  1. I’ve probably overlooked a lot of issues. Please
    educate me (Matz, Guy, Dave, David, others…)

Although not listed this bugged me. See what I came up with
(attached).

Not listed? :slight_smile:

others.include?(Robert) # true

Oooops, my implementation of include? seems to be broken. Thank’s for the
fix. :slight_smile:

Cons:

  • ‘this’ had to be introduced since I found no way to
    rebind the proc (even if there were a way we had
    to deal with multiple concurrent invocations). The
    deeper reason for this problem is, that the block
    comes into existence in class context but should be
    evaluated in instance context later.

I was having a hard time with that, too, and I wasn’t
even worrying about threads.

That’s the single biggest problem for the hook approach with blocks. If
that could be solved, maybe by a method Proc#rebind( binding ) => Proc
returning a new proc with the given binding we had the perfect solution.
I think there were discussions about rebinding somethread else.

  • Argument lists have to be doubled for the block

Hmm, not sure what you mean here. But I am going to
play with this a little.

If you want to access method arguments in the block you have to include
them like I did in the second test script (attached).

Pros:

  • It nearly looks like what you asked for.
  • Return values work reasonably.

By “reasonably” do you mean that the return value of
prior will be used even if that call is not the last
one inside the hook block? Ideally we shouldn’t have
to do the “val” stuff that I do. I’ll test.

No, I meant, that you can decide whether you want to propagate them or
override them. But it can be fixed to always return the original. Lemme
see… Done. If I find some time I might even include a test for
duplicate prior calls from the same block. Oops, that was easy. I’ll
have to stop that… Please don’t come up with further feature requests.
:-))

  • It even works for nested invocations, i.e. recursion
  • It works with several instances in the call chain

This is essential if I understand you rightly. It’s also
one reason we need this – methods are sometimes aliased
more than once, e.g., in a library and in your own code.

Yep, that was what I suspected was the typical case. You don’t want to
worry whether someone else hooked a method already.

  • You can exactly do what you put in

So I can! I see that the code in hook_test is line for line
the same as mine.

Si!

However, on 1.7.3 on Windows I got a syntax error on
line 40 of hooks.rb (had to remove the “proc &”). Not
sure what the issue is here – assume it worked for you
that way?

Strange, I got no error with 1.7.3 on Win. Need to investigate this. The
version I have here at work complains, too.

Thanks for a quick reply…

Oh, I took some detours in between… (I dropped a version with a String
instead a block.)

this is another bit of code
that I’ll certainly save.

That’s very kind of you.

I do hope the discussion continues, though…

Of course! There’s always room for improvement (see attached :-))

Kind regards

robert

hooks.rb (1.62 KB)

another_test.rb (733 Bytes)

hook_test.rb (414 Bytes)

···

----- Original Message -----

You mean, you do not like the idea of a
sequence of wrapped methods?

Well, I've had the strange impression that something more general, more
usefull can be build but because I'm stupid I've never found it.

Guy Decoux

ts decoux@moulon.inra.fr writes:

foometh.before = proc { |arg| puts “This is before, with #{arg}” }

You can have more than an advice for a method, how do you do in this
case ?

I didn’t want to complicate the example too much. I noted further down
(for that exact reason) that those accessors should probably be arrays, and
therefore defined like this:

foometh.before.push(proc { |arg| puts “This is before, with #{arg}” })

Dan

···


/^Dan Debertin$/ |
airboss@nodewarrior.org |
www.nodewarrior.org |

I think it is at least more general than the
:before and :after stuff. But maybe there is
a reason Matz likes the other way better.

As for your stupidity… I hope to reach
your same level of stupidity someday. :wink:

Hal

···

----- Original Message -----
From: “ts” decoux@moulon.inra.fr
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Cc: ruby-talk@ruby-lang.org
Sent: Thursday, May 22, 2003 9:15 AM
Subject: Re: How I’d like method-wrapping to work…

You mean, you do not like the idea of a
sequence of wrapped methods?

Well, I’ve had the strange impression that something more general, more
usefull can be build but because I’m stupid I’ve never found it.

Hi,

···

In message “Re: How I’d like method-wrapping to work…” on 03/05/22, “Hal E. Fulton” hal9000@hypermetrics.com writes:

I think it is at least more general than the
:before and :after stuff. But maybe there is
a reason Matz likes the other way better.

It’s like multiple inheritance vs mix-in. Sometimes restricted one
behave better than more general one.

						matz.