3

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?

Jason Sears
  • 429
  • 1
  • 7
  • 18
  • 1
    ruby as a language is a huge proponent of "duck typing" so that you can pass any argument to a method that responds to a particular method so say `def something(arg); arg.to_a; end` now this method can accept an `Array` or a `Hash` or any other argument that has a `to_a` method. See this [SO Post](https://stackoverflow.com/questions/9467395/whats-the-difference-between-to-a-and-to-ary) for a more in depth explanation. – engineersmnky May 25 '17 at 17:56
  • 1
    Notice that Integer has `to_i`, String has `to_s`, Hash has `to_h`. – the Tin Man May 25 '17 at 18:00
  • For example, I have worked with an API that returns either object or an array of these objects. It was convenient to use `#to_a` to be sure that as result I will get an array. – yzalavin May 25 '17 at 18:03
  • `thing.to_a` can be used to coerce to array in some situations, but not everything responds to it, so `[*thing]` can be used as well. – max pleaner May 25 '17 at 18:06
  • Regularity of behaviors. – Dave Newton May 25 '17 at 18:09
  • As this is a pure-Ruby question I suggest you remove the "ruby-on-rails" tag. – Cary Swoveland May 25 '17 at 19:12

3 Answers3

10

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.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
0

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
Mickey Sheu
  • 758
  • 7
  • 16
0

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.

steenuil
  • 384
  • 3
  • 6