InlineArray pattern matching and destructuring

I have come across a need in pattern matching an InlineArray and was very surprised to find out that while I can do this:

@available(macOS 26, *)
mutating func names<let count: Int>() -> [count of Name] {
	InlineArray { _ in _names.next() }
}

I can't actually use this the way I wanted:

// doesn't work :(
let [x, y, z] = names()

forcing me to instead do either of these, which are suboptimal because I'm going to be writing hundreds of tests with that pattern:

// locals[0], locals[1], etc. I've lost the name :(
let locals = names()
// got the name back but "lost" my short-hand :(
let (x, y, z) = (locals[0], locals[1], locals[2])
// wonderful! except...
let (x, y, z) = names()
// ...now I've got this in my code and it keeps growing to like 30
	mutating func vars() -> (Var) {
		(_vars.next())
	}

	mutating func vars() -> (Var, Var) {
		(_vars.next(), _vars.next())
	}

	mutating func vars() -> (Var, Var, Var) {
		(_vars.next(), _vars.next(), _vars.next())
	}

	mutating func vars() -> (Var, Var, Var, Var) {
		(_vars.next(), _vars.next(), _vars.next(), _vars.next())
	}

	mutating func vars() -> (Var, Var, Var, Var, Var) {
		(_vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next())
	}

	mutating func vars() -> (Var, Var, Var, Var, Var, Var) {
		(_vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next())
	}

	mutating func vars() -> (Var, Var, Var, Var, Var, Var, Var) {
		(_vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next())
	}

	mutating func vars() -> (Var, Var, Var, Var, Var, Var, Var, Var) {
		(_vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next(), _vars.next())
	}

I'd therefore like to suggest an InlineArray pattern matching.

let [x, y, z] = names()

switch names {
case let [x]: // ...
case let [x, y]: // ...
case let [x, y, z]: // ...
case let [_, y, z, w] where y * z == w: // ...
default: // ...
}

if case let [x] = names { /* ... */ }

and so forth.

Thoughts?

2 Likes

Why don't you use a tuple instead?

1 Like

Because then I have to make a names function for each possible arity, that’s what I’m doing right now but I have like 30 functions for returning tuples of 1-30 elements, I’d much rather be able to write 1 function that infers the count!

Can you explain your actual use-case in more detail? It's hard to imagine a situation where this would be meaningful:

let (x1, x2, x3, /* ... */, x30) = names()

... expression involving all of x1 ... x30

Unit tests for various passes of a toy compiler. I’m naming all those variables and building up the input and expected structure using them.

Also in unit tests for graphs where I name nodes to build up graphs.

Basically at this scale, unit tests!

Another option could be supporting same-type constraints on parameter packs, that would also support my use case.

1 Like

You could forgo type safety and just use strings to name your nodes, or you could make your graph data type generic over a NameType: Hashable that is used for the names, and then define local enums like this:

enum Name {
  case x
  case y
  case z
}

That could be an option, it would require large adjustments to the main code just for the tests which isn’t ideal but it would definitely have its advantages!

This sort of kind of exists already. It just requires some nonsense for explicit shaping.

var varsity = Varsity()
let (x, y, z) = varsity.vars(0, Never.self, "🍌")
#expect((x, y, z) == (1, 2, 3))
struct Varsity {
  private var _vars = sequence(first: 1) { $0 + 1 }
  
  mutating func vars<each Element>(
    _: repeat each Element
  ) -> (repeat Each<Int, each Element>) {
    `repeat`((repeat each Element).self) { _vars.next()! }
  }
}
@inlinable public func `repeat`<each Element, Value>(
  _: (repeat each Element).Type = (repeat each Element).self,
  _ value: () -> Value
) -> (repeat Each<Value, each Element>) {
  (repeat { _ in value() } ((each Element).self))
}

public typealias Each<Value, each Element> = Value

While I love this hacky solution, its still longer at call-site, I’ll keep my 30 functions for now!

1 Like