[#101981] [Ruby master Bug#17519] set_visibility fails when a prepended module and a refinement both exist — dbfeldman@...

Issue #17519 has been reported by fledman (David Feldman).

12 messages 2021/01/08

[#102003] [Ruby master Bug#17527] rb_io_wait_readable/writable with scheduler don't check errno — julien@...

SXNzdWUgIzE3NTI3IGhhcyBiZWVuIHJlcG9ydGVkIGJ5IHlzYmFkZGFkZW4gKEp1bGllbiBQb3J0

13 messages 2021/01/11

[#102008] [Ruby master Bug#17529] Ractor Segfaults with GC enabled — sin@...

Issue #17529 has been reported by prajjwal (Prajjwal Singh).

9 messages 2021/01/12

[#102065] [Ruby master Bug#17536] Segfault in `CFUNC :define_method` — v.ondruch@...

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

13 messages 2021/01/13

[#102083] [Ruby master Bug#17540] A segfault due to Clang/LLVM optimization on 32-bit ARM Linux — xtkoba+ruby@...

Issue #17540 has been reported by xtkoba (Tee KOBAYASHI).

12 messages 2021/01/14

[#102102] [Ruby master Bug#17543] Ractor isolation broken by `self` in shareable proc — marcandre-ruby-core@...

Issue #17543 has been reported by marcandre (Marc-Andre Lafortune).

14 messages 2021/01/15

[#102118] [Ruby master Feature#17548] Need simple way to include symlink directories in Dir.glob — keithrbennett@...

Issue #17548 has been reported by keithrbennett (Keith Bennett).

8 messages 2021/01/17

[#102158] [Ruby master Bug#17560] Does `Module#ruby2_keywords` return `nil` or `self`? — nobu@...

Issue #17560 has been reported by nobu (Nobuyoshi Nakada).

9 messages 2021/01/19

[#102163] [Ruby master Bug#17561] The timeout option for Addrinfo.getaddrinfo is not reliable on Ruby 2.7.2 — sean@...

Issue #17561 has been reported by smcgivern (Sean McGivern).

8 messages 2021/01/19

[#102249] [Ruby master Bug#17583] Segfault on large stack(RUBY_THREAD_VM_STACK_SIZE) — yoshiokatsuneo@...

Issue #17583 has been reported by yoshiokatsuneo (Tsuneo Yoshioka).

12 messages 2021/01/26

[#102256] [Ruby master Bug#17585] DWAR5 support? — v.ondruch@...

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

19 messages 2021/01/26

[#102301] [Ruby master Bug#17591] Test frameworks and REPLs do not show deprecation warnings by default — eregontp@...

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

14 messages 2021/01/29

[#102305] [Ruby master Feature#17592] Ractor should allowing reading shareable class instance variables — marcandre-ruby-core@...

Issue #17592 has been reported by marcandre (Marc-Andre Lafortune).

25 messages 2021/01/29

[ruby-core:102128] [Ruby master Feature#17551] Pattern Matching - Default Object#deconstruct and Object#deconstruct_keys

From: marcandre-ruby-core@...
Date: 2021-01-18 04:54:20 UTC
List: ruby-core #102128
Issue #17551 has been updated by marcandre (Marc-Andre Lafortune).


Your example would have `deconstruct*` methods had it subclassed `Struct.new(:name, :age, :children)` (even though there are many things I personally dislike about `Struct`)

I don't think there is a sensible default for either methods, other than maybe use `to_ary` and `to_hash` if those existed. I believe the builtin classes that define these also define `deconstruct*`.

More importantly, even if there was a good default, it is too late to change it as the incompatibility could be enormous. `Hash` defines `to_a` but does not define `deconstruct` (for good reasons imo), but your proposal would. Code that does not match in 3.0 might suddenly match in 3.1, or worse cause some strange side effects (immagine trying to match with `clear:` or `freeze:` keys...)

I think it is best to leave no default and we don't have a choice anyways. Instead, let's open issues with those gems that would benefit from defining `deconstruct*`...

----------------------------------------
Feature #17551: Pattern Matching - Default Object#deconstruct and Object#deconstruct_keys
https://bugs.ruby-lang.org/issues/17551#change-89982

* Author: baweaver (Brandon Weaver)
* Status: Open
* Priority: Normal
----------------------------------------
Pattern Matching is a very powerful feature, but many classes are not able to enjoy its functionality due to the lacking of a default `deconstruct` and `deconstruct_keys` method being present.

This feature request is to introduce a series of sane defaults, and what they might look like. What these defaults are or should be is up for debate, but I would like to propose a few ideas to get things started.

## Reasonable Defaults

### The Fast Version

I will elaborate on this in the below content, but the fast version of my proposal is:

1. `deconstruct_keys` should default to a classes public API
2. `deconstruct` should default to being an alias of `to_a` or other `Array` coercion methods

### Deconstruct Keys

`deconstruct_keys` is used for extracting values out of an object in use with a `Hash`-like pattern match. In the case of a literal `Hash` with `Symbol` keys the deconstructed keys are extracted from the `Hash`.

My proposal would be to base the default `deconstruct_keys` on the attributes of an object as defined by `attr_*` methods. Consider this `Person` class:

```ruby
class Person
  attr_reader :name, :age, :children
  
  def initialize(name:, age:, children: [])
    @name     = name
    @age      = age
    @children = children
  end
end
```

The attributes exposed by the proposed default `deconstruct_keys` would be `name`, `age`, and `children`.

As `attr_reader` has made these values public they are the interface into the class, meaning this will not break encapsulation of values and relies on the already established API it provides.

In current Ruby this behavior can be approximated as seen here in a test gem I call Dio: https://github.com/baweaver/dio#attribute-forwarder

It does a comparison of instance variables versus all methods to find public readers:

```ruby
ivars = Set.new base_object
  .instance_variables
  .map { _1.to_s.delete('@').to_sym }

all_methods = Set.new base_object.methods

attributes = ivars.intersection(all_methods)
```

Which allows me to do this:

```ruby
Person.new(
  name: 'Alice',
  age: 40,
  children: [
    Person.new(name: 'Jim', age: 10),
    Person.new(name: 'Jill', age: 10)
  ]
)

case Dio.attribute(alice)
in { name: /^A/, age: 30..50 }
  true
else
  false
end

case Dio.attribute(alice)
in { children: [*, { name: /^J/ }, *] }
  true
else
  false
end
```

My list of ideas for this default `deconstruct_keys` method are:

1. `attr_` based - Any exposed attribute
2. public method based (`public_send`) - All public methods on the class
3. all methods (`send`) - Every potential method

I believe the first is the most conservative and Ruby-like, as well as the least surprising. A case could be made for the second which allows for more flexibility and remains within the encapsulation of the class. The third is more unrealistic as it exposes everything.

I would like to discuss between the first two.

### Deconstruct

`deconstruct` is used for extracting values out of an object in use with an `Array`-like pattern match. In the case of an `Array` the values are returned directly.

My proposal would be to base the default `deconstruct` on the Ruby concept of Duck typing through `to_a` or `Enumerable`:

```ruby
module Enumerable
  alias_method :deconstruct, :to_a
end
```

Consider this `Node` class:

```ruby
class Node
  attr_reader :value, :children

  def initialize(value, *children)
    @value    = value
    @children = children
  end

  def to_a() = [@value, @children]

  def self.[](...) = new(...)
end
```

It is `Array`-like in nature, and through `to_a` we could infer `deconstruct` instead of explicitly requiring a method:

```ruby
tree = Node[1,
  Node[2, Node[3, Node[4]]],
  Node[5],
  Node[6, Node[7], Node[8]]
]

case tree
in [1, [*, [5, _], *]]
  true
else
  false
end
```

I believe this is a good use of duck typing, and presents a reasonable default. If no `Array` coercion methods are available it would make sense that it cannot be pattern matched against like an Array.

My proposal here is to use the established `to_a` or other `Array` coercion methods to imply `deconstruct`

## Why Defaults?

Many Ruby gems and code do not implement `deconstruct` or `deconstruct_keys`, meaning pattern matching cannot be used against them easily. This change will allow for pattern matching against Ruby code from any generation, and open up the feature to far more use across code bases.

I believe this feature would not be substantial work to implement, but will have substantial gains for all Ruby code.

Thank you for your time in reading, and I apologize for another long feature request.



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