Sylvain Joyeux <sylvain.joyeux@polytechnique.org> writes:
I have a class inheriting Array,
Hi Sylvain,
I'm sorry, but this is asking for trouble.
and I expected slice() and to return Array objects.
I think I'd expect them to return instances of the derived class.
Surely a subrange of a Foo is also a Foo?
Unfortunately
irb(main):001:0> class Expression < Array
irb(main):002:1> end
=> nil
irb(main):003:0> test = Expression.new
=>
irb(main):004:0> test.concat( (1..10).to_a )
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):005:0> test.slice(1..5)
=> [2, 3, 4, 5, 6]
irb(main):006:0> test.slice(1..5).class
=> Expression
And I don't understand why ...
Looking through array.c, it's pretty easy to see why this happens on a
purely mechanical level --- I'll walk you through it.
We'll start in `Init_Array':
rb_define_method(rb_cArray, "", rb_ary_aref, -1);
rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);
If we call this method with two arguments, the following will happen:
VALUE
rb_ary_aref(argc, argv, ary)
int argc;
VALUE *argv;
VALUE ary;
{
VALUE arg;
long beg, len;
if (argc == 2) {
[...]
beg = NUM2LONG(argv[0]);
len = NUM2LONG(argv[1]);
[...]
return rb_ary_subseq(ary, beg, len);
}
[...]
}
static VALUE
rb_ary_subseq(ary, beg, len)
VALUE ary;
long beg, len;
{
VALUE klass, ary2, shared;
[...]
klass = rb_obj_class(ary);
[...]
ary2 = ary_alloc(klass);
[...]
return ary2;
}
static VALUE
ary_alloc(klass)
VALUE klass;
{
NEWOBJ(ary, struct RArray);
OBJSETUP(ary, klass, T_ARRAY);
[...]
return (VALUE)ary;
}
#define OBJSETUP(obj,c,t) do {\
RBASIC(obj)->flags = (t);\
RBASIC(obj)->klass = (c);\
if (rb_safe_level() >= 3) FL_SET(obj, FL_TAINT);\
} while (0)
However, I'm ignorant about the rationale of this behavior.
Moreover, since my Expression class has instance variables, the
instance produced by slice() does not have it anymore (which is bad)
That is bad. But I'm not sure whether it could be easily fixed;
arrays are pretty special in Ruby. For example, when you say
foo[1..5], a new array is returned, but no elements are copied.
I wokarounded this by using Delegator
Any idea on why this behaviour ?
Although I agree that it would be nice if inheriting from Array worked
painlessly, that is unfortunately not (currently) the case.
If you are looking for practical advice, I say avoid inheriting from
core classes like Array, and stick with Delegator.
On the other hand, if you are seeking to discuss the possibilities of
changing how Array is implemented, don't let me stand in your way.
···
--
Daniel Brockman <daniel@brockman.se>
So really, we all have to ask ourselves:
Am I waiting for RMS to do this? --TTN.