Promisekit: 3.0.0: Cannot call value of non-function type 'ErrorType?'

Created on 6 Jan 2016  路  17Comments  路  Source: mxcl/PromiseKit

Installed PromiseKit 3.0.0 via Cocoapods. The following code generates a compile error (Xcode Version 7.2 (7C68)):

let (p2, _, _) = Promise<Void>.pendingPromise()
p2.error { error -> Void in
        NSLog("Error: %@", error)
}

This appears to be because Promise+Properties.swift defines public var error: ErrorType? and Promise.swift defines public func error(policy policy: ErrorPolicy = .AllErrorsExceptCancellation, _ body: (ErrorType) -> Void).

I'm not sure Swift allows you to have properties with the same names as functions. The Swift compiler is getting confused.

Most helpful comment

It happens to me often when the code within the error block has compiler errors. For instance:

API.instance.cancelTripRequest(tripRequest).then { _ -> Void in
    self.onTripRequestCancelled(tripRequest)
}.error { error in // Cannot call value of non-function type 'ErrorType?'
    Kugel.publish(TripRequestCancelFailed, object: error.nsError)
}

But the actual error is within the error block: TripRequestCancelFailed is a static member of the current class, not a global variable. A good way to debug this is by moving your error code outside of the error block, and see what the compiler says.

Not sure it's the cause of your issue, but I thought it was worth mentioning.

All 17 comments

OK. So perhaps this is a situation where Swift's Type Inference system is screwing me. As this compiles:

let (p2, _, _) = Promise<Void>.pendingPromise()
p2.error { (error:ErrorType) -> Void in
        NSLog("Error: \(error)")
}

I've seen this before but it didn't immediately jump out at me so while I knew it wasn't an PK problem I was coming up empty handed with an answer for you. Happy you figured it out. And you're right, seems like a Swift shortcoming.

As an aside, and for my own curiosity, does your error handler get called as expected? Asking because I've never attempted adding a handler to a promise chain in this manner (i.e. outside of the initial chain). I see no reason why it wouldn't of course, just wondering.

Yeah. Swift can be a bit confusing at times. As for the use of error I'm not actually doing it this way. It was just the smallest code snippet that showed the compile issue. Anyway. Closing this issue as it was my dumb fault not PK. :)

I get this error even when similar code is used in other parts of my application.
}.error {
(error: ErrorType) -> Void in
dest.failureToLoadAlertForError(error) {}
}
}

@w0mba7 can you post the full promise chain (i.e. what comes before }.error {)?

@orj @nathanhosselton

I get the same error and can't figure out how to solve it. Any tips?

func promise1() -> Promise<Class1>
func promise2() -> Promise<Void>

func promise1and2() -> Promise<Void> {
    return promise1().then { class1object in
        return promise2()
    }.error { error in
        log.error(error)
    }
}

Thanks!

@gunnarblom did you try it with the syntax in orj's answer? { (error:ErrorType) -> Void in

Adding the parameter types is what worked for me. Swift compiler needs a little type inference help.

This has been bugging me over and over again and I can't quite figure out when it does and does not work. In most cases, helping the type inference along a little bit will help, but I'm currently in a situation where even that doesn't help.

A simplified version of the code would be this:

class AuthenticationRequest {
  // ...
  func perform() -> Promise<AuthenticationResponse> {
    // ...
  }
}

class User {
  public static func login(userName userName: String, password: String? = nil) -> Promise<User> {
    // some setup
    return AuthenticationRequest(forUser: currentUser, withPassword: password).perform().then { response -> Promise<User> in
      // some handling of the response
      // ...

      return Promise(currentUser)
    }
  }
}

func processCredentials() -> Promise<()> {
  let login = User.login(userName: userName, password: password).asVoid()  // <- Tried this with and without the 'asVoid'

  return login.error { (error: ErrorType) -> Void in  // Cannot call value of non-function type 'ErrorType?'
    print("something")
  }
}

I can't be much more explicit about types than this, and the compiler still rejects it...

Basically, I'm not entirely sure if Swift supports having both a property and a function with the same name. I mean, it should be possible for the compiler to correctly infer the type and make it work - I'm just not sure if it's supposed to. The question, then, becomes "should both of these things be called error?"

Browsing around things like StackOverflow, I think this might not be legal Swift (but somehow, the compiler approves it, which might be a bug triggered by the fact that one of them is in an extension while the other is in the Promise<T> class itself). This post has a very convincing argument about allowing the use of the same names for properties and methods.

We should start a Swift 3 branch, which necessitates a bump to PromiseKit 4.0, and in there remove the error property.

Maybe we should just bump it now, the lack of a stable ABI as things stands makes maintaining semantic versioning a futile effort.

I understand there's a lot of pain involved right now in maintaining such a codebase as this for the... slightly unstable demands of the Swift compiler throughout versions, especially when it comes to type inference and all. Still, I really appreciate the effort because the project I'm currently on would have been a lot - that is, a _lot_ harder without PromiseKit.

Having said that, I think any call will do. Becoming Swift-standards compliant sooner rather than later would have my preference, simply because it occurs to me that the compiler bug (again, I'm speculating, but it seems plausible) which makes this possible might be removed by Apple at any point right now...

The easiest fix from a library standpoint would probably be to just duplicate the error method and properties with different names, so that you're still backwards compatible but also allow escaping situations such as this one, where there is no really good way to tell the compiler which one we need. For example, if the error method was aliased as onError or handle or some other dramatically bad name I can come up with, I might just work around this bug for now...

The easiest fix from a library standpoint would probably be to just duplicate the error method and properties with different names,

Great point, not sure why I didn't think of this. PR welcome. My initial ideas would be onError and errorValue

Here you go. Comments are welcome - although I have worked with Swift a lot over the last eight months, libraries and testing are not yet the points I know most about.

It happens to me often when the code within the error block has compiler errors. For instance:

API.instance.cancelTripRequest(tripRequest).then { _ -> Void in
    self.onTripRequestCancelled(tripRequest)
}.error { error in // Cannot call value of non-function type 'ErrorType?'
    Kugel.publish(TripRequestCancelFailed, object: error.nsError)
}

But the actual error is within the error block: TripRequestCancelFailed is a static member of the current class, not a global variable. A good way to debug this is by moving your error code outside of the error block, and see what the compiler says.

Not sure it's the cause of your issue, but I thought it was worth mentioning.

@ldiqual this was my problem as well, hard to track down when you are focusing on the compiler confusion issues

With the merging of #397 as an interim workaround, this issue should now be permanently resolved. If Swift is still not able to properly infer the use of error() by the time we next reach a point where a major semantic version bump is required, we will rename it. More than likely this will not be until Swift 3.0 is released.

Was this page helpful?
0 / 5 - 0 ratings