I believe you're looking for higher-rank types (rank-2 for your use case). Friendlier explanation here.
A nice name for this in Swift might be "generic function values" or "generic closures". It could look like this:
func foo(_ f: <T> (T) -> T) {
// ^~~~~~~~~~~~~~~ first-class generic closure
f(1)
f(2.0)
f(3 as UInt8)
}
let identity: <T> (T) -> T = { $0 }
foo(identity)
I don't think this language feature is widely supported outside of Haskell, as it's known to complicate type inference.
Though it is possible to emulate rank-2 types by adding a level of indirection, like defining a protocol with a generic method (as you did), or a ~>
trait with a polymorphic apply
method in Scala.
// `Rank2Function` represents `<T> (T) -> T`.
protocol Rank2Function {
static func callAsFunction<T>(_ input: T) -> T
// ^~~ higher-rank type parameters bound here
}
func foo<Fn: Rank2Function>(_ f: Fn.Type) {
// `static func callAsFunction` sugar isn't supported yet, so we have to spell it out.
f.callAsFunction(1)
f.callAsFunction(2.0)
f.callAsFunction("Hello")
}