Swift 2: throwing errors in computed properties

To indicate that a function or method can throw an error, we write the

throws
keyword in its declaration.


How would we indicate this for computed properties. For example:



    public var isSuccess:  Bool {
        if let r = result {
            switch r {
                case .Success: return true
                case .Failure: return false
            }
        }
        else {
            throw FutureError.NotCompleted
        }
    }



According the documentation, it seems it is not (yet) implemented.


Possibly, it may read as this (does not compile):


    public var isSuccess throws:  Bool {
        if let r = result {
            switch r {
                case .Success: return true
                case .Failure: return false
            }
        }
        else {
            throw FutureError.NotCompleted
        }
    }



    public var isSuccess:  Bool { get throws {
        if let r = result {
            switch r {
                case .Success: return true
                case .Failure: return false
            }
        }
        else {
            throw FutureError.NotCompleted
        }}
    }

I think a better workaround is to change the getter into a function with no parameters.


However, I suggest you think long and hard before you propagate errors (via "throws") as in your example. When the propagated error is something that "can't happen", there's really no point in distorting the entire chain of calls to handle errors, since there's really nothing you can do about them. How (for example) can a utility random number generator fail in a way that isn't a programmer error?


I know that proponents of exception handling like to think that if you propagate the exception up to the top level, cleaning up via catch blocks on the way up, the app can continue. But in the case of that kind of error, there's a good argument to say that the app should not be allowed to continue. If the choice is a crash or a future data corruption of potentially unlimited scope, which would the user rather have?


[Also, I know it's not your main point, but if you do want to swallow the error as above, it's a crummy idea to use 'assert', which will only do anything in debug mode. Since this getter really doesn't know how to handle an error, an error is going to lead to meaningless and unpredictable in the release configuration, which is where it really matters that it should not be meaningless and unpredictable. I believe it's generally preferable to use 'precondition', 'preconditionFailure' or 'fatalError' to force a crash.]

Thank you very much!

I suggest you think long and hard before you propagate errors (via "throws") as in your example. When the propagated error is something that "can't happen", there's really no point in distorting the entire chain of calls to handle errors


I honestly wasn't sure whether what I was trying to do was the reasonable way to be approaching this. And so I very much welcome your suggestion. I really shouild be handling these conditions closer to where they occur.


Although this is a digression,

How (for example) can a utility random number generator fail in a way that isn't a programmer error?

According to https://developer.apple.com/library/prerelease/ios/documentation/Security/Reference/RandomizationReference/index.html#//apple_ref/c/func/SecRandomCopyBytes

Return Value
Returns 0 if the function completed successfully and -1 if there was an error. Check the errno system variable for the error.

But that doesn't take away from your larger point that there is little value in me passing that error far up the chain. In randomInRange(lower: Int, upper: Int), I also have things like


  guard lower <= upper else { throw ... }


But I should catch and handle those there instead of having randomInRange() throw.


Your point about 'assert()' is spot on and extremely helpful. I will look at how to appropropriate force a crash.


Thanks again!

TL;DR: properties reflect the current state of an object. If they cannot provide state they should return `nil`. Throwing is for functions that mutate state or perform some operation.


IMO error handling in Swift 2 is (intended to be) for runtime errors. Querying a property when it is not yet valid is not a runtime error, it is a programmer error (they should have first checked if the Promise is pending before asking for its resolution status).


Thus IMO, return an optional or change the property to be called something like "state" and return an enum for all states.


With API design it is important to consider which is more readable:


if let succeeded = promise.isSucceeded where succeeded {
    //…

}


// versus:


do {
    if try promise.isSucceeded {
   }

} catch Future.Pending {
    //…

}


// versus:


if case .Succeeded = promise.state {
    //…

}

For me it's the final option.

Swift 2: throwing errors in computed properties
 
 
Q