From: daniel@...42.com Date: 2020-10-22T13:07:53+00:00 Subject: [ruby-core:100502] [Ruby master Feature#16461] Proc#using Issue #16461 has been updated by Dan0042 (Daniel DeLorme). Eregon (Benoit Daloze) wrote in #note-10: > Maybe a way to design this restriction could be to annotate the method `where` with "enables MyRefinement on the lexical block given to it". I like this idea. Since the block should always have the same refinements, it makes sense to define them once rather than every time the block is called. How about ```ruby class ModelClassMethods using ActiveRecord::WhereDSL, def where(&block) ... block.call / yield end end ``` ---------------------------------------- Feature #16461: Proc#using https://bugs.ruby-lang.org/issues/16461#change-88122 * Author: shugo (Shugo Maeda) * Status: Assigned * Priority: Normal * Assignee: matz (Yukihiro Matsumoto) * Target version: 3.0 ---------------------------------------- ## Overview I propose Proc#using to support block-level refinements. ```ruby module IntegerDivExt refine Integer do def /(other) quo(other) end end end def instance_eval_with_integer_div_ext(obj, &block) block.using(IntegerDivExt) # using IntegerDivExt in the block represented by the Proc object obj.instance_eval(&block) end # necessary where blocks are defined (not where Proc#using is called) using Proc::Refinements p 1 / 2 #=> 0 instance_eval_with_integer_div_ext(1) do p self / 2 #=> (1/2) end p 1 / 2 #=> 0 ``` ## PoC implementation For CRuby: https://github.com/shugo/ruby/pull/2 For JRuby: https://github.com/shugo/jruby/pull/1 ## Background I proposed [Feature #12086: using: option for instance_eval etc.](https://bugs.ruby-lang.org/issues/12086) before, but it has problems: * Thread safety: The same block can be invoked with different refinements in multiple threads, so it's hard to implement method caching. * _exec family support: {instance,class,module}_exec cannot be supported. * Implicit use of refinements: every blocks can be used with refinements, so there was implementation difficulty in JRuby and it has usability issue in headius's opinion. ## Solutions in this proposal ### Thread safety Proc#using affects the block represented by the Proc object, neither the specific Proc object nor the specific block invocation. Method calls in a block are resolved with refinements which are used by Proc#using in the block at the time. Once all possible refinements are used in the block, there is no need to invalidate method cache anymore. See [these tests](https://github.com/shugo/ruby/pull/2/commits/1c922614ad7d1fb43b73e195348c81da7a4546ef) to understand how it works. Which refinements are used is depending on the order of Proc#using invocations until all Proc#using calls are finished, but eventually method calls in a block are resolved with the same refinements. ### * _exec family support [Feature #12086](https://bugs.ruby-lang.org/issues/12086) was an extension of _eval family, so it cannot be used with _exec family, but Proc#using is independent from _eval family, and can be used with _exec family: ```ruby def instance_exec_with_integer_div_ext(obj, *args, &block) block.using(IntegerDivExt) obj.instance_exec(*args, &block) end using Proc::Refinements p 1 / 2 #=> 0 instance_exec_with_integer_div_ext(1, 2) do |other| p self / other #=> (1/2) end p 1 / 2 #=> 0 ``` ### Implicit use of refinements Proc#using can be used only if `using Proc::Refinements` is called in the scope of the block represented by the Proc object. Otherwise, a RuntimeError is raised. There are two reasons: * JRuby creates a special CallSite for refinements at compile-time only when `using` is called at the scope. * When reading programs, it may help understanding behavior. IMHO, it may be unnecessary if libraries which uses Proc#using are well documented. `Proc::Refinements` is a dummy module, and has no actual refinements. -- https://bugs.ruby-lang.org/ Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>