From: "jeremyevans0 (Jeremy Evans) via ruby-core" Date: 2026-09-09T17:06:38+00:00 Subject: [ruby-core:126617] [Ruby Bug#22273] Aliasing doesn't interact well with Module#prepend Issue #22273 has been updated by jeremyevans0 (Jeremy Evans). Eregon (Benoit Daloze) wrote in #note-6: > What if one actually wants to alias a method from a prepended module, how should they do it? > > Maybe > ```ruby > define_method(:my_alias, instance_method(:original)) > ``` > ? > That gives the same result. Actually, it doesn't, for the example I gave above: ```ruby module M def m = [M, :m, *super] end module N def m2 = [N, :m2, *super] end class Object def m = [Object] end class C prepend N define_method(:m, instance_method(:m2)) prepend M define_method(:m2, instance_method(:m)) def m = [C, :m, *super] end C.ancestors # => [M, N, C, Object, Kernel, BasicObject] C.new.m2 # => [N, :m2, M, :m, Object] m = C.new.method(:m2) # => # m = m.super_method # => # m = m.super_method # => # m = m.super_method # => nil ``` Note that it in this case, the `super` calls do not reverse the ancestry, in terms of the method owner. For the `alias` case, it goes N -> M -> C, even though the ancestry is M -> N -> C. With `define_method`, it goes N -> C, which is consistent with the ancestry. To answer your question about how to alias a method in a prepended module, you would define a method that calls the method you want to alias (or use `define_method` as shown above) instead of using `alias`/`alias_method`. All of these approaches have different semantics (as shown above), but for most cases, either new method that calls original method or `define_method` with original method should work. > My general thinking is `alias` and `alias_method` should do exactly the same as `define_method(alias_name, instance_method(original_name))`. > That would be so much easier to understand and much simpler semantically (ZSUPER aliases add so many complications). > > Though in this issue it seems they are equivalent for this reproduction. I agree that would be simpler, but they are not equivalent. For one, `define_method` doesn't even require the original method be from the current lookup hierarchy, you can use an instance method from a module the class doesn't include/prepend, whereas `alias`/`alias_method` require the method be defined in the lookup hierarchy. > Mixing `prepend` (especially on `Kernel`) and `alias` works poorly in general, I think one should use one or the other. Agreed that it works poorly. More importantly to me is this issue with `super` allows behavior that I don't think should be allowed. > In short, `alias` and `alias_method` do a lookup similar to `instance_method`. > While changing that would fix this specific case it could break others. > And I guess most agree we shouldn't change how `instance_method` looks things up (i.e. it should find prepended methods), so if we'd change `alias` lookup we'd make it inconsistent. As shown above, `alias` and `define_method` with `instance_method` operate differently and `define_method` with `instance_method` does not have the same issue with `super`. As the behavior already differs between the two cases, I don't think they need to be consistent. In terms of your idea that `alias`/`alias_method` operate like `define_method(alias_name, instance_method(original_name))`, that would be a different breaking change. ---------------------------------------- Bug #22273: Aliasing doesn't interact well with Module#prepend https://bugs.ruby-lang.org/issues/22273#change-118856 * Author: luke-gru (Luke Gruber) * Status: Open * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- Currently, aliasing doesn't interact well with `Module#prepend` in my opinion. ### Example ```ruby module Kernel prepend(Module.new do def require(feature) puts "requiring feature (prepend): #{feature}" super end end) end module Kernel alias original_require require def require(feature) puts "requiring feature (alias): #{feature}" original_require(feature) end end require "set" ``` This produces this behavior: ``` requiring feature (prepend): set requiring feature (alias): set requiring feature (prepend): set ../ruby/test.rb:5:in 'require': super: no superclass method 'require' for main (NoMethodError) ``` I would expect this behavior: ``` requiring feature (prepend): set requiring feature (alias): set # Then, the original require would succeed ``` This has caused issues such as [22263](https://bugs.ruby-lang.org/issues/22263) and has [confused gem authors](https://github.com/fxn/zeitwerk/pull/203#issuecomment-1107777206). In the second link, the ignored alias was due to [this bug](https://github.com/ruby/ruby/commit/c59c4d717a2e687972341eab1574196e97d7d7be) which has recently been fixed. ### Bug? As far as I know this is intentional behavior introduced in Ruby 2.0 [here](https://bugs.ruby-lang.org/issues/7842). There are even tests and specs that codify this behavior such as `test_prepend_super_in_alias` and `prepend_spec.rb`. Even though it's intentional, I don't believe it's well thought out. I'm interested in hearing arguments for and against the current behavior (with code examples, preferably). -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/