Project

General

Profile

Actions

Feature #12533

closed

Refinements: allow modules inclusion, in which the module can call internal methods which it defines.

Added by chucke (Tiago Cardoso) over 7 years ago. Updated over 2 years ago.

Status:
Closed
Target version:
-
[ruby-core:76197]

Description

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.


Related issues 1 (0 open1 closed)

Related to Ruby master - Bug #14012: NameError is raised when use class variables in RefinementsRejectedActions

Updated by chucke (Tiago Cardoso) over 7 years ago

Sorry about the UI for the gist, apparently I don't know anymore how to properly declare code blocks in redmine...

Updated by akhramov (Artem Khramov) over 7 years ago

  • Description updated (diff)

Updated by akhramov (Artem Khramov) over 7 years ago

Hi Tiago, the syntax is

your(code).goes here

Sorry for offtop.

Updated by shugo (Shugo Maeda) over 7 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

Matz, what do you think of this?

Local rebinding may be worth considering, but there is a trade-off.

Updated by shevegen (Robert A. Heiler) almost 7 years ago

Sorry for slight off-topic from me here - I wonder if refinements will be refined when ruby 3.x comes out ... :)

Updated by duerst (Martin Dürst) over 6 years ago

chucke (Tiago Cardoso) wrote:

module Refinary

If possible, please change the spelling to "Refinery".

Updated by shyouhei (Shyouhei Urabe) over 6 years 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.

Updated by matz (Yukihiro Matsumoto) over 6 years 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 vegetables and 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, Module#inject. Unlike include, inject does not modify inheritance hierarchy. Instead, it copies attributes (constants, modules, and refinements) into the target class/module.

shevegen,
We have no concrete plan to refine the refinement. We are vaguely thinking about combining require and using in some way. Just idea.

Matz.

Updated by chucke (Tiago Cardoso) over 6 years 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.

Actions #10

Updated by nobu (Nobuyoshi Nakada) over 6 years ago

  • Related to Bug #14012: NameError is raised when use class variables in Refinements added

Updated by jeremyevans0 (Jeremy Evans) over 2 years ago

  • Status changed from Assigned to Closed

Refinement#include has been deprecated. Refinement#import_methods can now be used to handle this use case:

module Extensions
  def vegetables ; potatoe ; end
  def potatoe ; "potatoe" ; end
end

module Refinary
  refine String do
    import_methods Extensions
  end
end

using Refinary
p ''.vegetables 
# => "potatoe"

Updated by chucke (Tiago Cardoso) over 2 years ago

Thx for the reply. Is import_methods a new ruby 3.1 feature?

Updated by jeremyevans0 (Jeremy Evans) over 2 years ago

chucke (Tiago Cardoso) wrote in #note-12:

Thx for the reply. Is import_methods a new ruby 3.1 feature?

Yes. See 6606597109bdb535a150606323ce3d8f5750e1f6

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0