Enumberable fails for class Subclass < Array; def enum

Array includes Enumerable. But when a subclass of Array redefines
each(), then Enumerable still refers to the original Array.each(). Why
is that?
For example:

class RangeUnion < Array
def each
super{|x| x.each{|i| yield i} }
end
end

u = RangeUnion[1…3, 11…13, “a”…“c”]

u.each{|x| print "#{x} "} # THIS WORKS

#=> 1 2 3 11 12 13 a b c

u.collect{|x| x} # THIS FAILS!!!

#=> [1, 2, 3, 11, 12, 13, “a”, “b”, “c”] # expected

#=> [1…3, 11…13, “a”…“c”] # actual

In the example above, each() is redefined. But collect() does not use
the redefined each()!!! It uses Array.each(). Subclassing from some
other class works fine:

class RangeUnion < NotAnArray; … # gives expected result

It seems that Enumerable is hardwired (built in) in Array. But subclass
should be able to override it. Why isn’t that possible?

-Laza

Hi,

At Mon, 10 Jul 2006 15:05:11 +0900,
Laza wrote in [ruby-talk:201045]:

class RangeUnion < Array
define_method(:collect, Enumerable.instance_method(:collect))

Laza wrote:

other class works fine:

class RangeUnion < NotAnArray; … # gives expected result

It seems that Enumerable is hardwired (built in) in Array. But subclass
should be able to override it. Why isn’t that possible?

I’ve experienced problems myself when trying to redefine some of
Enumerable’s methods. Apparently Array uses its own #collect and #map
methods, plus some others. Maybe if you explicitly re-include Enumerable
into your subclass?

Cheers,
Daniel

Laza wrote:

Array includes Enumerable. But when a subclass of Array redefines
each(), then Enumerable still refers to the original Array.each(). Why
is that?

Your confusion is understandable but you’ve got the problem backward:
that
behavior is not because Enumerable uses Array’s each, it’s because Array
defines
its own version of collect. Look at Array’s local (non-inherited)
methods:

[].public_methods(false).sort
=> ["&", “*”, “+”, “-”, “<<”, “<=>”, “==”, “[]”, “[]=”, “assoc”, “at”,
“clear”,
“collect”, “collect!”, “compact”, “compact!”, “concat”, “delete”,
“delete_at”,
“delete_if”, “each”, “each_index”, “empty?”, “eql?”, “fetch”, “fill”,
“first”,
“flatten”, “flatten!”, “frozen?”, “hash”, “include?”, “index”,
“indexes”,
“indices”, “insert”, “inspect”, “join”, “last”, “length”, “map”, “map!”,
“nitems”, “pack”, “pop”, “push”, “rassoc”, “reject”, “reject!”,
“replace”,
“reverse”, “reverse!”, “reverse_each”, “rindex”, “select”, “shift”,
“size”,
“slice”, “slice!”, “sort”, “sort!”, “to_a”, “to_ary”, “to_s”,
“transpose”,
“uniq”, “uniq!”, “unshift”, “values_at”, “zip”, “|”]

I suppose it was done that way for optimization. Why do you need to
subclass
Array, anyway?

Daniel

re-including Enumerable does not help. I re-implemented the class by
having an Array member variable instead of inheriting from Array class.
Apparently, not all classes are created equal in Ruby.