[#105544] [Ruby master Feature#18239] Variable Width Allocation: Strings — "peterzhu2118 (Peter Zhu)" <noreply@...>

Issue #18239 has been reported by peterzhu2118 (Peter Zhu).

18 messages 2021/10/04

[#105566] [Ruby master Bug#18242] Parser makes multiple assignment sad in confusing way — "danh337 (Dan Higgins)" <noreply@...>

Issue #18242 has been reported by danh337 (Dan Higgins).

9 messages 2021/10/06

[#105573] [Ruby master Bug#18243] Ractor.make_shareable does not freeze the receiver of a Proc but allows accessing ivars of it — "Eregon (Benoit Daloze)" <noreply@...>

Issue #18243 has been reported by Eregon (Benoit Daloze).

11 messages 2021/10/06

[#105618] [Ruby master Bug#18249] The ABI version of dev builds of CRuby does not correspond to the ABI — "Eregon (Benoit Daloze)" <noreply@...>

Issue #18249 has been reported by Eregon (Benoit Daloze).

23 messages 2021/10/11

[#105626] [Ruby master Bug#18250] Anonymous variables seem to break `Ractor.make_shareable` — "tenderlovemaking (Aaron Patterson)" <noreply@...>

Issue #18250 has been reported by tenderlovemaking (Aaron Patterson).

14 messages 2021/10/12

[#105660] [Ruby master Feature#18254] Add an `offset` parameter to String#unpack and String#unpack1 — "byroot (Jean Boussier)" <noreply@...>

Issue #18254 has been reported by byroot (Jean Boussier).

13 messages 2021/10/18

[#105672] [Ruby master Feature#18256] Change the canonical name of Thread::Mutex, Thread::Queue, Thread::SizedQueue and Thread::ConditionVariable to just Mutex, Queue, SizedQueue and ConditionVariable — "Eregon (Benoit Daloze)" <noreply@...>

Issue #18256 has been reported by Eregon (Benoit Daloze).

6 messages 2021/10/19

[#105692] [Ruby master Bug#18257] SystemTap/DTrace coredump on ppc64le/s390x — "vo.x (Vit Ondruch)" <noreply@...>

Issue #18257 has been reported by vo.x (Vit Ondruch).

22 messages 2021/10/20

[#105781] [Ruby master Misc#18266] DevelopersMeeting20211118Japan — "mame (Yusuke Endoh)" <noreply@...>

Issue #18266 has been reported by mame (Yusuke Endoh).

13 messages 2021/10/25

[#105805] [Ruby master Bug#18270] Refinement#{extend_object, append_features, prepend_features} should be removed — "shugo (Shugo Maeda)" <noreply@...>

Issue #18270 has been reported by shugo (Shugo Maeda).

8 messages 2021/10/26

[#105826] [Ruby master Feature#18273] Class.subclasses — "byroot (Jean Boussier)" <noreply@...>

Issue #18273 has been reported by byroot (Jean Boussier).

35 messages 2021/10/27

[#105833] [Ruby master Feature#18275] Add an option to define_method to not capture the surrounding environment — "vinistock (Vinicius Stock)" <noreply@...>

Issue #18275 has been reported by vinistock (Vinicius Stock).

11 messages 2021/10/27

[#105853] [Ruby master Feature#18276] `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)` — "ko1 (Koichi Sasada)" <noreply@...>

Issue #18276 has been reported by ko1 (Koichi Sasada).

15 messages 2021/10/28

[ruby-core:105738] [Ruby master Bug#18021] Mixins in Refinements: possibly multiple bugs, workarounds are awkward

From: "jeremyevans0 (Jeremy Evans)" <noreply@...>
Date: 2021-10-21 16:13:19 UTC
List: ruby-core #105738
Issue #18021 has been updated by jeremyevans0 (Jeremy Evans).

Status changed from Open to Closed

Refinement#include is now deprecated and will be removed in Ruby 3.2.

----------------------------------------
Bug #18021: Mixins in Refinements: possibly multiple bugs, workarounds are awkward
https://bugs.ruby-lang.org/issues/18021#change-94237

* Author: josh.cheek (Josh Cheek)
* Status: Closed
* Priority: Normal
* ruby -v: ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [arm64-darwin20]
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN
----------------------------------------
## Maybe bug 1

Refinements must be created after methods are defined.

```ruby
# This one seems buggy
module M1
  refine(Object) { include M1 }
  def m() = M1
  using M1
  m rescue $! # => #<NameError: undefined local variable or method `m' for M1:Module>
end

# It works if you refine it after the method is defined
module M2
  def m() = M2
  refine(Object) { include M2 }
  using M2
  m # => M2
end

# If it wasn't in a refinement, it would work
module M3
  Object.class_eval { include M3 }
  def m() = M3
  m # => M3
end
```


## Maybe bug 2

Here, a module can't delegate to another method it defines, while using a refinement. Presumably this is because the refinement is lexically scoped and where the method is defined, it is not within that scope.

```ruby
module M
  def a() = 1
  def b() = a # ~> NameError: undefined local variable or method `a' for main:Object
  refine(Object) { include M }
end

using M
a # => 1
b # => 
```

## Maybe bug 3

Refinements declared after methods are defined are not available to those methods, even when they use the refinement. We try to use the module to address #2, but because of #1, the `refine` must go after the definitions.

```ruby
module M
  using M # <-- this line is added to the example from #2
  def a() = 1
  def b() = a # ~> NameError: undefined local variable or method `a' for main:Object
  refine(Object) { include M }
end

using M
a # => 1
b # => 
```

## Awkward workarounds:

So, `refine` depends on `def`, which depends on `using`, which depends on `refine`, which is where we started.


Here are several ways I found to break the cycle of dependance:

#### 1. "declare" methods by predefining them

```ruby
module M
  # Predefine method stubs
  def a() = nil
  def b() = nil

  # Now the refinement can see them, define the refinement
  refine(Object) { include M }

  # Now that the refinement exists, `using` will make it available
  using M

  # Now method definitions can see each other,
  # so overwrite the stubs with the real implementations
  def a() = 1
  def b() = a
end

# And now things work as expected
using M
a # => 1
b # => 1
```

#### 2. Put the module body in a loop

First iteration defines them without visibility to each other, because the refinement hasn't been made yet. On the second iteration, the refinement has been made, so the second time they're defined, they can see each other.

```ruby
module M
  2.times do
    using M # this can go either here or after `refine` below
    def a() = 1
    def b() = a
    refine(Object) { include M }
  end
end

# And now things work as expected
using M
a # => 1
b # => 1
```

#### 3. Define the methods in a block and call it before and after refining

```ruby
module M
  methods = lambda do
    using M
    def a() 1 end
    def b() a end
  end

  methods.call
  refine(Object) { include M }
  methods.call
end

using M
b # => 1
```

#### 4. (this does not work) Include the refinement into the module

Including this one, b/c it feels like maybe some sort of combination refinement/mixin might be what is needed. Like generally, methods defined in a refinement can see other methods, even across other blocks, if they're all on the same module. The problem is there isn't an obvious way to get them back out so that they're visible in the module outside of the refinement.

```ruby
module M
  include refine(M) { # ~> ArgumentError: refinement module is not allowed
    def a() = 1
    def b() = a
  }
  refine(Object) { include M }
end
```

## The problem with the obvious workaround

There is an obvious workaround: don't put them in a module, just refine directly:

```ruby
module M
  refine Object do
    def a() = 1
    def b() = a
  end
end
using M
b # => 1
```

However, then that behaviour isn't available for mixing into other classes and objects. So if I want both (which I often find I do), then there don't seem to be any good options available.

For example, can anyone come up with a better way to write the example below? (without moving `to_type` to `Object`, as that can't be expected to work for the next helper method).

```ruby
module Types
  2.times do
    using Types
    private def to_type(o) = (Symbol === o ? RespondTo.new(o) : o)
    def |(type) = Or.new(to_type(self), to_type(type))
    refine(Module) { include Types }
    refine(Symbol) { include Types }
  end

  RespondTo = Struct.new(:name) {
    def ===(o) o.respond_to? name end
    def inspect() = ".respond_to?(#{name.inspect})"
  }.include(Types)

  Or = Struct.new(:left, :right) {
    def ===(o) left === o || right === o end
    def inspect() = "(#{left.inspect} | #{right.inspect})"
  }.include(Types)
end

using Types
Integer | String | (:to_int | :to_str) 
# => ((Integer | String) | (.respond_to?(:to_int) | .respond_to?(:to_str)))
```



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

In This Thread

Prev Next