Issue #15240 has been updated by shevegen (Robert A. Heiler).


From my own experience there is often a very good chance for extending duck typing
possibilities in ruby if there is a specific (or at the least potential rather
than abstract) use case.

You suggested the replacement (e. g. from going to .is_a? towards .respond_to? and
I believe if there is no reason against it, then this may have a high chance to 
be accepted. Of course I can not speak for anyone else; I merely base this on my
own experience in regards to ruby's duck typing opportunities (matz has lots of
duck-pictures in his presentations too).

I would suggest to have this issue take some days for others to comment on it
and chime in; and then, if you would like to, to consider adding it towards the
next developer meeting at:

  https://bugs.ruby-lang.org/issues/15229

That way may be the best to get feedback from matz and perhaps also approval;
and at the very least some discussion, which may help resolve other parts that
may require more information (but I think in this case, it is a rather simple
issue request, so I believe there is not that much that may have to be discussed
e. g. compared to larger issue requests such as adding pattern matching into
ruby and similar).

----------------------------------------
Bug #15240: Set operations check for is_a?(Set), rather than allowing duck typing
https://bugs.ruby-lang.org/issues/15240#change-74554

* Author: ivoanjo (Ivo Anjo)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
* ruby -v: ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-linux]
* Backport: 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN
----------------------------------------
Hello there 

Ruby's `Set`, unlike `Array` or `Hash`, cannot easily interoperate with user-created classes as several operations (`#==`, `#flatten`, `#flatten!`, `#intersect?`, `#disjoint?`, `#subset?`, `#proper_subset?`, `#superset?`, `#proper_superset?`) check that the other class `is_a?(Set)`, rather than allowing duck-typing.

Example:

```ruby
require 'set'
class MySet
  include Enumerable
  def each(&block) [:my, :set].each(&block) end
  def size() to_a.size end
end
puts Set[:set].subset?(MySet.new)

=> Traceback (most recent call last):
        1: from testcase.rb:8:in `<main>'
set.rb:292:in `subset?': value must be a set (ArgumentError)
```

The only way I've found of going around this issue and looking at the Ruby sources, is to fake a response to `is_a?`:

```ruby
require 'set'
class MySet
  include Enumerable
  def each(&block) [:my, :set].each(&block) end
  def size() to_a.size end
  def is_a?(klass) super || klass == Set end # <== Hack! 
end
puts Set[:set].subset?(MySet.new)

=> true
```

This is a very ugly hack, and instead it would be nice if, instead, I could just provide a `to_set` method that `Set` could call to allow duck typing.

I'm willing to work on a patch to solve this (would be pretty nice to do my first contribution to Ruby core!), so hopefully we can discuss how this problem can be tackled.

---

### Background / TL;DR

This issue came about as I am the creator of a gem called [persistent-](https://gitlab.com/ivoanjo/persistent-dmnd/). This gem provides immutable arrays, hashes and sets. Most of the hard work is delegated to another gem ([hamster](https://github.com/hamstergem/hamster)), but I've added a number of tweaks to allow the persistent- variants to easily interoperate with their Ruby counterparts.

Because I wanted to allow `Persistent::Set` instances to be used together with Ruby's `Set`, I studied the `set.rb` implementation and came up with the `is_a?(Set)` hack above. This works on all Ruby versions the gem supports (1.9->2.6), but broke on JRuby 9.2 when a new optimized `Set` implementation was added, that did not do the `is_a?(Set)` check and thus broke the hack.

I've brought up this issue with the JRuby developers -- <https://github.com/jruby/jruby/issues/5227> -- and from there we moved the discussion to ruby/spec -- <https://github.com/ruby/spec/pull/629>.

We ended up concluding that it would make sense to raise this on the Ruby tracker as something that should be fixed on `Set` itself, rather than codifying this hack as something that Ruby is expected to support.

Since Ruby sets already support an implicit conversion method -- `to_set` -- it seems natural to replace the `is_a?(Set)` with some kind of `other.respond_to?(:to_set) && other = other.to_set` in all places where the `is_a?(Set)` was being used. Note that his would be all that's needed to be able to use a `Set` duck-type --- the [`Persistent::Set` specs](https://gitlab.com/ivoanjo/persistent-dmnd/blob/master/spec/unit/set_spec.rb) are a pretty good proof of it.

Thanks for the time , and rock on !



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