Forum: Ruby-core Proposal: Module#copy_method

Posted by Yehuda Katz (wycats)
on 2009-01-18 08:28
(Received via mailing list)
I'd like it to be possible to copy methods from one module to another. 
The
rationale is that this feature would make it possible to modify existing
methods without being forced to resort to aliasing. Here's an 
implementation
that works on 1.8 and 1.9:
static VALUE copy_method(VALUE self, VALUE klass, VALUE symbol) {
  ID id = rb_to_id(symbol);
  NODE *node = NULL;
  st_table *other_method_table = RCLASS(klass)->m_tbl;
  st_table *method_table = RCLASS(self)->m_tbl;

  if(st_lookup(other_method_table, id, (void *)&node)) {
    st_insert(method_table, (st_data_t)id, (st_data_t)node);
    return (Qtrue);
  } else {
    rb_raise(rb_eRuntimeError, "No such method `%s' on %s", 
rb_id2name(id),
STR2CSTR(rb_mod_name(klass)));
  }
}

void Init_copy_method() {
  VALUE c = rb_cObject;
  c = rb_const_get(c,rb_intern("Module"));
  rb_define_method(c, "copy_method", copy_method, 2);
}

And the ruby code:

module TrackAddedMethods
  def method_added(meth)
    @__added_methods ||= []
    @__added_methods << meth
  end

  def __added_methods
    @__added_methods
  end
end

class Module
  def __move_to_module(*meth_names)
    me = self
    mod = Module.new do
      meth_names.each do |name|
        copy_method me, name
      end
    end
    meth_names.each do |name|
      remove_method name
    end
    include mod
  end

  def chain(&blk)
    mod = Module.new do
      extend TrackAddedMethods
    end
    mod.class_eval(&blk)
    __move_to_module(*mod.__added_methods)
    include mod
  end
end

It would be used like:

class Bar
  def foo
    1
  end

  chain do
    def foo
      super + 1
    end
  end
end

This particular Ruby implementation is just one option, but the point is
that having the generic ability to move methods around would be 
powerful.
Posted by Yehuda Katz (wycats)
on 2009-01-18 08:33
(Received via mailing list)
Note that this implementation is fairly tailored for the use-case I was
targeting, and as such doesn't COPY the method, but only repoints and
undefines the original one. A more generic implementation would probably 
be
similar semantically to alias_method. Imagine:
alias_method_to_module :bar, Baz, :bat

-- Yehuda
Posted by Eero Saynatkari (Guest)
on 2009-01-18 09:25
(Received via mailing list)
Excerpts from Yehuda Katz's message of Sun Jan 18 09:26:19 +0200 2009:
> I'd like it to be possible to copy methods from one module to another. The
> rationale is that this feature would make it possible to modify existing
> methods without being forced to resort to aliasing.

Do you have an expanded use-case for this (copying in general)?
The chaining example seemed a bit convoluted, probably due to
having been distilled.

If implemented, it should certainly actually copy the method, but
I am a bit unsure of its utility at the user code level. How well
do you see it integrating with normal #include/#extend semantics?
Posted by Yehuda Katz (wycats)
on 2009-01-18 10:00
(Received via mailing list)
I see it as integrating with include/extend identically to
alias/alias_method.
The convoluted nature of this example should be counteracted by the fact
that it's a *real* example.

-- Yehuda
Posted by Pit Capitain (Guest)
on 2009-01-18 10:25
(Received via mailing list)
2009/1/18 Yehuda Katz <wycats@gmail.com>:
> I'd like it to be possible to copy methods from one module to another. (...)

Note that on 1.8 you can simply change the line

  copy_method me, name

to

  define_method(name, me.instance_method(name))

and it works without #copy_method.

Regards,
Pit
Posted by Eero Saynatkari (Guest)
on 2009-01-18 17:25
(Received via mailing list)
Excerpts from Pit Capitain's message of Sun Jan 18 11:23:48 +0200 2009:
> 
> and it works without #copy_method.

Only if me <= self, though? One can work around that
by using Method#to_proc, but it has its own problems.

I am still not entirely sure of the use case, but I
take Yehuda at his word that he actually uses "foo"
in real code ;)
Posted by Yehuda Katz (wycats)
on 2009-01-18 18:25
(Received via mailing list)
The code I posted above is a replacement for Rails' alias_method_chain 
that
retains the original method name and allows the use of super instead of
calling the old method via foo_without_bar.
Eero is correct that copying methods the way Pit Captain suggested will 
not
work unless me <= self. However, that restriction doesn't seem like it's
actually required (unless I'm missing something), so maybe extend
define_method to work like copy_method above is the way to go.

-- Yehuda
Posted by Charles Oliver Nutter (Guest)
on 2009-01-18 19:26
(Received via mailing list)
Yehuda Katz wrote:
> end
> 
> This particular Ruby implementation is just one option, but the point is 
> that having the generic ability to move methods around would be powerful.

Isn't this just AOP? You want to be able to specify an "around" behavior
without the alias chaining that Rails does currently. What's the status
of AOP support in third-party extensions? I believe Matz has also
proposed an AOP that uses super to call the original in a similar way.
It seems like adding a general-purpose AOP mechanism to Ruby, either
through an extension or added to core, would be cleaner than a method
transplanting mechanism.

- Charlie
Posted by Ryan Davis (Guest)
on 2009-01-18 20:23
(Received via mailing list)
On Jan 18, 2009, at 08:23 , Eero Saynatkari wrote:

>> to
>>
>>  define_method(name, me.instance_method(name))
>>
>> and it works without #copy_method.
>
> Only if me <= self, though? One can work around that
> by using Method#to_proc, but it has its own problems.

I was going to point out the same thing, but it is a 1 line removal to
make that not that case and I think it is worth it. Much easier and it
opens up a whole lot of possibilities.

> I am still not entirely sure of the use case, but I
> take Yehuda at his word that he actually uses "foo"
> in real code ;)

actors or instance-based-inheritance (ala self) are two use cases. I'd
love to do the latter more.
Posted by Yehuda Katz (wycats)
on 2009-01-18 21:27
(Received via mailing list)
AOP requires being able to hook into method dispatch, which either 
requires
a modification to core, or method transplanting. Unless you see a 
different
approach?
-- Yehuda

On Sun, Jan 18, 2009 at 10:25 AM, Charles Oliver Nutter <
Posted by Yehuda Katz (wycats)
on 2009-01-18 21:28
(Received via mailing list)
Agreed. I'm happy with modifying define_method to not do the <= check.
-- Yehuda
Posted by Charles Oliver Nutter (Guest)
on 2009-01-18 21:33
(Received via mailing list)
Yehuda Katz wrote:
> AOP requires being able to hook into method dispatch, which either 
> requires a modification to core, or method transplanting. Unless you see 
> a different approach?

Not necessarily. Around can also be a bit of method logic that simply
aggregates the original method, like an alias with extra logic. And of
course you've essentially implemented "around" behavior without
modifying core, so there's bound to be other ways too.

- Charlie
Posted by Yehuda Katz (wycats)
on 2009-01-18 21:40
(Received via mailing list)
So I guess the question is... what's wrong with the implementation ;)
-- Yehuda

On Sun, Jan 18, 2009 at 12:31 PM, Charles Oliver Nutter <
Posted by Charles Oliver Nutter (Guest)
on 2009-01-18 22:03
(Received via mailing list)
Not a whole lot, except that it causes another level of method searching
(included anon module) for each such use, which will impact perf in MRI.

It does seem like the method-accepting form of define_method could be a
little less restrictive (as you and I discussed online), allowing pure
Ruby methods to be "copied" directly without Method#bind inheritance
restrictions. Modifying Method#bind logic in JRuby in a similar way does
allow this to work correctly:

module Foo
   def foo
     puts 'here'
   end
end

module Bar
   define_method(:a, Foo.instance_method(:foo))
end

include Bar
a # => prints 'here'

I think the tricky bit here is that modifying this Method#bind check
could cause native methods to be moved to classes with different
in-memory structure. Filtering out all native methods (as we discussed
online) would be a necessity. I'm not sure if there's precedent for that
kind of check currently.
Posted by John Barnette (Guest)
on 2009-01-18 22:15
(Received via mailing list)
On Sun, Jan 18, 2009 at 1:02 PM, Charles Oliver Nutter
<charles.nutter@sun.com> wrote:
> I think the tricky bit here is that modifying this Method#bind check could
> cause native methods to be moved to classes with different in-memory
> structure. Filtering out all native methods (as we discussed online) would
> be a necessity. I'm not sure if there's precedent for that kind of check
> currently.

Aaron Patterson had a patch for this floating around last year, and
IIRC the native method issues were the reason it didn't get anywhere.


~ j.
Posted by Charles Oliver Nutter (Guest)
on 2009-01-18 22:34
(Received via mailing list)
Yehuda Katz wrote:
> I'd like it to be possible to copy methods from one module to another. 
> The rationale is that this feature would make it possible to modify 
> existing methods without being forced to resort to aliasing. Here's an 
> implementation that works on 1.8 and 1.9:

Here's a pure-ruby version that works on an unmodified JRuby:

require 'jruby'

class Module
   def copy_method(mod, sym)
     # get JRuby DynamicMethod object from RubyMethod
     org.jruby.RubyMethod.field_reader :method
     ruby_method = mod.instance_method(sym)
     real_method = JRuby.reference(ruby_method).method

     # native methods may have memory/object layout needs
     raise "can't copy native method" if real_method.native?

     # Add it directly to the JRuby RubyModule self
     real_modcls = JRuby.reference(self)
     real_modcls.add_method(sym.to_s, real_method)
   end
end

- Charlie
Posted by Yehuda Katz (wycats)
on 2009-01-19 00:16
(Received via mailing list)
Just FYI, the define_method version fails if your method requires the 
use of
super. Observe:

class Zoo1
  def bar
    p "In superclass"
    1
  end
end

class Zoo2 < Zoo1
  def bar
    p "in Zoo2mod #{self}"
    super + 1
  end

  mod = Module.new do
    define_method(:bar, Zoo2.instance_method(:bar))
  end
  include mod

  def bar
    p "In bar"
    super + 1
  end
end

p Zoo2.new.bar

The above produces SystemStackError: stack level too deep, 
infinite-looping
in the original bar implementation on Zoo2. Also, it doesn't work on 
1.9.

-- Yehuda

On Sun, Jan 18, 2009 at 2:32 PM, Charles Oliver Nutter <
Posted by Yehuda Katz (wycats)
on 2009-01-19 05:57
(Received via mailing list)
MenTaLguY had a very good suggestion: modify alias_method so its second
argument can be an unbound method. I like this solution a *lot*.
-- Yehuda
Posted by Tomas Matousek (Guest)
on 2009-01-19 06:06
(Received via mailing list)
I believe that methods (and blocks defined as methods via 
method_defined) are somehow closed over the module that declares them so 
that "super" can find the right module/class whose parent's method to 
call. At least they are in IronRuby :)

A simple copy of a method without changing binding to its declaring 
module therefore causes super to fail (or changes its semantics). I 
think this is the case even for module_function (I might have an example 
somewhere that demonstrates it, will send it tomorrow if I find it). 
module_function "copies" the method from declaring module to its 
singleton class, which is indeed not derived from the declaring module, 
breaking super in some constructed cases.

Tomas

From: Yehuda Katz [mailto:wycats@gmail.com]
Sent: Sunday, January 18, 2009 3:14 PM
To: ruby-core@ruby-lang.org
Subject: [ruby-core:21423] Re: Proposal: Module#copy_method

Just FYI, the define_method version fails if your method requires the 
use of super. Observe:

class Zoo1
  def bar
    p "In superclass"
    1
  end
end

class Zoo2 < Zoo1
  def bar
    p "in Zoo2mod #{self}"
    super + 1
  end

  mod = Module.new do
    define_method(:bar, Zoo2.instance_method(:bar))
  end
  include mod

  def bar
    p "In bar"
    super + 1
  end
end

p Zoo2.new.bar

The above produces SystemStackError: stack level too deep, 
infinite-looping in the original bar implementation on Zoo2. Also, it 
doesn't work on 1.9.

-- Yehuda
On Sun, Jan 18, 2009 at 2:32 PM, Charles Oliver Nutter 
<charles.nutter@sun.com<mailto:charles.nutter@sun.com>> wrote:
Yehuda Katz wrote:
I'd like it to be possible to copy methods from one module to another. 
The rationale is that this feature would make it possible to modify 
existing methods without being forced to resort to aliasing. Here's an 
implementation that works on 1.8 and 1.9:

Here's a pure-ruby version that works on an unmodified JRuby:

require 'jruby'

class Module
 def copy_method(mod, sym)
   # get JRuby DynamicMethod object from RubyMethod
   org.jruby.RubyMethod.field_reader :method
   ruby_method = mod.instance_method(sym)
   real_method = JRuby.reference(ruby_method).method

   # native methods may have memory/object layout needs
   raise "can't copy native method" if real_method.native?

   # Add it directly to the JRuby RubyModule self
   real_modcls = JRuby.reference(self)
   real_modcls.add_method(sym.to_s, real_method)
 end
end

- Charlie
Posted by Yehuda Katz (wycats)
on 2009-01-19 06:18
(Received via mailing list)
I can show that this is not the case in MRI because my very crude hack
(which just copies the method) doesn't break super semantics.
-- Yehuda

On Sun, Jan 18, 2009 at 10:04 PM, Tomas Matousek <
Posted by Tomas Matousek (Guest)
on 2009-01-19 09:01
(Received via mailing list)
I'm not saying that it couldn't work. If implemented differently than 
1.9's module_function then it might be ok.
The example I referred to is following (1.8.6 works fine, 1.9 doesn't):

class Module
  # module_function is not defined on Class class, let's add a public 
alias:
  alias :mf :module_function
  public :mf
end

# define foo in D and a different one in singleton of D:
class D
  def foo
    puts 'D::foo'
  end

  class << self
    def foo
      puts 'S(D)::foo'
    end
  end
end

# define foo in C < D and in singleton of C via module_function:
class C < D
  # call module_function
  # - module_function checks that self is a class
  # - thus we need to call it with a module receiver (e.g. Kernel, but 
any other module would do)
  Kernel.mf

  # defines foo in singleton of C since module_function has been called 
in the current scope:
  def foo
    puts 'foo'
    super
  end

  # calls foo on C, which should invoke foo on singleton of C, which 
should call super method on singleton of D
  foo
end

Ruby 1.8.6 (right):
foo
S(D)::foo

Ruby 1.9.1 (wrong):
foo
D::foo

Tomas

From: Yehuda Katz [mailto:wycats@gmail.com]
Sent: Sunday, January 18, 2009 9:16 PM
To: ruby-core@ruby-lang.org
Subject: [ruby-core:21426] Re: Proposal: Module#copy_method

I can show that this is not the case in MRI because my very crude hack 
(which just copies the method) doesn't break super semantics.

-- Yehuda
On Sun, Jan 18, 2009 at 10:04 PM, Tomas Matousek 
<Tomas.Matousek@microsoft.com<mailto:Tomas.Matousek@microsoft.com>> 
wrote:

I believe that methods (and blocks defined as methods via 
method_defined) are somehow closed over the module that declares them so 
that "super" can find the right module/class whose parent's method to 
call. At least they are in IronRuby :)



A simple copy of a method without changing binding to its declaring 
module therefore causes super to fail (or changes its semantics). I 
think this is the case even for module_function (I might have an example 
somewhere that demonstrates it, will send it tomorrow if I find it). 
module_function "copies" the method from declaring module to its 
singleton class, which is indeed not derived from the declaring module, 
breaking super in some constructed cases.



Tomas



From: Yehuda Katz [mailto:wycats@gmail.com<mailto:wycats@gmail.com>]
Sent: Sunday, January 18, 2009 3:14 PM
To: ruby-core@ruby-lang.org<mailto:ruby-core@ruby-lang.org>
Subject: [ruby-core:21423] Re: Proposal: Module#copy_method



Just FYI, the define_method version fails if your method requires the 
use of super. Observe:

class Zoo1
  def bar
    p "In superclass"
    1
  end
end

class Zoo2 < Zoo1
  def bar
    p "in Zoo2mod #{self}"
    super + 1
  end

  mod = Module.new do
    define_method(:bar, Zoo2.instance_method(:bar))
  end
  include mod

  def bar
    p "In bar"
    super + 1
  end
end

p Zoo2.new.bar

The above produces SystemStackError: stack level too deep, 
infinite-looping in the original bar implementation on Zoo2. Also, it 
doesn't work on 1.9.

-- Yehuda

On Sun, Jan 18, 2009 at 2:32 PM, Charles Oliver Nutter 
<charles.nutter@sun.com<mailto:charles.nutter@sun.com>> wrote:

Yehuda Katz wrote:

I'd like it to be possible to copy methods from one module to another. 
The rationale is that this feature would make it possible to modify 
existing methods without being forced to resort to aliasing. Here's an 
implementation that works on 1.8 and 1.9:



Here's a pure-ruby version that works on an unmodified JRuby:

require 'jruby'

class Module
 def copy_method(mod, sym)
   # get JRuby DynamicMethod object from RubyMethod
   org.jruby.RubyMethod.field_reader :method
   ruby_method = mod.instance_method(sym)
   real_method = JRuby.reference(ruby_method).method

   # native methods may have memory/object layout needs
   raise "can't copy native method" if real_method.native?

   # Add it directly to the JRuby RubyModule self
   real_modcls = JRuby.reference(self)
   real_modcls.add_method(sym.to_s, real_method)
 end
end

- Charlie



--
Yehuda Katz
Developer | Engine Yard
(ph) 718.877.1325
Posted by Yukihiro Matsumoto (Guest)
on 2009-01-19 09:12
(Received via mailing list)
Hi,

In message "Re: [ruby-core:21399] Proposal: Module#copy_method"
    on Sun, 18 Jan 2009 16:26:19 +0900, Yehuda Katz <wycats@gmail.com> 
writes:

|I'd like it to be possible to copy methods from one module to another. The
|rationale is that this feature would make it possible to modify existing
|methods without being forced to resort to aliasing. Here's an implementation
|that works on 1.8 and 1.9:

This implementation can easily cause segmentation fault, since many C
implemented method does not check type of their receivers.  Try

  class Foo
    copy_method Bignum, :odd?
  end
  Foo.new.odd?

for example.  The issue is more difficult than it seems.  I've had
similar issue when we implemented Method#bind, this one is even
tougher.

              matz.
Posted by Yehuda Katz (wycats)
on 2009-01-19 10:06
(Received via mailing list)
Matz,
Couldn't we just filter out all c-funcs from copying?

-- Yehuda
Posted by Yehuda Katz (wycats)
on 2009-01-19 10:22
(Received via mailing list)
In particular:
if(nd_type(node) == NODE_CFUNC && !(rb_class_inherited_p(self, klass))) 
{
  rb_raise("you can not move a c function to an incompatible class");
}

-- Yehuda
Posted by Ryan Davis (Guest)
on 2009-01-19 11:23
(Received via mailing list)
On Jan 19, 2009, at 00:10 , Yukihiro Matsumoto wrote:

> tougher.
Isn't it sufficient to not let cfunc nodes be copied? Or don't worry
about it and trust the developers. To quote you on the :!= issue:

> It's intentional.  Ruby's flexibility sometimes allows you to do some
> nasty things.  Ruby consider you smart enough to avoid such
> contradiction.

couldn't that work here?
Posted by Yukihiro Matsumoto (Guest)
on 2009-01-19 15:44
(Received via mailing list)
Hi,

In message "Re: [ruby-core:21433] Re: Proposal: Module#copy_method"
    on Mon, 19 Jan 2009 19:21:59 +0900, Ryan Davis 
<ryand-ruby@zenspider.com> writes:

|> This implementation can easily cause segmentation fault, since many C
|> implemented method does not check type of their receivers.  Try
|>
|>  class Foo
|>    copy_method Bignum, :odd?
|>  end
|>  Foo.new.odd?

|Isn't it sufficient to not let cfunc nodes be copied?

I am afraid it's too much restriction for OP's use-case, since so many
methods are implemented in C.

|Or don't worry  
|about it and trust the developers. To quote you on the :!= issue:
|
|> It's intentional.  Ruby's flexibility sometimes allows you to do some
|> nasty things.  Ruby consider you smart enough to avoid such
|> contradiction.
|
|couldn't that work here?

I don't think so.  Segmentation fault is too much to tolerate.

              matz.
Posted by Roger Pack (Guest)
on 2009-01-19 20:05
(Received via mailing list)
On Sun, Jan 18, 2009 at 12:26 AM, Yehuda Katz <wycats@gmail.com> wrote:

> I'd like it to be possible to copy methods from one module to another. The
> rationale is that this feature would make it possible to modify existing
> methods without being forced to resort to aliasing. Here's an implementation
> that works on 1.8 and 1.9:


You can somewhat copy methods from one module to another [in a somewhat
round about way] currently, or am I off?

http://eigenclass.org/hiki/Rename+and+reject+methods+from+included+modules
http://betterlogic.com/roger/?p=537

-=r
Posted by Yehuda Katz (wycats)
on 2009-01-19 21:11
(Received via mailing list)
It's not really an issue for AOP, since the C methods are included into 
a
module, which is then reincluded into the original class. So there's no 
risk
of segfault in that case.
-- Yehuda
Posted by Charles Oliver Nutter (Guest)
on 2009-01-19 21:17
(Received via mailing list)
Yukihiro Matsumoto wrote:
> |Isn't it sufficient to not let cfunc nodes be copied?
> 
> I am afraid it's too much restriction for OP's use-case, since so many
> methods are implemented in C.

The JRuby example I posted does filter out native methods. I agree
that's a necessary minimum restriction in order to support this. I do
not think it's too limiting a restriction for copy_method to be useful
though.

> I don't think so.  Segmentation fault is too much to tolerate.

Agreed.

- Charlie
Posted by Charles Oliver Nutter (Guest)
on 2009-01-19 21:21
(Received via mailing list)
Tomas Matousek wrote:
> I believe that methods (and blocks defined as methods via 
> method_defined) are somehow closed over the module that declares them so 
> that “super” can find the right module/class whose parent’s method to 
> call. At least they are in IronRuby J

You may find that you need to track separately the actual class you're
calling a method against and store it in whatever context you use to
correspond to a Ruby method frame. That's how we do it in JRuby after
much consternation and hassle, since compatibility simply required.
That's not to say there's not a better way, but we haven't found it so 
far.

And at the very least, you need to track the actual target class
separately since included module methods need to super up the right
hierarchy. How do you handle that in IronRuby?

- Charlie
Posted by Pit Capitain (Guest)
on 2009-01-19 22:08
(Received via mailing list)
2009/1/18 Yehuda Katz <wycats@gmail.com>:
> Eero is correct that copying methods the way Pit Captain suggested will not
> work unless me <= self.

The define_method replacement for copy_method was not meant as a
general solution, just for your use case in Module#__move_to_module.
Ruby 1.8 does allow it in this case, but 1.9 doesn't.

Regards,
Pit
Posted by Yukihiro Matsumoto (Guest)
on 2009-01-20 08:11
(Received via mailing list)
Hi,

In message "Re: [ruby-core:21439] Re: Proposal: Module#copy_method"
    on Tue, 20 Jan 2009 05:14:49 +0900, Charles Oliver Nutter 
<charles.nutter@sun.com> writes:

|> I am afraid it's too much restriction for OP's use-case, since so many
|> methods are implemented in C.
|
|The JRuby example I posted does filter out native methods. I agree 
|that's a necessary minimum restriction in order to support this. I do 
|not think it's too limiting a restriction for copy_method to be useful 
|though.

Although Yehuda (OP) also suggested the same limitation, I am strongly
against.  Ruby does provide little or no distinction between C
implemented methods and Ruby implemented ones.  I would not feel
reasonable when I see unexpected error to copy some methods, where
other methods can be copied.  This enhancement cannot have a place in
the built-in methods.  It should be separated library at most.

              matz.
Posted by Yehuda Katz (wycats)
on 2009-01-20 16:17
(Received via mailing list)
For my use-case, it would be ok to support copying methods to ANY 
module,
and then raise an exception at runtime if the module was included into 
an
incompatible class. This is *kind* of possible in 1.8 (with problems 
with
super) and not really possible in 1.9.
I would also be willing to write something along the lines of the use 
case
in the OP as a patch (which would work with all methods).

-- Yehuda
Posted by Ken Bloom (Guest)
on 2009-01-20 16:39
(Received via mailing list)
On Tue, 20 Jan 2009 05:09:14 +0900, Yehuda Katz wrote:

> It's not really an issue for AOP, since the C methods are included into
> a module, which is then reincluded into the original class. So there's
> no risk of segfault in that case.
> -- Yehuda

You'd need to code in another special AOP exception in order to make AOP
work.

--Ken
Posted by Brian Mitchell (Guest)
on 2009-01-20 17:33
(Received via mailing list)
On Tue, Jan 20, 2009 at 10:15, Yehuda Katz <wycats@gmail.com> wrote:
> For my use-case, it would be ok to support copying methods to ANY module,
> and then raise an exception at runtime if the module was included into an
> incompatible class. This is *kind* of possible in 1.8 (with problems with
> super) and not really possible in 1.9.
> I would also be willing to write something along the lines of the use case
> in the OP as a patch (which would work with all methods).
> -- Yehuda

I wonder if your specific use case might have other solutions that
involve changes to Ruby which do not have such specific drawbacks?

Bringing back Matz's slides from 2005 [0] we can see some concepts of
what some extensions might look like. Right now those seem to use
implicit calls to the original method. Might there be a way to set a
reference to the intended super method to override basic dispatch in a
method context? Specifically, a method could compile to a node which
sets up some special info inside the call context which is checked
which is checked for when super is looked up. This could also possibly
be shared with method lookup caches.

I'm not an implementation expert but this seems much more likely to
work well with C methods and avoids the whole copy method problem. If
a method needs moving, delegation is likely to be the best pattern.
Unless someone can come up with a different use of method copying [1]
I would oppose such a change.

Brian.

[0]: http://www.rubyist.net/~matz/slides/rc2005/mgp00034.html
[1]: I assume that experiments with self-style activated slots would
count as mostly experimental and can usually be best done with vclass
semantics already. Also, this is the sort of case where being able to
uninclude a module might be interesting.
Posted by Yehuda Katz (wycats)
on 2009-01-21 09:19
(Received via mailing list)
What you're suggesting could certainly work; I was looking for the least
invasive possible change to Ruby's core that would still support an AOP 
DSL.
Your suggestion is likely invasive enough to end up being a 
long-standing
attempted project (it has been "in progress" at least since 2005), since 
it
would mean that the AOP specifics would need to be implemented in core, 
as
opposed to providing facilities for implementing it in pure Ruby which 
could
then be rolled back in once the implementation is worked out.
-- Yehuda
Posted by Brian Mitchell (Guest)
on 2009-01-21 16:33
(Received via mailing list)
On Wed, Jan 21, 2009 at 03:17, Yehuda Katz <wycats@gmail.com> wrote:
> What you're suggesting could certainly work; I was looking for the least
> invasive possible change to Ruby's core that would still support an AOP DSL.
> Your suggestion is likely invasive enough to end up being a long-standing
> attempted project (it has been "in progress" at least since 2005), since it
> would mean that the AOP specifics would need to be implemented in core, as
> opposed to providing facilities for implementing it in pure Ruby which could
> then be rolled back in once the implementation is worked out.
> -- Yehuda

Right but it does seem to be much more direct at attacking both the
target problem (also probably making it more efficient) as well as
working across the board with C methods. Now it is invasive to Ruby
but I think a well working copy system might end up needing quite a
few adjustments for edge cases as well.

The only reason I brought it up is because the concept is from Matz's
slides (2005 granted but I think most of them are still valid
directions to take) which muse over quite a number of yet
unimplemented ideas. Some of them might be worth considering for an
upcoming 1.9 release as well as possibly a 1.8 release if we can show
it doesn't break old code (I can't think of why it would).

Brian.
Posted by Roger Pack (Guest)
on 2009-01-22 19:08
(Received via mailing list)
>     def foo
>       super + 1
>     end
>   end
> end
>

Just throwing this out [and I realize it's already been discussed a 
bit]...
It seems possible as long as you start with a module always...

class Class
 def chainable &block
  m = Module.new
  m.module_eval &block
  include m
 end
end

class A
 chainable do
   def abc
     print "in original abc"
   end
 end

 chainable do
    def abc
      print "in second abc"
      super
    end
 end

end

>> A.new.abc
in second abcin original abc


I will admit the inability to share a method from one class to another
[unrelated class] is surprising.  It seems that it is possible to move
methods from module to module and from module to class but not from 
class to
module or class to class.  Odd but not the end of the world.

I suppose for a more aggressive approach you could incorporate 
object2module
<tongue in cheek> [1].

Take care.

-=r

[1]
http://banisterfiend.wordpress.com/2008/12/18/object2module-converting-ruby-objects-to-modules/
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.