I recently encountered a situation where I wanted to pass a generic function as an argument, while keeping it generic.
Here’s a toy example entirely unrelated to what I was actually doing:
// If we have this generic function
func foo<T: Numeric>(_ x: T) -> T {
x + 1
}
// We can call it on many different types of values:
func bar() {
foo(1)
foo(2.0)
foo(3 as UInt8)
}
// But if we want the caller to pass a function of their choice
// with the same signature as foo, how can we do it?
func hmm(_ f: (T)->T) { // error: Use of undeclared type 'T'
f(1)
f(2.0)
f(3 as UInt8)
}
There’s nowhere to hang a <T: Numeric> on the parameter f, and we don’t want to put it on hmm because then f could only operate on one type.
Is this something that other people have wanted?
• • •
Note that we can work around this by making the function a requirement of a protocol and passing a conforming type, so it’s clearly not a fundamental impossibility:
protocol P {
static func f<T: Numeric>(_ x: T) -> T
}
func huh<T: P>(_ t: T.Type) {
t.f(1)
t.f(2.0)
t.f(3 as UInt8)
}
But that makes the call-site require declaring a type, and then passing the type itself:
enum Doubler : P {
static func f<T: Numeric>(_ x: T) -> T { 2 * x }
}
huh(Doubler.self)
Ideally I want to use trailing closures at the call-site:
To add to Dan's excellent answer, this technique of using an apply function is called defunctionalization, in case you want to read up more on this. You may also see this pop up in other contexts; for example higher-kinded types can be mimicked in a language without higher-kinded types using defunctionalization. The Lightweight Higher-Kinded Polymorphism paper describes how this would work in the context of OCaml.