A few things that I wish I could do with parameter packs appear to be impossible. I'll outline my use case here.
I'm working with SwiftSyntax, looking for instances of layer.cornerRadius = blah
:
let list = CodeBlockItemListSyntax(node.statements.map { item in
guard let sequenceExpr = item.item.as(SequenceExprSyntax.self),
// helper function to turn .elements into `[ExprSyntax]`
let exprs = expressions(sequenceExpr.elements, expecting: 3),
// is assignment
let _ = exprs[1].as(AssignmentExprSyntax.self),
let memberAccess = exprs[0].as(MemberAccessExprSyntax.self),
let layerAccess = memberAccess.base.as(DeclReferenceExprSyntax.self),
// last member access is `cornerRadius`
memberAccess.declName.baseName.text == "cornerRadius",
// previous member access is `layer`
layerAccess.baseName.text == "layer",
let assignmentValue = exprs[2].as(ExprSyntax.self)
else {
// Not a corner radius assignment, skip
return item
}
...
}
I'd like to tidy up this boilerplate with variadic generics somehow. Ideally I should be able to do this:
// imagined signature of `tuplify`
func tuplify<T, each U>(_ value: [T]) -> (repeat each U)
...
let list = CodeBlockItemListSyntax(node.statements.map { item in
guard let sequenceExpr = item.item.as(SequenceExprSyntax.self),
let (
memberAccess: MemberAccessExprSyntax,
_: AssignmentExprSyntax,
assignmentValue: ExprSyntax,
) = tuplify(sequenceExpr.elements), // pretend .elements is an array
let layerAccess = memberAccess.base.as(DeclReferenceExprSyntax.self),
// last member access is `cornerRadius`
memberAccess.declName.baseName.text == "cornerRadius",
// previous member access is `layer`
layerAccess.baseName.text == "layer"
else {
// Not a corner radius assignment, skip
return item
}
...
}
This currently isn't possible because you can't pass in [T]
in place of a repeat each T
parameter. And Swift doesn't have a way to "spread" the array to fit it, either. Doing so would require a runtime check as to whether the run-time length of the array matches the compile-time length of the pack.
I can see how this use case was overlooked, but I wish an affordance had been included. Onto my next attempt...
Let's try this instead:
// new imagined signature of `tuplify`
func tuplify<each T, each U>(_ value: repeat each T) -> (repeat each U)
...
let list = CodeBlockItemListSyntax(node.statements.map { item in
guard let sequenceExpr = item.item.as(SequenceExprSyntax.self),
let s = sequenceExpr.elements, // pretend `s` is an array
let (
memberAccess: MemberAccessExprSyntax,
_: AssignmentExprSyntax,
assignmentValue: ExprSyntax,
) = tuplify(s[0], s[1], s[2]),
let layerAccess = memberAccess.base.as(DeclReferenceExprSyntax.self),
// last member access is `cornerRadius`
memberAccess.declName.baseName.text == "cornerRadius",
// previous member access is `layer`
layerAccess.baseName.text == "layer"
else {
// Not a corner radius assignment, skip
return item
}
...
}
I can't even do this, I assume because only one pack is used in the signature, so it for some reason can't guarantee that they have the same size? The error is Pack expansion requires that 'each T' and 'each U' have the same shape
.
Also, you can't cast between each T
and each U
, as far as I can tell.
Here's how I tried to implement the last tuplify()
:
func tuplify<each T, each U>(_ value: repeat each T) -> (repeat each U) {
return (repeat each value as! each U)
}
Are there any other ways to accomplish what I want here, or am I outta luck here?