I'm having an issue with some code I'm writing that uses pack expansion and macros

The fundamental issue is that (repeat each foo) doesn't expand to a tuple if there is only a single value:

func tuple<each T>(foos: (repeat each T)) -> (repeat each T) {
  (repeat each foos)
}
let singleElementTuple = tuple(foos: (1))
singleElementTuple.0 // Value of type 'Int' has no member '0'
let twoElementTuple = tuple(foos: (1, 2))
twoElementTuple.0 // Works fine

In regular code this is mostly-fine, but in generated code (such as a macro) it is a little annoying as I need to special-case the single-element case. Is there any better way to deal with this?

1 Like

I don't think so, because not only do packs support single elements, but also, zero elements:

tuple(foos: ()).0 //🙂‍↔️

Maybe this is helpful, for the special casing?

func isReallyTuple<each Element>(_ element: (repeat each Element)) -> Bool {
  var hasFirstElement = false
  for _ in repeat (each Element).self {
    if hasFirstElement { return true }
    else { hasFirstElement = true }
  }
  return false
}

One element tuples were pitched and rejected in [Pitch] One-Element Tuples.

Maybe you want something like this?

func first<T, each U>(_ tuple: (T, repeat each U)) -> T {
  return tuple.0
}

That crashes the compiler in the latest Xcode beta. Do you actually have it working?

I tried something like this, but for my use case it is actually a (repeat (ConcreteType, each ParameterPackType)) so in order to make that work I'd need to provide a value of ConcreteType in order to make it work. Fortunately, it isn't a huge issue for the logic to be conditional on whether or not there is one or multiple elements (0 elements is actually another interesting case). I was just wondering if there was some language feature I missed which cleaned this up but it seems like that isn't the case.