Array constructor from Mirror Subject?

This would be a new initializer for Array, allowing it to be created from a Subject, targeted at providing greater ease-of-use to tuples.

The problem with Tuples

Homogenous tuples are already the standard way of representing fixed-sized arrays from C, and while there is this proposal which could potentially introduce fixed-sized arrays, there are still other valid reasons to treat a tuple like an array or other collection. For instance, printing the contents of a tuple element-by-element currently requires either using Mirror or printing each element with print(myTuple.0); print(myTuple.1)... et cetera.

One solution to this is simply making Tuple into its own collection by making it conform to the various protocols, but this is riddled with issues. Tuples are of course meant to be temporary values, and I worry that empowering them as collections encourages programmers to lean on them in places where other collections, or even structs, might be more appropriate. Heterogenous tuples would be problematic for iteration and mapping:

let myTuple = (1, 2, "Hello")

for element in myTuple {
     // element would be of type Any, which isn't really clear
}

Lastly, even if tuples conform to Collection, there could still be more array-like use cases that are left unsatisfied. For instance, there might be a need for a tuple concatenation method similar to append(contentsOf: ).

Array initialization from Subject

One solution for this that I'm pondering is to allow Arrays to be initialized from Subject, something that I already regularly add to my projects as an extension:

extension Array {
      init?<Subject>(mirroredFrom subject: Subject) {
            guard let array = Mirror(reflecting: subject).children.map(\.value) as? Self
            else { 
                  return nil
            }

            self = array
        }
}

Some of the benefits of this:

  • Encourages converting tuples to arrays if you want to use them for long-term purposes
  • Gives access to all of the benefits of Collection and to all of Array's methods.
  • Long arrays are preferable to pass into functions than long tuples, which are destructured into individual arguments
  • Heterogenous tuples can't be converted
  • Allows for array initialization from other reflectables, which could be a benefit or a drawback. (Do we really want to be able to convert structs to arrays?)
1 Like

Mirror won't allow writing back to the tuple though. Also, AFAIK it is not random accessible, e.g. this would take O(index) time, which could be problematic for large tuples:

    let m = Mirror(reflecting: tuple)
    let items = m.children
    return items[items.index(items.startIndex, offsetBy: index)].value

Here we could require an explicit type annotation:

for element in (1, 2, 3) { ... }            // ✅
for element in (1, 2, "Hello") { ... }      // 🛑
for element: Any in (1, 2, "Hello") { ... } // ✅

similar to existing precedent:

let x = [1, "x"] // 🛑 Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
let x: [Any] = [1, "x"] // ✅

I don't see how: tuples are fixed size data structures.

I feel like this is all the more reason to encourage converting tuples to arrays. Relying on Mirror.Children is clunky and inefficient, converting it to an array will save time in the long run especially if there are multiple accesses or there are large tuples (such as in the case of some converted C-arrays).

I was suggesting that someone could want a method which returns the concatenation of two tuples without in-place modification; though I guess comparing it to append(contentsOf:) made that unclear. Either way, with easier tuple-to-array conversion, the need for concatenation would be minimized.

I agree that requiring an explicit type declaration on for-in loops would basically fix the type clarity problem, though.