Refinements: allow modules inclusion, in which the module can call internal methods which it defines.
Right now this isn't possible:
module Extensions def vegetables ; potatoe ; end def potatoe ; "potatoe" ; end end module Refinary refine String do # this doesn't work include Extensions # this would work... # def vegetables ; potatoe ; end # def potatoe ; "potatoe" ; end end end using Refinary puts "tomatoe".vegetables #=> in <main>': undefined method 'vegetables' for "tomatoe":String
Wrongly reported as a bug here.
According to Shugo Maeda, this was expected behaviour. I argued that this is the way most monkey-patches work, and if Refinements can't cover the use case of inserting a custom DSL which references itself in the classes it refines, it can't fully replace monkey-patches, which I read was the main reason Refinements have been added to the language.
#7 [ruby-core:82991] Updated by shyouhei (Shyouhei Urabe) 11 months ago
We looked at this issue at a developer meeting today.
The OP's intension is clear. We can describe how it works, but that seems to be different from how it should work. Matz was there at the discussion so he understands the situation. I'm sure he's going to have some decision.
#8 [ruby-core:83019] Updated by matz (Yukihiro Matsumoto) 11 months ago
As you may know,
include inserts the module in the inheritance hierarchy. In this case,
module Extensions is inserted above
String in the
using Refinary scope. That means the lexical scope of
potatoes are different from the refined scope so that
potatoes cannot be called from
vegetables because the scope is not refined. The situation is a bit complex. Do you follow me?
In some other class extension proposals found in other languages (for example, ClassBox in Java), scopes of methods called from within ClassBox are also modified. This is called local rebinding. But we don't choose that because it has bigger side effects. We chose the safer side.
The issue is by
include we might expect the features from the included module are available but in fact, they aren't due to the mechanism of
include and refinements.
So I counter-propose a new feature,
inject does not modify inheritance hierarchy. Instead, it copies attributes (constants, modules, and refinements) into the target class/module.
We have no concrete plan to refine the refinement. We are vaguely thinking about combining
using in some way. Just idea.
#9 [ruby-core:83159] Updated by chucke (Tiago Cardoso) 10 months ago
How about redefining
#include in the context of refine as what would be expected of the new
#inject method? I get that the semantics of module inclusion differ in both context regarding inheritance hierarchy order, but I'm still thinking from the user perspective: "if I do it, what do I expect to happen?".
From this user perspective, I'd prefer an
#include method which does what I expect, instead of yet another method (
#inject) that I have to learn.
But there might be other implications. I'm fine with whichever proposal which makes refinements more usable for meta-programming.