There's no way to write a generic closure wrapper that preserves type attributes (@MainActor, @Sendable, custom global actors).
Example
@globalActor actor MyActor { static let shared = MyActor() }
func inject<T>(_ f: @escaping (Int) -> T) -> () -> T {
{ f(42) }
}
let f = inject { @MyActor x in print(x) }
// error: converting '@MyActor @Sendable (Int) -> ()'
// to '(Int) -> ()' loses global actor 'MyActor'
The closure is @MyActor, but (Int) -> T can't carry that. Attribute lost.
To preserve it, you need a separate overload per attribute:
func inject<T>(_ f: @escaping @MyActor (Int) -> T) -> @MyActor () -> T { ... }
This is not a viable option - throws/async combinations already require 4 overloads, each attribute doubles that, and custom global actors defined by downstream users make it impossible. Moreover, you can't even have both @MainActor and @MainActor @Sendable overloads - the compiler considers them a redeclaration.
Discussion
Has there been any discussion about parameterizing over type attributes? Something like:
func inject<T, @attributes Attr>(_ f: @escaping @Attr (Int) -> T) -> @Attr () -> T
This seems like a natural next step after parameter packs solved the arity problem - type attributes are the remaining axis of the overload explosion. Curious if anyone has run into this or if there are existing proposals/pitches I missed.