Proposal: call_up() for use in redefined methods

Hi all,

A common ruby idiom for redefining a method in a class is to alias it
to give it a new name before defining the replacement:

allow integer indices to wrap.

“test”[6] #=>“e”

class String
alias :old_brackets :[]
def [](idx, num=nil)
idx = ((idx % size) + idx) % size if idx.kind_of? Fixnum
num ? old_brackets(idx, num) : old_brackets(idx)
end
end

However, this breaks if it is done a second time, with the same alias:

allow ranges to wrap around

“abcd”[-2…1] #=>“cdab”

class String
alias :old_brackets :[]
def [](idx, num=nil)
if Range === idx and idx.first < 0 and idx.end > 0
end_range = idx.exclude_end?? (0…idx.end) : (0…idx.end)
self[idx.first…-1] + self[end_range]
else
num ? old_brackets(idx, num) : old_brackets(idx)
end
end
end

then in irb:

irb(main):004:0>>> “abcd”[3]
(eval):4:in old_brackets': stack level too deep (SystemStackError) from (eval):5:inold_brackets’
from (eval):5:in `old_brackets’

This can be a problem. If those two definitions occurred in two
separate libraries, then the maintainers of both libraries would have
to get together (once the problem is identified) and modify their code
just so the libraries can be used together. It could discourage people
from using what is (IMHO) one of Ruby’s greatest strengths… the
ability to easily redefine methods.

One solution comes from the common OO method/construct: super().
Calling super() lets you call the parent class’ version of a particular
function; it’s smart, and knows to call the same function that you are
currently in. Since most OO languages (that I’ve worked with) have
non-redefinable classes, super() is enough. But when you redefine a
method in Ruby, there’s no way of getting to the previously defined
method. I propose a new method/construct, tentatively named call_up(),
which would give access to the older versions of a method; and a
companion method redefine() which stores the method in a stack before
undefing it.

Here’s a partial example of a hacked solution. Additional coding would
be needed to support the same thing in modules and singleton classes, I
but it shows a rough idea of the way it would work.

class Object
def redefine(sym)
if implements? sym
@@__redefined_methods[sym] ||= []
@@__redefined_methods[sym].push method(sym)
undef sym
else raise ArgumentError, "not a valid method"
end
end

 def call_up(*args)
   # Deep Magic Goes Here:
   # - detects the calling method
   # - detects if it is in the __redefined_methods stack
   # - determines which method version was defined immediately 

previous
# - calls that method with *args
end
end

Each class (and singleton object) would keep track of the explicitly
redefined methods, and allow a super()ish way of calling the next
method up. This could allow the above method redefinition code to look
like this:

allow integer indices to wrap.

“test”[6] #=>“e”

class String
redefine :[]
def [](idx, num=nil)
idx = ((idx % size) + idx) % size if idx.kind_of? Fixnum
num ? call_up(idx, num) : call_up(idx)
end
end

allow ranges to wrap around

“abcd”[-2…1] #=>“cdab”

class String
redefine :[]
def [](idx, num=nil)
if Range === idx and idx.first < 0 and idx.end >= 0
end_range = idx.exclude_end?? (0…idx.end) : (0…idx.end)
self[idx.first…-1] + self[end_range]
else
num ? call_up(idx, num) : call_up(idx)
end
end
end

If we could do this, it would eliminate any worries about conflicting
method names, and would free Ruby programmers to redefine methods
whenever it makes sense. I don’t think it would introduce any real
added complexity, and it would standardize how to redefine a method and
call the original.

For an even more real-world example, consider all the different numeric
types defined in the stdlib math libraries. If you want to create a new
Numeric, and want Math.sqrt() to support it, you have to redefine
Math.sqrt. But if it just happens that someone else did the same thing,
in the same way, then everything will go horribly wrong the moment you
use Math.sqrt() after require()ing both files.

Comments? Questions? Flames? :slight_smile:

–Mark

Mark Hubbart wrote:

[snip]

Comments? Questions? Flames? :slight_smile:

I think this is essentially the same as my suggestion in
http://ruby-talk.org/38136

The answer then was basically: Something like this will come
later.

In fact, I believe that Matz’s plans for pre/post/wrap would make
this sort of thing easy (and far more generalized).

Unless I’ve misunderstood.

Cheers,
Hal

Hi,

FYI, Perl 6’s Apocalypse 12 describes a mechanism that seems similar to
yours. You may want
to have a look at it. I think call_up() is “next” or something.

Yours,

Jean-Hugues Robert

···

At 07:33 20/04/2004 +0900, you wrote:

Hi all,

A common ruby idiom for redefining a method in a class is to alias it to
give it a new name before defining the replacement:

allow integer indices to wrap.

“test”[6] #=>“e”

class String
alias :old_brackets :
def (idx, num=nil)
idx = ((idx % size) + idx) % size if idx.kind_of? Fixnum
num ? old_brackets(idx, num) : old_brackets(idx)
end
end

However, this breaks if it is done a second time, with the same alias:

allow ranges to wrap around

“abcd”[-2…1] #=>“cdab”

class String
alias :old_brackets :
def (idx, num=nil)
if Range === idx and idx.first < 0 and idx.end > 0
end_range = idx.exclude_end?? (0…idx.end) : (0…idx.end)
self[idx.first…-1] + self[end_range]
else
num ? old_brackets(idx, num) : old_brackets(idx)
end
end
end

then in irb:

irb(main):004:0>>> “abcd”[3]
(eval):4:in old_brackets': stack level too deep (SystemStackError) from (eval):5:in old_brackets’
from (eval):5:in `old_brackets’

This can be a problem. If those two definitions occurred in two separate
libraries, then the maintainers of both libraries would have to get
together (once the problem is identified) and modify their code just so
the libraries can be used together. It could discourage people from using
what is (IMHO) one of Ruby’s greatest strengths… the ability to easily
redefine methods.

One solution comes from the common OO method/construct: super(). Calling
super() lets you call the parent class’ version of a particular function;
it’s smart, and knows to call the same function that you are currently in.
Since most OO languages (that I’ve worked with) have non-redefinable
classes, super() is enough. But when you redefine a method in Ruby,
there’s no way of getting to the previously defined method. I propose a
new method/construct, tentatively named call_up(), which would give access
to the older versions of a method; and a companion method redefine() which
stores the method in a stack before undefing it.

Here’s a partial example of a hacked solution. Additional coding would be
needed to support the same thing in modules and singleton classes, I but
it shows a rough idea of the way it would work.

class Object
def redefine(sym)
if implements? sym
@@__redefined_methods[sym] ||=
@@__redefined_methods[sym].push method(sym)
undef sym
else raise ArgumentError, “not a valid method”
end
end

def call_up(*args)
  # Deep Magic Goes Here:
  # - detects the calling method
  # - detects if it is in the __redefined_methods stack
  # - determines which method version was defined immediately previous
  # - calls that method with *args
end

end

Each class (and singleton object) would keep track of the explicitly
redefined methods, and allow a super()ish way of calling the next method
up. This could allow the above method redefinition code to look like this:

allow integer indices to wrap.

“test”[6] #=>“e”

class String
redefine :
def (idx, num=nil)
idx = ((idx % size) + idx) % size if idx.kind_of? Fixnum
num ? call_up(idx, num) : call_up(idx)
end
end

allow ranges to wrap around

“abcd”[-2…1] #=>“cdab”

class String
redefine :
def (idx, num=nil)
if Range === idx and idx.first < 0 and idx.end >= 0
end_range = idx.exclude_end?? (0…idx.end) : (0…idx.end)
self[idx.first…-1] + self[end_range]
else
num ? call_up(idx, num) : call_up(idx)
end
end
end

If we could do this, it would eliminate any worries about conflicting
method names, and would free Ruby programmers to redefine methods whenever
it makes sense. I don’t think it would introduce any real added
complexity, and it would standardize how to redefine a method and call the
original.

For an even more real-world example, consider all the different numeric
types defined in the stdlib math libraries. If you want to create a new
Numeric, and want Math.sqrt() to support it, you have to redefine
Math.sqrt. But if it just happens that someone else did the same thing, in
the same way, then everything will go horribly wrong the moment you use
Math.sqrt() after require()ing both files.

Comments? Questions? Flames? :slight_smile:

–Mark


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

Mark Hubbart wrote:

[snip]

Comments? Questions? Flames? :slight_smile:

I think this is essentially the same as my suggestion in
http://ruby-talk.org/38136

Damn! you’re right - and I like “prior” better, too :slight_smile: I should’ve
googled it a little better, might have turned that up.

The answer then was basically: Something like this will come
later.

In fact, I believe that Matz’s plans for pre/post/wrap would make
this sort of thing easy (and far more generalized).

hmmm… from what I’ve read, it appears the hooks are just methods, and
they can be redefined. So if you re-open a class and define foo:wrap,
and foo was already wrapped, it looks like the new wrapper will replace
the old one, rather than wrapping the old wrapper.

The beauty of the “prior” method is that you can add support for other
object types easily, by redefining the method. So if you are writing
libraries for HyperNumerics and PsuedoNumerics, you just go:

file hypernumeric.rb

module Math
redefine :sqrt
def sqrt(obj)
if obj.kind_of? HyperNumeric
# handle hypernumeric numbers
else prior(obj)
end
end
end

file psuedonumeric.rb

module Math
redefine :sqrt
def sqrt(obj)
if obj.kind_of? PsuedoNumeric
# handle psuedonumeric numbers
else prior(obj)
end
end
end

no method entanglement, no namespace clutter… And it doesn’t matter
which one gets defined first, since it just grabs the argument if it
can handle it; otherwise it passes it on.

Unless I’ve misunderstood.

Or me :slight_smile: I might easily have misunderstood how the pre/post/wrap thing
will work… I’m sorta unsure of it, the more I think about it…

–Mark

···

On Apr 19, 2004, at 4:04 PM, Hal Fulton wrote:

and is super() in rubyland (2.0)

···

il Tue, 20 Apr 2004 17:03:30 +0900, Jean-Hugues ROBERT jean_hugues_robert@yahoo.com ha scritto::

Hi,

FYI, Perl 6’s Apocalypse 12 describes a mechanism that seems similar to
yours. You may want
to have a look at it. I think call_up() is “next” or something.

Yours,

Mark Hubbart wrote:

[snip]

Comments? Questions? Flames? :slight_smile:

I think this is essentially the same as my suggestion in
http://ruby-talk.org/38136

It seems nobody saw this posting of mine:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/97440

That code allows you to do

class A
def foo(*a)
puts “A#foo #{a.inspect}”
end
end
a = A.new
a.foo

class A
def_advice(:foo) do |prior, *a|
puts “before”
prior[*a]
puts “after”
end
end
a.foo

i.e. the previous definition is available as prior (or whatever name
you care to give it :). The most important drawback is that it is
still impossible to propagate blocks that way (until matz implements
{|&block|…}).

The answer then was basically: Something like this will come
later.

In fact, I believe that Matz’s plans for pre/post/wrap would make
this sort of thing easy (and far more generalized).

matz’ advices will be more powerful, see the references in [97440]
for more info.

···

On Tue, Apr 20, 2004 at 08:04:54AM +0900, Hal Fulton wrote:


Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

‘Ooohh… “FreeBSD is faster over loopback, when compared to Linux
over the wire”. Film at 11.’
– Linus Torvalds

Mauricio Fernández wrote:

···

On Tue, Apr 20, 2004 at 08:04:54AM +0900, Hal Fulton wrote:

Mark Hubbart wrote:

[snip]

Comments? Questions? Flames? :slight_smile:

I think this is essentially the same as my suggestion in
http://ruby-talk.org/38136

It seems nobody saw this posting of mine:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/97440

I had forgot about this. It’s a good idea, but I will probably
just wait for 2.0.

Hal

Hi –

gabriele renzi surrender_it@remove.yahoo.it writes:

···

il Tue, 20 Apr 2004 17:03:30 +0900, Jean-Hugues ROBERT > jean_hugues_robert@yahoo.com ha scritto::

Hi,

FYI, Perl 6’s Apocalypse 12 describes a mechanism that seems similar to
yours. You may want
to have a look at it. I think call_up() is “next” or something.

Yours,

and is super() in rubyland (2.0)

That would be strange. Are you sure about that?

David


David A. Black
dblack@wobblini.net

maybe I misunderstood something?

the thing the OP requested looks to me the method
decoration/defadvice-ish thing that we’ll see in ruby2, and from the
rubyconf 2k3 it seem that the syntax would be:

def foo:wrap
blabla
super
blablabla
end

that makes sense cause a redefinition in a subclass may be seen just
like a decoration of the first definition.

Next in perl6 should work somehow like this in that it allows method
combination calling a method from another, and even if probably it
does more things (my head was spinning at that point of apocalypse12)
it gives, imo, similar functionality.

···

il 20 Apr 2004 15:13:04 -0700, David Alan Black dblack@wobblini.net ha scritto::

FYI, Perl 6’s Apocalypse 12 describes a mechanism that seems similar to
yours. You may want
to have a look at it. I think call_up() is “next” or something.

Yours,

and is super() in rubyland (2.0)

That would be strange. Are you sure about that?

wxRuby 0.3.0 has been released and is now available for download from
RubyForge at:

http://wxruby.rubyforge.org/

This release includes binary builds for Max OS X Panther and MS Windows.
Hopefully, within a couple weeks I will release RubyGems for the source, and
RubyGem binaries for Mac, Windows, and Linux.

Please report any bugs or feature requests here:

http://rubyforge.org/tracker/?group_id=35

Changes in this release include:

  • Added MDI support, and an MDI sample
  • Fixed MAJOR problems with Validators.
  • Cleaned up many method names and parameters.
  • Many bug fixes.
  • Added a note to controls/controls.rb sample
    explaining that the current get/set_client_data
    can segfault

See the ChangeLog for a detailed list of changes.

···

What is wxRuby?

wxRuby is an open source GUI toolkit for the [Ruby] programming
language. It provides a Ruby interface to the cross-platform
wxWidgets C++ GUI framework (formerly known as wxWindows).
wxRuby is intended to dramatically extend Ruby’s usefulness in
rapid prototyping and general UI development.

wxWidgets is a mature, cross-platform GUI toolkit that uses native
GUI widgets. This means that wxRuby applications can integrate
seamlessly on each supported platform, and can behave like the
user expects.

wxRuby is still in early beta. The adventurous can get the latest
source code from the cvs repository, including nightly snapshots.
wxRuby seems to work pretty well for a variety of test applications.

Curt

Hi –

gabriele renzi surrender_it@remove.yahoo.it writes:

FYI, Perl 6’s Apocalypse 12 describes a mechanism that seems similar to
yours. You may want
to have a look at it. I think call_up() is “next” or something.

Yours,

and is super() in rubyland (2.0)

That would be strange. Are you sure about that?

maybe I misunderstood something?

the thing the OP requested looks to me the method
decoration/defadvice-ish thing that we’ll see in ruby2, and from the
rubyconf 2k3 it seem that the syntax would be:

def foo:wrap
blabla
super
blablabla
end

that makes sense cause a redefinition in a subclass may be seen just
like a decoration of the first definition.

Yes, I forgot that ‘super’ was being used for that. But I’m not sure
this corresponds exactly to what the OP wanted (a way to access
aliased methods, which is what I thought you meant here). But maybe
it can be used in some of the same situations.

For what it’s worth, I don’t agree that it’s very similar to
redefinition in a subclass. I would actually find it hard to find a
unified way to explain both 'super’s to someone, and I’m not sure why
the same word is being reused. I guess ‘wrapee’ is a bit too bizarre
:slight_smile: But I do wish it were something other than super, since the
wrapped method is not ‘above’ the wrapper (the way a superclass’s
method is above the subclass’s method).

David

···

il 20 Apr 2004 15:13:04 -0700, David Alan Black dblack@wobblini.net > ha scritto::


David A. Black
dblack@wobblini.net

Hello Curt,

Wednesday, April 21, 2004, 12:41:52 AM, you wrote:

I get the following error from the splitter sample

Z:\work\arachno\make>"C:\Program Files\Arachno Ruby IDE\ruby\samples\wxRuby-0.3.0\splitter\splitter.
rb"
C:/Program Files/Arachno Ruby IDE/ruby/samples/wxRuby-0.3.0/splitter/splitter.rb:257:in `main_loop':
undefined method `on_draw' for #<MyCanvas:0x2a54830> (NoMethodError)
        from C:/Program Files/Arachno Ruby IDE/ruby/samples/wxRuby-0.3.0/splitter/splitter.rb:257

All other samples seem to work fine.

···

--
Best regards,
Lothar mailto:mailinglists@scriptolutions.com

Okay, maybe someone could clear this up… how exactly will the
defadvice stuff work? once you wrap a method, is the wrapper permanent?
For example, can you:

class Foo
def foo
print “foo”
end
def foo:wrap
print “:”; super; print “:”
end
end

then, later reopen the class and:

class Foo
def foo:wrap
print “.”; super; print “.”
end
end

…and expect that calling Foo.new.foo will print “.:foo:.”? If so,
that would solve the problem. If not, it would (obviously) allow only
one redefinition per method.

I see the ability to call “prior” (or whatever it would be called) as a
solution to the multiple redefinition problem… I first thought of it
while browsing through the math libraries, and seeing what was done to
get new Numerics to work with other libraries. They all had to be
careful not to step on each others feet.

cheers,
–Mark

···

On Apr 20, 2004, at 3:34 PM, gabriele renzi wrote:

the thing the OP requested looks to me the method
decoration/defadvice-ish thing that we’ll see in ruby2, and from the
rubyconf 2k3 it seem that the syntax would be:

def foo:wrap
blabla
super
blablabla
end

that makes sense cause a redefinition in a subclass may be seen just
like a decoration of the first definition.

Hmm, I installed the Mac OS X version, but I can’t seem to find where it
put it. It’s not in my local install of ruby, nor in /sw nor in
/usr/local.

···

On Wednesday, 21 April 2004 at 7:41:52 +0900, Curt Hibbs wrote:

wxRuby 0.3.0 has been released and is now available for download from
RubyForge at:

http://wxruby.rubyforge.org/

This release includes binary builds for Max OS X Panther and MS Windows.
Hopefully, within a couple weeks I will release RubyGems for the source, and
RubyGem binaries for Mac, Windows, and Linux.


Jim Freeze
This is an unauthorized cybernetic announcement.

The wrapper is really ‘around’ the method… so I like ‘inner’ myself.

Nathaniel
Terralien, Inc.

<:((><

···

On Apr 20, 2004, at 18:04, David Alan Black wrote:

For what it’s worth, I don’t agree that it’s very similar to
redefinition in a subclass. I would actually find it hard to find a
unified way to explain both 'super’s to someone, and I’m not sure why
the same word is being reused. I guess ‘wrapee’ is a bit too bizarre
:slight_smile: But I do wish it were something other than super, since the
wrapped method is not ‘above’ the wrapper (the way a superclass’s
method is above the subclass’s method).

Lothar Scholz wrote:

Hello Curt,

Wednesday, April 21, 2004, 12:41:52 AM, you wrote:

I get the following error from the splitter sample

Z:\work\arachno\make>"C:\Program Files\Arachno Ruby
IDE\ruby\samples\wxRuby-0.3.0\splitter\splitter.
rb"
C:/Program Files/Arachno Ruby
IDE/ruby/samples/wxRuby-0.3.0/splitter/splitter.rb:257:in `main_loop':
undefined method `on_draw' for #<MyCanvas:0x2a54830> (NoMethodError)
        from C:/Program Files/Arachno Ruby
IDE/ruby/samples/wxRuby-0.3.0/splitter/splitter.rb:257

All other samples seem to work fine.

Thanks.

I tried fixing the reported error. The main window displays just fine, but
then it segfault's when I try to resize the window. :frowning:

I'm working on a wxRuby app that does have a working splitter in it. If you
need an example, let me know and I'll send it to you.

Curt

Hi,

Okay, maybe someone could clear this up… how exactly will the
defadvice stuff work? once you wrap a method, is the wrapper permanent?

You can stack wrapper methods in a class, so that

For example, can you:

class Foo
def foo
print “foo”
end
def foo:wrap
print “:”; super; print “:”
end
end

then, later reopen the class and:

class Foo
def foo:wrap
print “.”; super; print “.”
end
end

…and expect that calling Foo.new.foo will print “.:foo:.”

should work as you expected. I haven’t designed the API to remove the
around (and before/after) methods yet.

						matz.
···

In message “Re: proposal: call_up() for use in redefined methods” on 04/04/22, Mark Hubbart discord@mac.com writes:

Hi,

Okay, maybe someone could clear this up… how exactly will the
defadvice stuff work? once you wrap a method, is the wrapper
permanent?

You can stack wrapper methods in a class, so that
[snip my code]
should work as you expected. I haven’t designed the API to remove the
around (and before/after) methods yet.

  					matz.

So you can either add a second wrapper, or replace the older one! Very
cool :slight_smile:

Thanks,
–Mark

···

On Apr 21, 2004, at 10:42 AM, Yukihiro Matsumoto wrote:

In message “Re: proposal: call_up() for use in redefined methods” > on 04/04/22, Mark Hubbart discord@mac.com writes: