Uniform vector class, inheriting from Array: How to make sure thatmethods return a Vector and not an

T

Thomas

Hi folks,

I recently tried to implement a uniform vector class being defined as a
vector the elements of which all comply to some kind of common interface
or are a subclass of some prototype class.

My first naive approach was to inherit from Array and to overwrite some
methods to make sure that new elements are ok. This works fine for
methods like []=, <<, or unshift but when implementing + I ran into the
following problem: Array#+ returns an Array and not a Vector which is
why I have to create a new Vector from the result of Array#+. As Array#+
already creates a new Array, this probably isn't very efficient and I
don't like the idea of having to do this for all possible methods that
return an Array. I was hoping to be able to somehow limit my
modifications of Array to a few essential methods.

So, the question is: Does somebody know a way to make sure that methods
returning an Array (+, &, -, * etc.) always return a Vector without
having to redefine each of them?

Please find my current toy implementation down below.

Cheers,
Thomas.


class Vector < Array
def []=(pos, val)
check_values([val])
super
end

def +(values)
check_values(values)
Vector.new(super)
end

def <<(*values)
check_values(values)
super
end

def unshift(*values)
check_values(values)
super
end

def check_values(values)
unless defined?(@protoclass)
prototype = self[0] || values[0]
@protoclass = prototype.class
end
values.each do |e|
unless e.kind_of?(@protoclass)
raise TypeError, "Expected #{@protoclass} but got
#{e.class}", caller[2..-1]
end
end
end
end

if __FILE__ == $0
v = Vector.new
# these are okay
v << 1
v[2] = 2
p v + [1,2,3]
v += [1,2,3]
v.unshift(3)

# but this should throw an error (that's what this is all about)
v << "a"
end
 
B

Brian Schröder

Hi folks,
=20
I recently tried to implement a uniform vector class being defined as a
vector the elements of which all comply to some kind of common interface
or are a subclass of some prototype class.
=20
My first naive approach was to inherit from Array and to overwrite some
methods to make sure that new elements are ok. This works fine for
methods like []=3D, <<, or unshift but when implementing + I ran into the
following problem: Array#+ returns an Array and not a Vector which is
why I have to create a new Vector from the result of Array#+. As Array#+
already creates a new Array, this probably isn't very efficient and I
don't like the idea of having to do this for all possible methods that
return an Array. I was hoping to be able to somehow limit my
modifications of Array to a few essential methods.
=20
So, the question is: Does somebody know a way to make sure that methods
returning an Array (+, &, -, * etc.) always return a Vector without
having to redefine each of them?
=20
Please find my current toy implementation down below.
=20

Hello Thomas,

I'm quite shure you don't need to do what you are doing, because this
violates duck typing, but if you really want to you can use something
like the below.

Here I don't inherit from an array, but forward to an array, which
often is the simpler approach.

Please rethink your design. If you really want to do this for speed,
you could implement a simple C extension that includes a numeric
array, or use the narray class. Otherwise just don't put anything
wrong into the array, and everything will work out fine. And if
someone wants to put something into the array that quacks like a duck
but is no duck, also everything will continue to work.

best regards,

Brian

class Vector
def initialize(*args)
@numbers =3D args
check_values(args)
end

def method_missing(method, *args, &block)
result =3D @numbers.send(method, *args, &block)
if result.is_a?Array
Vector.new(*result) =20
else
result
end
end

private
def check_values(values)
return true if @numbers.empty? and values.empty?
unless defined?(@protoclass)
prototype =3D @numbers[0] || values[0]
@protoclass =3D prototype.class
end
values.each do |e|
unless e.kind_of?(@protoclass)
=09raise TypeError, "Expected #{@protoclass} but got #{e.class}", caller[2.=
-1]
end
end
end
end

if __FILE__ =3D=3D $0
v =3D Vector.new
# these are okay
p v
v << 1
p v
v[1] =3D 2
p v
p (v + [1,2,3])
v +=3D [1,2,3]
p v
v.unshift(3)
p v

# but this should throw an error (that's what this is all about)
v << "a"
p v
end


--=20
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/
 
G

gabriele renzi

Brian Schröder ha scritto:
I'm quite shure you don't need to do what you are doing, because this
violates duck typing, but if you really want to you can use something
like the below.

Here I don't inherit from an array, but forward to an array, which
often is the simpler approach.


this makes me think: the delegation approach is usually simpler.
But is this because of an underlying fault of the inheritance mechanic
or is it the obvious more simple thing?
I've often thought that there must be a better way of handling this
"generative" methods like "+" (but, say, a polymorphic #map could be the
same) which make subclassing harder, has someone haver thought the same?
Is there something in other languages that allow to overcome this kind
of limitations?
 
T

Thomas

I'm quite shure you don't need to do what you are doing, because this
violates duck typing

Thank you very much for pointing out how to use method_missing here.

BTW I don't think that it "violates" duck typing. The idea is to have a
collection type class with "entrance rules" that can guarantee that
every single element complies with some sort of standard/interface/rule
set/constraints/contracts ... Using kind_of? is only one possible
constraint here. Another one could be respond_to?:)quack). On the long
run, I would like it to look/feel like this:

class QuackQuack < UniformArray
add_contstraint lambda {|e| e.respond_to?:)quack)}
end

class ArmyOfDucks < UniformArray
add_contstraint lambda {|e| e.kind_of?(Duck)}
end

class FlockOfDucks < UniformArray
add_contstraint lambda {|e| e.kind_of?(Duck)}
permit nil # There may be nil values
allow :nested # The array may be nested
end

d = UniformArray.new
d.add_contstraint lambda {|e| e.kind_of?(Numeric) and e >= 0 and e <= 10}
d.add_handler lambda {|e| e > 10 ? 10 : 0}

etc.

I don't think this is incompatible with duck typing. It's just some sort
of specialized collection type of class.

Cheers,
Thomas.
 
M

Mark Hubbart

=20
Thank you very much for pointing out how to use method_missing here.
=20
BTW I don't think that it "violates" duck typing. The idea is to have a
collection type class with "entrance rules" that can guarantee that
every single element complies with some sort of standard/interface/rule
set/constraints/contracts ... Using kind_of? is only one possible
constraint here. Another one could be respond_to?:)quack). On the long
run, I would like it to look/feel like this:
=20
class QuackQuack < UniformArray
add_contstraint lambda {|e| e.respond_to?:)quack)}
end
=20
class ArmyOfDucks < UniformArray
add_contstraint lambda {|e| e.kind_of?(Duck)}
end
=20
class FlockOfDucks < UniformArray
add_contstraint lambda {|e| e.kind_of?(Duck)}
permit nil # There may be nil values
allow :nested # The array may be nested
end
=20
d =3D UniformArray.new
d.add_contstraint lambda {|e| e.kind_of?(Numeric) and e >=3D 0 and e <=3D= 10}
d.add_handler lambda {|e| e > 10 ? 10 : 0}
=20
etc.
=20
I don't think this is incompatible with duck typing. It's just some sort
of specialized collection type of class.

Use of a "uniform" array would seem to preclude duck-typing. Even if
you use respond_to? instead of kind_of?, it wouldn't catch objects
that use method_missing to handle messages. For example:

class Wrapper
def initialize(obj)
@obj =3D obj
end
def method_missing(*args, &block)
@obj.__send__ *args, &block
end
end

If you use this class to wrap an object, code that relies entirely on
duck typing will still be able to use the object, even though it won't
respond_to? much of anything, and it won't be kind_of? the class you
want.

On the other hand, if you call it ConstrainedArray and focus on
constraints rather than types, it could end up being quite useful...

cheers,
Mark
 
D

Douglas Livingstone

this makes me think: the delegation approach is usually simpler.
But is this because of an underlying fault of the inheritance mechanic
or is it the obvious more simple thing?

I think you've got it. Classes which have methods which return
instances of their own class should, once extended, return instances
of that extended class.

I'm not sure how it would work, something like return self.new.

Douglas
 
J

Jacob Fugal

Use of a "uniform" array would seem to preclude duck-typing. Even if
you use respond_to? instead of kind_of?, it wouldn't catch objects
that use method_missing to handle messages. For example:
=20
class Wrapper
def initialize(obj)
@obj =3D obj
end
def method_missing(*args, &block)
@obj.__send__ *args, &block
end
end
=20
If you use this class to wrap an object, code that relies entirely on
duck typing will still be able to use the object, even though it won't
respond_to? much of anything, and it won't be kind_of? the class you
want.

Well, on this point, isn't it recommended that whenever you override
method_missing you should also override respond_to? as well so that
they agree? I thought this was in the docs but can't find it now in
either ri method_missing or ri respond_to. Despite that, I know I've
heard it mentioned various times on this list and in general it seems
like a Good Idea.

Jacob Fugal
 
M

Mark Hubbart

=20
Well, on this point, isn't it recommended that whenever you override
method_missing you should also override respond_to? as well so that
they agree? I thought this was in the docs but can't find it now in
either ri method_missing or ri respond_to. Despite that, I know I've
heard it mentioned various times on this list and in general it seems
like a Good Idea.

I'd never heard it mentioned on the list (that I remember), but it
seems to be mentioned in the style guide, on the rubygarden wiki. No
disrespect to those who posted it, but what you see on a wiki doesn't
always reflect the views of the entire community. (not that I'm the
authority on what this community thinks)

Grepping the standard library, I found 24 instances of method_missing
being overridden, and 0 instances of respond_to? being overridden:

mark@eMac% ruby -e'p ARGF.grep(/def (\S+\.)?method_missing/).size' **/*.rb
24
mark@eMac% ruby -e'p ARGF.grep(/def (\S+\.)?respond_to\?/).size' **/*.rb
0

So, even if the programmer follows that rule, the stdlib doesn't seem
to, so you can still get objects creeping in that will respond to a
method without raising an error, but don't respond_to? it.

cheers,
Mark
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top