Reputation: 429
Are there any use cases in which we'd call Array.to_a
and Array.to_ary
on an object that's already an Array?
If not, why do these methods exist within the Array class?
Upvotes: 3
Views: 1627
Reputation: 768
Array implements to_a
and to_ary
because this allows for cleaner methods that coerce arguments into specific types.
For example, what if you had a method:
def foo(object)
#does some work on object that requires object acts like an array.
...
end
and wanted to use this method on sets generated from somewhere else in your code.
One way you can do this is to cast object.to_a
before doing the operation:
def foo(object)
array = object.to_a
...
end
If Array didn't implement to_a
, then you'd have to do a check:
def foo(object)
array = object.to_a if object.respond_to?(:to_a)
...
end
Upvotes: 0
Reputation: 384
I think it's (among other things) to avoid having a special case for nil
.
Let's say foo
is a method that can either return an array or nil
. The snippet below would fail half of the time:
foo.each { |x| puts x }
If Array
didn't implement a to_a
method, you'd probably have to write something like this, which in my opinion is a bit ugly:
(foo || []).each { |x| puts x }
Instead of this:
foo.to_a.each { |x| puts x }
In a similar fashion, Integer
has a to_i
method and String
has a to_s
method, and so on.
Upvotes: 0
Reputation: 369594
Are there any use cases in which we'd call these coercion methods (Array.to_a and Array.to_ary) on an object that's already Array?
Yes: in Ruby, you generally never care what class an object is an instance of. Only what it can do. So, you only care about "can this object convert itself to an array". Obviously, an array can convert itself to an array, so it should have those methods.
Slightly longer answer: if we don't care what class an object is an instance of … then why do we care whether it can convert itself to an array? Well, that's a pragmatic choice. From a purely OO perspective, it shouldn't matter. But there are certain operations that are implemented deep inside the core of the execution engine that require an object to be of a certain class, for efficiency and performance reasons. In other words, sometimes objects don't work, you need an Abstract Data Type.
For example, there are certain operations inside the Ruby execution engine that take advantage of the fact that they know about the internal memory layout of Array
s. Obviously, those operations will break in horrible ways if you hand them something that is not an Array
and they go poking around in that object's memory. From a purely OO perspective, those operations shouldn't know that, and they should use Array
's public interface, but alas, they don't. But, in order to give you (the programmer) an escape hatch for your own array-like objects, those operations will allow you to convert yourself to an Array
first, by calling to_ary
.
In other words, implementing to_ary
means that your object is a kind-of array. Obviously, an array is a kind-of array, that's why it responds to to_ary
.
There are other similar conversion methods in Ruby: to_str
for strings, to_int
for integers, to_float
for floats, to_proc
for "functions".
There are also their single-letter variants. The long variants mean "I really am an array, I just don't happen to be an instance of the Array
class." The short variants, instead, mean "I can kinda-sorta represent myself as an array".
You can see that most obvious with nil
: it responds to to_i
(because it kinda-sorta makes sense to represent nil
as the integer 0
), but it doesn't respond to to_int
(because nil
is not an integer in different clothing, it is something completely different).
The fact that arrays, integers, etc. also implement to_a
, to_ary
, to_i
, to_int
, etc. means that you can treat all array-like objects the same, polymorphically. It doesn't matter if it's an array, a stack, a set, a tree, an enumerator, a range, or whatever. As long as it can kinda-sorta be represented as an array, it will respond to to_a
, and as long as it actually is an array (even if its class isn't Array
), it will respond to to_ary
, and you don't have to check because it doesn't matter.
However, note that these situations ideally should be rare. In general, you should care about, say, whether the object can iterate itself (i.e. it responds to each
). In fact, most of the things you can do with an array, you can also do with any other Enumerable
, without using to_ary
or to_a
. Those should be the last resort.
Upvotes: 11