Hey everyone, I'm struggling to get some parameter pack code to compile.
I'm trying to make a variadic route builder, so that I can do things like:
let router = Router()
let group = router.group()
group
// by default, closures are a "(Request) -> Response" type
.on(.get, "/about") { request -> Response in ... }
group
// this value becomes a parameter to the closure
.addingParameter(someContext)
// the handler now takes two parameters
.on(.get, "/with/context") { request, context -> Response in ... }
group
// add a context parameter
.addingParameter(someContext)
// validate the request, supply an account parameter
.requiringUserAuthorization()
// the handler now takes three parameters
.on(.get, "/user/account") { request, context, account -> Response in ... }
I've gotten the pattern of this reduced down to this struct. The parameter pack (each Parameter) represents all the additional parameters that are passed to the .on(...) closure, in addition to the initial Request value.
When the builder gets the .on(...) method called, it uses the register closure to package up that closure and notify another object (the previous builder, or ultimately the Router) about it.
(note: I've left out the "method" and "path" parameters for brevity)
struct Builder<each Parameter> {
typealias Handler = (Request, repeat each Parameter) -> Response
typealias Registrar = (@escaping Handler) -> Void
let register: Registrar
func addParameter<P>(_ newParam: P) -> Builder<repeat each Parameter, P> {
typealias NewBuilder = Builder<repeat each Parameter, P>
let newRegistrar: NewBuilder.Registrar = { (newHandler: NewBuilder.Handler) in
self.register({ (request: Request, myParams: repeat each Parameter) -> Response in
return newHandler(request, (repeat each myParams, newParam))
})
}
return NewBuilder(register: newRegistrar)
}
func requiringUserAuthorization() -> Builder<repeat each Parameter, Account> { ... }
}
This looks right to me, but I cannot get this to compile. I have tried:
- removing typealiases
- adding a
typealias Parameters = repeat each Parameter
- making the
addParameter return type be Builder<(repeat each Parameter, P)>: this crashes the compiler
- removing parenthesis on the
newHandler(request ...) call
- adding type annotations everywhere
- probably other things I've forgotten
As the code currently stands above, the compiler produces two errors on the newHandler(request, (repeat ...)) line:
- Cannot pass value pack expansion to non-pack parameter of type 'repeat each Parameter'
- Missing argument for parameter #3 in call
Any ideas what I'm missing?
Edit: I'm on Xcode 15.3 (Swift version 5.10 - 5.10.0.13) on macOS Sonoma, in case that matters