Before, after and around Ruby 1.9

Any chance Ruby 1.9 will have before, after and around method
composition support?

T.

Hi,

In message “Re: before, after and around Ruby 1.9”
on Thu, 6 Sep 2007 03:07:14 +0900, Trans [email protected]
writes:

|Any chance Ruby 1.9 will have before, after and around method
|composition support?

No. Wait for 2.0 for built-in method combination. The vague plan is
making open-class to stack methods on the current ones, unless
explicitly removed, i.e.

class Foo < Object
def foo
puts “Foo#foo (1)”
end
end
class Foo # re-open
def foo
super # calls the first foo
puts “Foo#foo (2)”
end
end

will print

  Foo#foo (1)
  Foo#foo (2)

No alias required.

This works as “around”. And “before” and “after” can be rewritten
using “around”. Note that this is not a fixed idea at all.

          matz.

Hi,

In message “Re: before, after and around Ruby 1.9”
on Thu, 6 Sep 2007 08:41:45 +0900, Lionel B.
[email protected] writes:

|Does it mean that we won’t be able to call the ancestor’s implementation
|when monkey-patching a method?

I am not sure what you mean by monkey-patching. At least you can call
the ancestor’s implementation, when

  • it’s not covered by the class
  • or the method is explicitly removed

|I’m asking because I do this very (ugly?) thing for the Rails 1.1
|PostgreSQL ActiveRecord driver: add_column is buggy with my PostgreSQL
|version and it happens that the AbstractDriver implementation works out
|of the box :slight_smile:

In this case, you just need to remove the covering method, I guess.

          matz.

Yukihiro M. wrote:

|I’m asking because I do this very (ugly?) thing for the Rails 1.1
|PostgreSQL ActiveRecord driver: add_column is buggy with my PostgreSQL
|version and it happens that the AbstractDriver implementation works out
|of the box :slight_smile:

In this case, you just need to remove the covering method, I guess.

So obvious I didn’t saw it :slight_smile: Thanks.

Yukihiro M. wrote:

explicitly removed, i.e.
end
end

will print

  Foo#foo (1)
  Foo#foo (2)

No alias required.

Does it mean that we won’t be able to call the ancestor’s implementation
when monkey-patching a method?

I’m asking because I do this very (ugly?) thing for the Rails 1.1
PostgreSQL ActiveRecord driver: add_column is buggy with my PostgreSQL
version and it happens that the AbstractDriver implementation works out
of the box :slight_smile:

Lionel

Yukihiro M. wrote:

explicitly removed, i.e.
end
end

What if you want to reopen without the “around” semantics? Could we
have these two variations:

class Foo < Foo # <-- This is a type error in 1.8
def foo; super; end # AROUND
end

class Foo
def foo; super; end # REDEFINE
end

At least that is a conservative extension.

Or perhaps some new syntax for the AROUND case…?

Hi,

In message “Re: before, after and around Ruby 1.9”
on Thu, 6 Sep 2007 10:32:03 +0900, Joel VanderWerf
[email protected] writes:

|What if you want to reopen without the “around” semantics?

I thought we need to remove the previous one first.

|Could we have these two variations:
|
|class Foo < Foo # <-- This is a type error in 1.8
| def foo; super; end # AROUND
|end
|
|class Foo
| def foo; super; end # REDEFINE
|end
|
|At least that is a conservative extension.

Or maybe providing two supers, one for the current behavior, the other
for the new. I don’t whatever name is suitable for new super yet,
however.

          matz.

On Sep 5, 2007, at 7:32 PM, Yukihiro M. wrote:

making open-class to stack methods on the current ones, unless
puts “Foo#foo (2)”
This works as “around”. And “before” and “after” can be rewritten
using “around”. Note that this is not a fixed idea at all.

Perhaps to avoid overloading ‘super’, you might use ‘previous’ or
‘existing’ since we would be calling a previously existing version of
the method.

class Foo < Object
def foo
puts “Foo#foo (1)”
end
end
class Foo # re-open
def foo
previous
puts “Foo#foo (2)”
end
end

lass Foo < Object
def foo
puts “Foo#foo (1)”
end
end
class Foo # re-open
def foo
existing
puts “Foo#foo (2)”
end
end

Just a suggestion.

Regards, Morton

On Sep 5, 6:32 pm, Yukihiro M. [email protected] wrote:

explicitly removed, i.e.
end
using “around”. Note that this is not a fixed idea at all.

                                                    matz.

Very interesting. Using ‘super’ in this way lets you easily have
“around”, and as you say, “before” and “after” can be written using
“around” (such as your “after” example.) I like the idea, though it
does confuse/overload ‘super’ a bit

class Foo
def foo
‘foo’
end
end

class Bar < Foo
def foo
super + ‘bar’ # results in ‘foobar’
end
end

class Bar < Foo
def foo
super + ‘baz’ # results in ‘foobarbaz’
end
end

One thing I’m not sure about is what happens with the original method
if the class is re-opened and the new version doesn’t use ‘super’ at
all. I guess it sticks around without anything referring to it. Or
there could be an optimization to actually replace the old method at
that point. The other is if the new version takes different arguments

class Foo
def foo
‘foo’
end
end

class Foo
def foo(caps = true)
if caps
super.upcase
else
super
end
end
end

I’m imagining some aliasing happens behind the scenes, so the new
Foo#foo calls some Foo#old_foo and everything works out without
errors.

As you said, it’s not a fixed idea at all. I was just running through
some of the implications.

On 9/5/07, Rick DeNatale [email protected] wrote:

|end

    foo
   # do something else

end
end

Which I would see as internally aliasing the existing foo, and
resolving reference to fo in the new methods body to the alias.

Or instead of around, how about wrap, with a corresponding unwrap to
back out the wrapper method?

Kind of ugly. I’ll stick with my def thanks. I did like some of the
early code examples/ideas that Matz has put into past presentations.

The above would also make recursive calls quite hard. The bigger
question comes with the combination of two smaller problems: arguments
and recursion. How do we want to treat stacked methods are these like
methods in their own modules or are we going to keep different calling
semantics?

Consider an initial class with a simple method:

class Example
def fib(n)
if n > 1
fib(n - 2) + fib(n - 1)
else
1
end
end
end

Now we reopen the class and add a new fib:

class Example
def fib(n, prefix = 'Calculating fib of ')
puts prefix + n.to_s
super(n)
end
end

So in this example, calling fib(2) on an instance of Example would
yield a simple:

Calculating fib of 2
Calculating fib of 0
Calculating fib of 1

That works well because we can always call super with correct
arguments BUT the recursion in the first method could possibly cause
problems. The first method only knows about its own argument so it
forces the calculation to default after the first fib call. So fib(2,
'Try ') would give the confusing output of

Try 2
Calculating fib of 0
Calculating fib of 1

Not so great. So how would one reconcile this problem? Would recursive
calls directly call the current method? Would we use something like
Erlang’s reloading which differentiates calls that explicitly give a
target (i.e. self in this case) vs ones that make it implicit?

I do think super is an underused feature of Ruby (generally because it
is so easy to alias things in Ruby). I think the idea is wonderful but
there needs to be serious consideration on some of the calling
mechanisms. The above example is contrived. It might be appropriate to
use the same rules as regular non-stacked super methods.

I can’t quite tell what feels right in this case but I think being
able to recurse properly should be a top priority.

Brian.

On 9/5/07, Yukihiro M. [email protected] wrote:

|At least that is a conservative extension.

Or maybe providing two supers, one for the current behavior, the other
for the new. I don’t whatever name is suitable for new super yet,
however

Hi Matz,

super for this new usage just doesn’t feel right to me.

Isn’t this really looking for syntactic sugar for something like:

class Foo #reopen

alias_method :old_foo, :foo

def foo
old_foo #around
end
end

So as to avoid needing to explicitly alias the method and come up with a
name?

Perhaps instead of super in this case something in the vein of old,
or previous.

Alternatively what about instead of def

class Foo
around foo
#do something
foo
# do something else
end
end

Which I would see as internally aliasing the existing foo, and
resolving reference to fo in the new methods body to the alias.

Or instead of around, how about wrap, with a corresponding unwrap to
back out the wrapper method?


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Joel VanderWerf wrote:

What if you want to reopen without the “around” semantics? Could we
have these two variations:

class Foo < Foo # <-- This is a type error in 1.8
def foo; super; end # AROUND
end

class Foo
def foo; super; end # REDEFINE
end

+1

This is brilliant. It’s simple, and it perfectly expresses the idea of
reopening the class to add functionality on top of the existing methods.
Instead of subclassing a parent class, Foo subclasses itself (sort of
like Foo=Class.new(Foo)). Just Brilliant.

Daniel

On Sep 5, 4:32 pm, Yukihiro M. [email protected] wrote:

explicitly removed, i.e.
end
using “around”. Note that this is not a fixed idea at all.
After my very own heart! I think that’s a great approach. And I will
try to be patient for 2.0 :wink:

T.

On Sep 5, 6:32 pm, Joel VanderWerf [email protected] wrote:

making open-class to stack methods on the current ones, unless
puts “Foo#foo (2)”
end
end

What if you want to reopen without the “around” semantics? Could we
have these two variations:

class Foo < Foo # <-- This is a type error in 1.8
def foo; super; end # AROUND
end

Almost an anonymous cut.

class Foo
def foo; super; end # REDEFINE
end

At least that is a conservative extension.

The downside here is it’s static syntax --making it harder to work
with in dynamic metacode.

T.

On Sep 5, 6:46 pm, Yukihiro M. [email protected] wrote:

|
Or maybe providing two supers, one for the current behavior, the other
for the new. I don’t whatever name is suitable for new super yet,
however.

How would you be sure (and why would you want to?) know when a
“previous” exists or not? It rarely make sense to avoid advice.
However, when absolutely needed one can fallback to directed calling
with things like:

super_at(Object)

or

as(Object).foo

T.

Hi,

In message “Re: before, after and around Ruby 1.9”
on Thu, 6 Sep 2007 13:12:53 +0900, Morton G.
[email protected] writes:

|Perhaps to avoid overloading ‘super’, you might use ‘previous’ or
|‘existing’ since we would be calling a previously existing version of
|the method.

Interesting idea, especially knowing Common Lisp Object System uses
call-next-method instead of super.

          matz.

On 6 Sep 2007, at 06:17, Daniel DeLorme wrote:

+1

This is brilliant. It’s simple, and it perfectly expresses the idea
of reopening the class to add functionality on top of the existing
methods. Instead of subclassing a parent class, Foo subclasses
itself (sort of like Foo=Class.new(Foo)). Just Brilliant.

:slight_smile: I think I’m probably missing something, but I’m not sure that I’m
keen on this approach though I find the simplicity alluring.

The issue I have is what happens when the class is re-opened more
than once: when lots of methods get wrapped around? As far as I can
see, the order of the openings in the code is important, and I don’t
like the feel of that at all. I feel like I ought to be able to
reorder things such as the order of class definition without that
having a semantic effect. In fact, I think I find it confusing
already that if I define the same method more than once for the same
class, then the last definition will be the one that’s used.

Am I missing something? Does anyone else feel similarly? Is the
consensus that this wouldn’t be an issue in practice?

I suppose I’d prefer something that would somewhat break away from
what happens now…

I’d have all of the implementations of a method (within the class) be
called. I’d then have another mechanism for enforcing ordering: a
syntax that lets an implementation insist that it requires another to
be run before it (or after it). Once you’ve insisted that another
implementation runs before you, then you’re able to utilise the
return state of that method you asked to run before you.

Then again, what I’m asking for could easily be done with a library,
so perhaps it’s not so important :slight_smile:

Cheers,
Benjohn

On 9/6/07, Trans [email protected] wrote:

I thought we need to remove the previous one first.
|

super_at(Object)
Inheritance dependecies, oooouch, danger, hot do not touch!
Mixin dependecies even worse imagine you want to refer to a mixed in
method, maybe it was mixed in by an anonymous module, or the method
was defined at instance level.

After all it feels wrong to have around, before and after built into
the language, this seems to be a job for the metaprogrammers not for
Matz.
I’d prefer having an easy way to do this with metaprogramming and then
put the implementations into the library.

if there were a method object tree rather than a stack and if each
method object just had references to the defining Module/Singleton
Module, would we not be very happy?

module A
def a; 222 end
end
module A
def a; 132 end
end
class B
include A
def a; 110 end
end
class C < B
def a; 60 end
end

class C
include Module::new{ def a ; 52 end }
end

c = C.new
ma = c.method :a
ma = ma.unbind
ma.bind©.call => 52
ma.old.bind©.call => 60
ma.super.bind©.call => 110
and most importantly

D= Class::new {
define_method :a { -3 + ma.bind(self).call - 3 }
}
D.instance_method(:a).old == ma => true

Cheers
Robert

On Sep 6, 11:54 am, “Robert D.” [email protected] wrote:

|What if you want to reopen without the “around” semantics?
| def foo; super; end # REDEFINE
However, when absolutely needed one can fallback to directed calling
with things like:

super_at(Object)

Inheritance dependecies, oooouch, danger, hot do not touch!

Well, that was kind of my point. As often as this is useful, so is
skipping over around advice.

Mixin dependecies even worse imagine you want to refer to a mixed in
method, maybe it was mixed in by an anonymous module, or the method
was defined at instance level.

After all it feels wrong to have around, before and after built into
the language, this seems to be a job for the metaprogrammers not for
Matz.

Why exactly? Ruby has many meta-programming features built-in. That’s
part of it’s charm and power.

I’d prefer having an easy way to do this with metaprogramming and then
put the implementations into the library.

There are two problems with that. 1) any implementation one can create
as an add-on is going to have a less intuitive interface, and 2) but
much more importantly, any such implementation is going to be severely
hampered in execution efficiency compared to a native capability.

T.

Ahh the emotion go the better of me :wink:
D= Class::new© {
define_method :a { -3 + ma.bind(self).call - 3 }
}
D.instance_method(:a).super == ma => true

Sorry for the ugly error

Cheers
Robert

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs