ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:98268] [Ruby master Feature#16461] Proc#using
       [not found] <redmine.issue-16461.20191228033234.12@ruby-lang.org>
@ 2020-05-11 21:41 ` ihdadi.page
  2020-08-08  1:30 ` [ruby-core:99512] " shugo
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 6+ messages in thread
From: ihdadi.page @ 2020-05-11 21:41 UTC (permalink / raw)
  To: ruby-core

Issue #16461 has been updated by ihdadi.page@gmail.com (منصة شليله).


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/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:99512] [Ruby master Feature#16461] Proc#using
       [not found] <redmine.issue-16461.20191228033234.12@ruby-lang.org>
  2020-05-11 21:41 ` [ruby-core:98268] [Ruby master Feature#16461] Proc#using ihdadi.page
@ 2020-08-08  1:30 ` shugo
  2020-09-05 11:05 ` [ruby-core:99938] " dementiev.vm
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 6+ messages in thread
From: shugo @ 2020-08-08  1:30 UTC (permalink / raw)
  To: ruby-core

Issue #16461 has been updated by shugo (Shugo Maeda).

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

shugo (Shugo Maeda) wrote in #note-5:
> > I think that could be a good rule, all `Proc#using` must happen `before Proc#call` for a given block.
> > That would mean the code inside a block always calls the same methods (i.e., the refinements for a given call site are fixed), which seems essential to me for sanity when reading code.
> 
> OK, I'll try to fix PoC implementation.

Sorry for the delay.
I've fixed the CRuby implementation:

https://github.com/shugo/ruby/pull/2


----------------------------------------
Feature #16461: Proc#using
https://bugs.ruby-lang.org/issues/16461#change-86970

* Author: shugo (Shugo Maeda)
* Status: Assigned
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
* 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/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:99938] [Ruby master Feature#16461] Proc#using
       [not found] <redmine.issue-16461.20191228033234.12@ruby-lang.org>
  2020-05-11 21:41 ` [ruby-core:98268] [Ruby master Feature#16461] Proc#using ihdadi.page
  2020-08-08  1:30 ` [ruby-core:99512] " shugo
@ 2020-09-05 11:05 ` dementiev.vm
  2020-09-05 11:49 ` [ruby-core:99940] " eregontp
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 6+ messages in thread
From: dementiev.vm @ 2020-09-05 11:05 UTC (permalink / raw)
  To: ruby-core

Issue #16461 has been updated by palkan (Vladimir Dementyev).


Disclaimer: I'm a big fan of refinements; they make Ruby more expressive (or chaotic 😉) and allow developers to be more creative.

shugo (Shugo Maeda) wrote in #note-5:
> I admit that the behavior is complex, but DSL users need not
> understand details.

Unless they have to debug the code using this DSL, understanding the details leads to better code; IMO, Rails has enough magic to blow newcomers' minds.

At the same time, Refinements are already too complex for most developers, even if we only deal with lexical scope and explicit activation. With `Proc#using`, we will likely make Refinements a secret knowledge only available to magicians.

> I understand the intent, and not willing to have using ActiveRecord::DSL at the top-level as it would then override Symbol#== everywhere in that file.

What about introducing `unusing` or similar instead? 

```ruby
class User < ActiveRecord::Base
  using ActiveRecord::WhereDSL

  scope :not_so_young, -> { where { :age >= 30 } } 

  # deactivate refinement below this line
  unusing ActiveRecord::WhereDSL
end
```

Looks a bit uglier but, IMO, much easier to understand for end-users, especially if they're familiar with refinements: we still use lexical scope, nothing new here.

Another idea is to pass block to `using` (so, it's gonna be a mix of `instance_eval` + Proc#using, I think):

```ruby
using ActiveRecord::WhereDSL do
  scope :not_so_young, -> { where { :age >= 30 } }
end
```


----------------------------------------
Feature #16461: Proc#using
https://bugs.ruby-lang.org/issues/16461#change-87478

* Author: shugo (Shugo Maeda)
* Status: Assigned
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
* 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/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:99940] [Ruby master Feature#16461] Proc#using
       [not found] <redmine.issue-16461.20191228033234.12@ruby-lang.org>
                   ` (2 preceding siblings ...)
  2020-09-05 11:05 ` [ruby-core:99938] " dementiev.vm
@ 2020-09-05 11:49 ` eregontp
  2020-10-22 10:58 ` [ruby-core:100500] " eregontp
  2020-10-22 13:07 ` [ruby-core:100502] " daniel
  5 siblings, 0 replies; 6+ messages in thread
From: eregontp @ 2020-09-05 11:49 UTC (permalink / raw)
  To: ruby-core

Issue #16461 has been updated by Eregon (Benoit Daloze).


I think it would be good to get @headius' feedback on this.

I'd also like to take another detailed look (I won't be able before 14 September though).

----------------------------------------
Feature #16461: Proc#using
https://bugs.ruby-lang.org/issues/16461#change-87480

* Author: shugo (Shugo Maeda)
* Status: Assigned
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
* 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/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:100500] [Ruby master Feature#16461] Proc#using
       [not found] <redmine.issue-16461.20191228033234.12@ruby-lang.org>
                   ` (3 preceding siblings ...)
  2020-09-05 11:49 ` [ruby-core:99940] " eregontp
@ 2020-10-22 10:58 ` eregontp
  2020-10-22 13:07 ` [ruby-core:100502] " daniel
  5 siblings, 0 replies; 6+ messages in thread
From: eregontp @ 2020-10-22 10:58 UTC (permalink / raw)
  To: ruby-core

Issue #16461 has been updated by Eregon (Benoit Daloze).


Reading #12086 again, I feel #12281 is a much simpler alternative (`using(refinement) { literal block }`).
Wouldn't #12281 be enough?

`block.using(IntegerDivExt)` is mutating state and this feels too magic and dangerous to me.
I think DSL users can accept to wrap their DSL code with `using(refinement) { ... }` (or `using refinement` in a module/file).
That's actually not more verbose than `using Proc::Refinements` and I think so much clearer.

We could have additional requirements to ensure it's truly lexical and the `refinement` given to a given `using(refinement) { ... }` call site is always the same.
I think that would be good, because then we know code inside always use the given refinements, much like how `using refinement` works today.

For the `User.where { :age > 3 }` case, if we want to avoid an extra `using(refinement) { ... }`, I think it could be fine to automatically enable refinements *if* we can ensure it's still fully lexical.
Concretely that would mean for that block `{ :age > 3 }` it's always called with the same refinements, no matter where it's called from.
Attempting to use any other refinement would raise an error.

Maybe a way to design this restriction could be to annotate the method `where` with "enables MyRefinement on the lexical block given to it".
```ruby
class ModelClassMethods
  def where(&block)
    ...
    block.call / yield
  end
  instance_method(:where).use_refinements_for_block(ActiveRecord::WhereDSL)
end

User.where { :age > 3 }
```

That way I think we can ensure the given literal block always has the exact same refinements.
Multiple `use_refinements_for_block` calls for a method would be an error.
And `use_refinements_for_block` should be used before the method is called (so there is no block before executed without refinements).

Implementation-wise, `where` would modify the refinements for the block/Proc, before executing the body of `where`, so it would all be transparent to the user.

----------------------------------------
Feature #16461: Proc#using
https://bugs.ruby-lang.org/issues/16461#change-88120

* 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/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:100502] [Ruby master Feature#16461] Proc#using
       [not found] <redmine.issue-16461.20191228033234.12@ruby-lang.org>
                   ` (4 preceding siblings ...)
  2020-10-22 10:58 ` [ruby-core:100500] " eregontp
@ 2020-10-22 13:07 ` daniel
  5 siblings, 0 replies; 6+ messages in thread
From: daniel @ 2020-10-22 13:07 UTC (permalink / raw)
  To: ruby-core

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/

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-10-22 13:08 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <redmine.issue-16461.20191228033234.12@ruby-lang.org>
2020-05-11 21:41 ` [ruby-core:98268] [Ruby master Feature#16461] Proc#using ihdadi.page
2020-08-08  1:30 ` [ruby-core:99512] " shugo
2020-09-05 11:05 ` [ruby-core:99938] " dementiev.vm
2020-09-05 11:49 ` [ruby-core:99940] " eregontp
2020-10-22 10:58 ` [ruby-core:100500] " eregontp
2020-10-22 13:07 ` [ruby-core:100502] " daniel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).