Bug #18385
closedRefinement#import_methods(Enumerable) doesn't work
Added by zverok (Victor Shepelev) almost 3 years ago. Updated almost 3 years ago.
Description
Very simple to reproduce:
module M
refine String do
import_methods Enumerable
end
end
Leads to: import_methods': Can't import method: Enumerable#drop (ArgumentError)
Which, grepping through code, seems to be raised here but I am not versed enough in Ruby internals to debug further.
An attempt to import_methods Comparable
leads to the same problem, BTW: Can't import method: Comparable#between?
Am I missing something crucial about import_method
behavior?
Updated by Eregon (Benoit Daloze) almost 3 years ago
- Assignee set to shugo (Shugo Maeda)
It means you can't import methods defined in C, only methods defined with Ruby code.
Methods need to be defined in Ruby code to be affected by refinements (e.g. other refinements in that refine
block or under M
).
So for example this wouldn't work if you define each
inside refine String do
since those C methods wouldn't know how to find each
with refinements (e.g., they use rb_funcall
which doesn't know about refinements).
(it would work on Ruby implementations where Enumerable is defined in Ruby code, interestingly)
@shugo (Shugo Maeda) WDYT?
I think we should improve the error message, like:
Can't import method which is not defined with Ruby code: Comparable#between?
Updated by zverok (Victor Shepelev) almost 3 years ago
Yeah, at least the message should be clearer.
I didn't realize that import_methods
is a make-believe feature, not really integrated with the interpreter/object model.
Are there any plans to integrate it more tightly, with refinements being "visible" to C code?..
Updated by shugo (Shugo Maeda) almost 3 years ago
- Status changed from Open to Assigned
Eregon (Benoit Daloze) wrote in #note-1:
It means you can't import methods defined in C, only methods defined with Ruby code.
Methods need to be defined in Ruby code to be affected by refinements (e.g. other refinements in thatrefine
block or underM
).
So for example this wouldn't work if you defineeach
insiderefine String do
since those C methods wouldn't know how to findeach
with refinements (e.g., they userb_funcall
which doesn't know about refinements).
(it would work on Ruby implementations where Enumerable is defined in Ruby code, interestingly)@shugo (Shugo Maeda) WDYT?
Yes, it's an implementation issue.
I think we should improve the error message, like:
Can't import method which is not defined with Ruby code: Comparable#between?
Thank you. I'll fix the error message.
zverok (Victor Shepelev) wrote in #note-2:
Yeah, at least the message should be clearer.
I didn't realize that
import_methods
is a make-believe feature, not really integrated with the interpreter/object model.Are there any plans to integrate it more tightly, with refinements being "visible" to C code?..
Currently I have no plan and afraid that it may bring another implementation difficulty.
Updated by shugo (Shugo Maeda) almost 3 years ago
- Status changed from Assigned to Closed
Applied in changeset git|c2192cb985c10c90ba5e4d64652f79f89afff983.
Clarify the error message when trying to import C methods [Bug #18385]
Updated by Eregon (Benoit Daloze) almost 3 years ago
zverok (Victor Shepelev) wrote in #note-2:
I didn't realize that
import_methods
is a make-believe feature, not really integrated with the interpreter/object model.
Not sure what you mean by that, in my POV it is integrated and it has the effect to "copy" methods from a module to another module or to a refinement, so that refinements can apply as if they were defined there lexically.
C extension-defined methods never supported refinements and likely never will (there is no lexical context for a call there).
Updated by zverok (Victor Shepelev) almost 3 years ago
Eregon (Benoit Daloze) wrote in #note-5:
zverok (Victor Shepelev) wrote in #note-2:
I didn't realize that
import_methods
is a make-believe feature, not really integrated with the interpreter/object model.Not sure what you mean by that, in my POV it is integrated and it has the effect to "copy" methods from a module to another module or to a refinement, so that refinements can apply as if they were defined there lexically.
C extension-defined methods never supported refinements and likely never will (there is no lexical context for a call there).
Well... We can look from many different angles at the problem.
From one side, I can understand the technical reasons how you explain them.
On another side, I still "intuitively" expect refinements to be a hygienic replacement for core_ext.rb
, and every time this expectation fails, I am a bit frustrated. I mean, I might do that, right?.. (Save for the sanity of this approach discussion, but imagine I hack a few scripts for quick binary data investigation and decided in those scripts it would be the most convenient):
class String
alias each each_byte
include Enumerable
end
[1, 2, 3].chain('123').zip('abcdef')
# => [[1, 97], [2, 98], [3, 99], [49, 100], [50, 101], [51, 102]]
...but I can't for the love of all gods achieve the same with refinements—which would be the very first guess if you do something so violent you need in exactly one algorithm.
That's why I call import_methods
a "make-believe" feature: unlike include
/extend
(which didn't work in refinements) it makes the border between methods defined in Ruby and methods defined in C visible, and thus look like something loosely glued on top of the object model.
It can be "technically explained", but it doesn't become "natural" after the explanation.
But honestly, I feel like the whole mental model of refinements is doomed from the start, the limitation of the "additional" functionality to text file borders is unusable in a dynamic language.
(As an aside note, it is funny how "extension methods" became in fashion in all the languages that criticized Ruby for being "too unreliable" for being able to extend core classes—and at the same time, in the Ruby community, it became a taboo.)