Swift should implement "Either" type with "?." operator for functional error propagation
| Originator: | jlieske | ||
| Number: | rdar://17228902 | Date Originated: | 09-Jun-2014 01:08 AM |
| Status: | Duplicate | Resolved: | |
| Product: | OS X SDK | Product Version: | Xcode6 beta: Version 6.0 (6A215l) |
| Classification: | Enhancement | Reproducible: | Always |
The Swift language does not support exceptions, which makes it easier to reason about the behavior of code. However, the current standard library lacks an easy mechanism to propagate errors from computations with results.
The existing NSErrorPointer mechanism forces code to manually pass the error result holder to every call that can result in an error. This mechanism works, but it clutters up the code with error-handling boilerplate.
It would be more elegant to return errors as part of a union type. Haskell and Scala have the “Either” type for that; the “Left” case holds errors, and the “Right” case holds success results. Scala also has the “Try” type, which has monadic behavior like Swift’s Optional type.
I propose adding an enum type called “Result” to the standard Swift library. It would have “Success” and “Error” cases. Like Scala’s “Try” monad, it applies functions to “Success” results and simply propagates “Error” results.
In addition, I propose extending Swift’s conditional member access “?.” syntax to work with the “Result” monad.
The following code sample shows a possible declaration of “Result” along with sample code to exercise it. It also shows the ideal “?.” syntax in a comment.
(Note that it currently fails to compile; I have reported that at rdar://17228613)
enum Result<SuccessType, ErrorType> {
case Success(SuccessType)
case Error(ErrorType)
func getLogicValue() -> Bool {
switch self {
case .Success(_):
return true
case .Error(_):
return false
}
}
func map<U>(f: (SuccessType) -> U) -> Result<U,ErrorType> {
switch self {
case let .Success(value):
return .Success(f(value))
case let .Error(error):
return .Error(error)
}
}
func bind<U>(f: (SuccessType) -> Result<U,ErrorType>) -> Result<U,ErrorType> {
switch self {
case let .Success(value):
return f(value)
case let .Error(error):
return .Error(error)
}
}
}
/* Ideal syntax:
let result = NSData.myApp_dataWithContentsOfFile("filename.json")?.myApp_parseJSON()?.myApp_lookupString("name")?.uppercaseString
*/
let result = NSData.myApp_dataWithContentsOfFile("filename.json")
.bind({$0.myApp_parseJSON()})
.bind({$0.myApp_lookupString("name")})
.map({$0.uppercaseString})
switch result {
case let .Success(value):
println("Success: "+value)
case let .Error(error):
println("Error: "+error.localizedDescription)
}
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!
Apple closed as duplicate of rdar://17158652
NSData and NSDictionary extensions for code sample
}