Jason Sears
Jason Sears

Reputation: 429

Why does the Ruby Array class have to_a and to_ary methods?

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

Answers (3)

Mickey Sheu
Mickey Sheu

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

steenuil
steenuil

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

Jörg W Mittag
Jörg W Mittag

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 Arrays. 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

Related Questions