Closure With Generic Parameters

Closure with generic parameters

I believe what you're asking for can't make sense (having nothing to do with Swift). While I'm interested in being proven wrong, I don't believe this could be reasonably created in any strongly typed language. (EDIT: continuing my research, I believe this would be possible in a language with first-class polymorphism, but I am not aware of any general-use languages that actually have this feature.)

let myClosure = {<S where S: MyProtocol, S: MySuperClass>(param: S) in ... }

What type would you expect myClosure to be? A generic creates an abstract type. It does not become a real type until it is specialized. So myClosure would be of an abstract type itself. That's like asking for an instance of an abstract class. The whole point of "abstract" is you can't construct one. The best you could say would be that myClosure would itself be a type that you would need to instantiate into a real instance (but then let doesn't make any sense; you don't let types).

When you wrap this in a struct, what you're really doing is creating an abstract type that you will specialize into a real type when you create an instance.

Now what would make sense IMO (but appears currently to be impossible), is this:

typealias Mapping<S> = S -> S
let identity: Mapping<Int> = { return $0 }

That makes sense because you're defining an abstract type (Mapping), but then instantiating a concrete type Mapping<Int>. Unfortunately, typealias does not appear to support generics at this point, so a struct is probably the best tool we have.


Note that while typealias is a bust, it is clearly possible to specialize function variables themselves. This isn't a closure, I know, but may be useful in some of the same situations.

func Identity<T>(i:T) -> T {
return i
}

let identityInt:(Int -> Int) = Identity
identityInt(1) // => 1

Using this to explore the problem of abstract types a little more, consider:

func Identity<T>(i:T) -> T { return i }
let x = Identity

This fails to compile with the error:

error: cannot convert the expression's type '(T) -> T' to type '(T) -> T'

That's because the type (T) -> T is not a concrete type, so you can't have one called x. Compare that to identityInt, which I explicitly specialized into a concrete type, and then could construct.

Rust closure generics

You can do this on nightly using the fn_traits (and closely related unboxed_closures) features. This allows you to use Fn like Fn<Args, Output = V> where Args is a tuple type of all the parameters passed to the function.

#![feature(unboxed_closures)]
#![feature(fn_traits)]

struct Cacher<'a, Args, V: Clone>
{
calculation: &'a dyn Fn<Args, Output = V>,
value: Option<V>
}

impl<'a, Args, V: Clone> Cacher<'a, Args, V>
{
fn new(calculation: &'a dyn Fn<Args, Output = V>) -> Cacher<Args, V> {
Cacher {
calculation: calculation,
value: None,
}
}

fn value(&mut self, args: Args) -> V {
// all this cloning is probably not the best way to do this
match self.value.clone() {
Some(v) => v,
None => {
let v = self.calculation.call(args);
self.value = Some(v.clone());
v
}
}
}
}

This does require you to call value() with a tuple:

let mut cache1 = Cacher::new(&|a| a + 1);
let value1 = cache1.value((7,));

let mut cache2 = Cacher::new(&|a, b| a + b);
let value2 = cache2.value((7, 8));

However, you can make it nicer to use if you're willing to make the boilerplate for the numerous tuple types:

impl<'a, T, V: Clone> Cacher<'a, (T,), V>
{
fn value2(&mut self, arg1: T) -> V {
self.value((arg1, ))
}
}

impl<'a, T, U, V: Clone> Cacher<'a, (T, U), V>
{
fn value2(&mut self, arg1: T, arg2: U) -> V {
self.value((arg1, arg2))
}
}

// ...

let mut cache1 = Cacher::new(&|a: usize| a + 1);
let value1 = cache1.value2(7);

let mut cache2 = Cacher::new(&|a: usize, b: usize| a + b);
let value2 = cache2.value2(7, 8);

See it running on the playground.

This only works on nightly because its not yet been stabilized if this is how they will be supported generically in the future.

is it possible to create a generic closure in Swift?

No, because variables and expressions can't be generic. There are only generic functions and generic types.


To clarify: In some languages you can have types with a universal quantifier, like forall a. a -> a. But in Swift, types cannot have a universal quantifier. So expressions and values cannot be themselves generic. Function declarations and type declarations can be generic, but when you use such a generic function or an instance of such a generic type, some type (which could be a real type or a type variable) is chosen as the type argument, and thereafter the value you get is no longer itself generic.

Swift closure generic parameters

In the declaration of the method you're calling, the error: closure has the first parameter of optional type (error:(TError?, NSError)->Void), but you declared it with non-optional type. That may be the reason (indeed, Swift sometimes produces pretty unrelated error messages).

Generic parameter 'T' could not be inferred with closures

You need to explicitly tell the type when calling the function. If you are expecting String then call it with String type.

MyClass.myFunction { (response: String) in

}

Possible to define generic closure?

No, AFAIK you can't. I mean, you can define a generic closure, what you can't do is create a let binding with a generic left-hand side.

A fn get<T>, as the one you mention rewriting, undergoes monomorphisation, i.e. when compiling it, rustc generates a different version of get for every actual T that is used to call it. By the time you assign the result of that get (let a = get(...)), that result has a concrete type and size.

A let binding does not get monomorphised, so you can't have a let a<T> = ... and have a different version of a generated for you by the compiler.

What I think might enable this is the introduction of higher-kinded types, which is one of the highly desired but not yet fully fleshed out new features for Rust. They would enable you to write something like:

// does not work as of Rust 1
let a = for<T> |s: &str, t: T| {...}

i.e. return a closure that I can later parametrize with a T (which is what you're asking for).



Related Topics



Leave a reply



Submit