Set operations check for is_a?(Set), rather than allowing duck typing
Hello there 👋
Hash, cannot easily interoperate with user-created classes as several operations (
#proper_superset?) check that the other class
is_a?(Set), rather than allowing duck-typing.
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
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-💎. This gem provides immutable arrays, hashes and sets. Most of the hard work is delegated to another gem (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.
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 are a pretty good proof of it.
Thanks for the time 🙏, and rock on 💪!