[ruby-core:98268] [Ruby master Feature#16461] Proc#using
From:
ihdadi.page@...
Date:
2020-05-11 21:41:32 UTC
List:
ruby-core #98268
Issue #16461 has been updated by [email protected] (منصة شليله). shugo (Shugo Maeda) wrote: > ## 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. منصةشليله ---------------------------------------- Feature #16461: Proc#using https://bugs.ruby-lang.org/issues/16461#change-85506 * Author: shugo (Shugo Maeda) * Status: Open * Priority: Normal * Target version: 2.8 ---------------------------------------- ## 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:[email protected]?subject=unsubscribe> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>