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
Getting a "This Application Is Modifying the Autolayout Engine from a Background Thread" Error
How to Use Swift 4 Codable in Core Data
How to Run a Terminal Command in a Swift Script? (E.G. Xcodebuild)
Nsobject Subclass in Swift: Hash VS Hashvalue, Isequal VS ==
Alamofire Get API Request Not Working as Expected
How to Resolve "Ambiguous Use Of" Compile Error With Swift #Selector Syntax
Swift - How to Convert String to Double
Swiftui Hstack With Wrap and Dynamic Height
Do Swift-Based Applications Work on Os X 10.9/Ios 7 and Lower
How to Print the Type or Class of a Variable in Swift
Sending Json Array Via Alamofire
Uitableview With More Than One Custom Cells With Swift
Convert String to Date in Swift
Uncaught Error/Exception Handling in Swift