What does it mean when a function type is similar to `() -> () -> ()`

I practicing networking in Swift and I attempted to make a network layer where each request and its data is in an enum and each request has its own response handler.

However I cannot get past a type error...

This is my code, you can copy and paste it in a playground, it doesn't actually process the request.

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true
extension String: Error { }

typealias ResponseHandler = (Data?, URLResponse?, Error?, ResponseHandlerCompletion?) -> ()
typealias ResponseHandlerCompletion = (Result<AnyObject, Error>) -> ()

enum RequestType {
    
    case githubInfo
    case googleInfo
    
    var url: URL {
        switch self {
        case .githubInfo: return URL(string: "https://github.com")!
        case .googleInfo: return URL(string: "https://google.com")!
        }
    }
    
    var completion: ResponseHandler? {
        switch self {
        case .githubInfo: return NetworkLayer.githubInfoResponseHandler
        case .googleInfo: return NetworkLayer.googleInfoResponseHandler
        }
    }
    
}

class NetworkLayer {
    
    let session = URLSession(configuration: .default)
    
    func request(_ requestType: RequestType, completion: ResponseHandlerCompletion?) {
        
        let request = URLRequest(url: requestType.url)
        
        session.dataTask(with: request) { (data, response, error) in
            requestType.completion?(data, response, error, completion)
        }
        
    }
    
    func githubInfoResponseHandler(_ data: Data?, _ response: URLResponse?, error: Error?, _ completion: ResponseHandlerCompletion?) {
        print("Processing github response...")
        let result: Result<AnyObject, Error> = .failure("...")
        completion?(result)
    }
    
    func googleInfoResponseHandler(_ data: Data?, _ response: URLResponse?, error: Error?, _ completion: ResponseHandlerCompletion?) {
        print("Processing google reponse...")
        let result: Result<AnyObject, Error> = .failure("...")
        completion?(result)
    }
}

let networkLayer = NetworkLayer()
networkLayer.request(.githubInfo) { (result) in
    switch result {
    case .success(let object):
        print(object)
    case .failure(let error):
        print(error)
    }
}

The full error message is:

Cannot convert return expression of type '(NetworkLayer) -> (Data?, URLResponse?, Error?, ResponseHandlerCompletion?) -> ()' (aka '(NetworkLayer) -> (Optional, Optional, Optional, Optional<(Result<AnyObject, Error>) -> ()>) -> ()') to return type 'ResponseHandler?' (aka 'Optional<(Optional, Optional, Optional, Optional<(Result<AnyObject, Error>) -> ()>) -> ()>')

The error appears on these 2 lines:

case .githubInfo: return NetworkLayer.githubInfoResponseHandler
case .googleInfo: return NetworkLayer.googleInfoResponseHandler

So basically the function I'm trying to return is:
(NetworkLayer) -> (Data?, URLResponse?, Error?, ResponseHandlerCompletion?) -> ()
when it is expecting:
(Data?, URLResponse?, Error?, ResponseHandlerCompletion?)

I don't understand where that (NetworkLayer) -> (...) -> () comes in? I've never seen a function type like that.

Am I missing something, is there a way to fix and work with this or did I just hit a wall?

I'd appreciate any help.


I know that my code is not exactly good, I have force unwraps and my results are bogus, I was trying to isolate the problem as much as possible.

I just realized that if I change the 2 functions: githubInfoResponseHandler(_:_:_:_:) and googleInfoResponseHandler(_:_:_:_:) to static functions then the error goes away and I can work with this code. But I still don't exactly understand what is going on.

I you see multiple ->'s in a function type, this means that some function is returning a function.

To break it down, simply scan from left to right:

  1. the part left of the -> is the function arguments,
  2. the remainder to the right of the -> is the function return type.
  3. If the return type contains one or more -> itself, it is a function type. Go to step 1.

Those methods you mention are instance methods, what this means is that they are defined on instances of the NetworkLayer class. Instance methods are actually static methods on the type that take the instance as a parameter and return the original method type. Because they are actually static methods you can call them on the type itself, without requiring an instance immediately, and that is what you are doing with

case .googleInfo: return NetworkLayer.googleInfoResponseHandler

which returns:

(NetworkLayer) -> (Data?, URLResponse?, Error?, ResponseHandlerCompletion?) -> ()

This return type represents a function that takes an instance of the NetworkLayer class as input and returns another function that operates on that specific instance which has the type (Data?, URLResponse?, Error?, ResponseHandlerCompletion?) -> ().

Now, if both of those methods need anything at all from a specific instance of a NetworkLayer class, they should remain as instance methods, but if they do not, then you should make them static instead and your code should work without problems.
But if they need to remain as instance methods then you should either call those methods directly on the instance: networkLayer.githubInfoResponseHandler(...) and let swift pass the networkLayer instance to the method automatically; or in your case you must change the ResponseHandler typealias to the type shown above that takes an instance of NetworkLayer and produces another function, and you should change requestType.completion?(data, response, error, completion) to requestType.completion?(self)(data, response, error, completion) which first calls the function using self as the instance and then calls the returned function with all of the other parameters.

Maybe after knowing about this you can think of other ways of doing what you want that may be simpler.

Hope this helps!

2 Likes

That's a straightforward way to read it, thanks.

This is a great in depth explanation, I learned a lot. I'll rethink on how I could make this simpler. Thanks.

1 Like