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

is it possible to make this generic function a closure?

let myfunc = { <T>(i:T) -> T in
    return i
}

this doesn't work...

Ken Zhang
  • 1,454
  • 2
  • 13
  • 27
  • 1
    Similar (same?) question here: http://stackoverflow.com/questions/25401584/generics-as-parameters-to-a-closure-in-swift. – Martin R Aug 28 '14 at 09:28
  • 1
    Generics cannot be used with closures. The answer linked by @MartinR provides a workaround. I don't think this question is a duplicate, because the other question is about making a closure with generics work, whereas this is an explicit question whether generics can be used or not. – Antonio Aug 28 '14 at 09:41
  • I believe the answer is that this doesn't work because it actually makes no sense (having nothing to do with Swift in particular). `myfunc` would be of an abstract type, which is the same as trying to construct an abstract class. I discuss more in an answer to the question @MartinR links. – Rob Napier Aug 28 '14 at 13:40

4 Answers4

23

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.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • 1
    I agree with Rob, in the sense that it should make sense to use `typealias` in conjunction with a closure to define a generic closure type – Mazyod Oct 06 '15 at 20:24
  • In most programming languages, generic function arguments are effectively just function arguments with values baked in at compile-time.  So it would mean that the possible values for the generic argument are evaluated at compile-time instead of just used at run-time (like normal function/closure arguments). The advantage would be the ability to parameterize a closure with types that need to be evaluated at compile-time, e.g. `StringLiteralConvertible`. I think your confusion lies in thinking about the variable or expression as generic; rather, it's the closure which is generic. – Slipp D. Thompson Jun 22 '16 at 03:18
  • How about this one ? typealias ResultClosure = (ResultCode, String?, T?) -> Void func loginUser(userName: String, password: String, resultHandler: ResultClosure?) It is generic and have compile time type check – Konstantin Khamenok Feb 19 '20 at 23:06
  • Variables are allowed to be generic in C++. – Sapphire_Brick Jun 26 '20 at 15:54
11

Probably you need something like this.

Type declaration:

typealias ResultClosure<T> = (ResultCode, String?, T?) -> Void

Function declaration:

func loginUser(userName: String, password: String, resultHandler: ResultClosure<TokenModel>?)

Usage:

    NetConnector.shared.loginUser(userName: userName ?? "", password: password ?? "") { (code, message, data) in
        self.display?.unlockScreen()
        if code == .success {
            if let activeToken = data {
                AppData.shared.userToken = activeToken
            }
            self.display?.showHome()
        } else {
            self.display?.showError(errorMessage: message)
        }
    }
  • Nice, but it won't work using type constraint and defining an object property with that type (typealias) – OhadM Dec 28 '21 at 13:07
3

As mentioned, variables in Swift cannot be generic, so creating a closure, whose generic types are specified by the caller is not possible. However, there are workarounds:

With SE-253, it is possible to make arbitrary (nominal) types callable. So instead of declaring a generic closure, we can declare a (non-generic) struct that has a generic callAsFunction method:

struct MyFunc {
    func callAsFunction<T>(_ i: T) -> T {
        return i
    }
}

Now, we can declare a non-generic variable that we can call with a generic value:

let myFunc = MyFunc()
let x = myFunc(42) // -> Int
let y = myFunc("foo") // -> String

Note that this workaround doesn't apply to all situations, but it can be helpful in some.

Palle
  • 11,511
  • 2
  • 40
  • 61
0

I have found some alternative way , you can use Anyobject in your closure and pass any values to your method .

typealias genericCompletion<T:AnyObject> = ((Bool,T,String) -> Void)
struct Student {
    var name:String = "Kishore"
    var age : String = "125"
}
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.createAGenericReturn { (success, object, message) in

        }

        self.createStructGeneric { (success, student, message) in

        }

    }


    func createAGenericReturn(callback:@escaping(genericCompletion<AnyObject>)){
        callback(true,434.433 as AnyObject,"kishoreTest")
    }

    func createStructGeneric(callback:@escaping(genericCompletion<AnyObject>)){
        callback(true,Student.init() as AnyObject,"kishoreTest")
    }

}

Here you can see I mentioned Generic as Anyobject typealias genericCompletion = ((Bool,T,String) -> Void) , So you can pass any values to it .

Kishore Kumar
  • 4,265
  • 3
  • 26
  • 47
  • Interesting solution, making use of polymorphism. However, this weakens the type checker, also the code that needs to implement the generic closure will have to do a lot of downcases if it will want to support multiple types. – Cristik Mar 30 '21 at 06:52