Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active February 3, 2018 23:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rnapier/72f50e8ef1e08ef4aa7c to your computer and use it in GitHub Desktop.
Save rnapier/72f50e8ef1e08ef4aa7c to your computer and use it in GitHub Desktop.
Comparing Result vs ErrorPointer
// Parsing w/ Result and >>-
import Foundation
import Result
let queryBase = "http://en.wikipedia.org/w/api.php?"
+ "action=opensearch&format=json&search="
func mkError(message: String) -> NSError {
return NSError(domain: "", code: 0,
userInfo: [NSLocalizedDescriptionKey: message])
}
func URLForSearch(search: String) -> Result<NSURL, NSError> {
return
Result(search.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding),
failWith: mkError("Malformed Search: \(search)"))
>>- { Result(NSURL(string: queryBase + $0),
failWith: mkError("Malformed URL: \($0)"))}
}
func DataForURL(url: NSURL) -> Result<NSData, NSError> {
return try { error in
NSURLConnection.sendSynchronousRequest(NSURLRequest(URL: url),
returningResponse: nil, error: error)}
}
func JSONForData(data: NSData) -> Result<AnyObject, NSError> {
return try { error in
NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions(0), error: error)}
}
func ParseJSON(json: AnyObject) -> Result<[String], NSError> {
return
Result(json as? [AnyObject], failWith: mkError("Expected array. Received: \(json)"))
>>- { Result($0.count >= 2 ? $0 : nil, failWith: mkError("Array incorrect size: \($0)"))}
>>- { Result($0[1] as? [String], failWith: mkError("Malformed array: \($0)"))}
}
func pagesForSearch(search: String) -> Result<[String], NSError> {
return URLForSearch(search)
>>- DataForURL
>>- JSONForData
>>- ParseJSON
}
pagesForSearch("Albert ").description
// Parsing w/ NSErrorPointer and built-in Swift
import Foundation
let queryBase = "http://en.wikipedia.org/w/api.php?"
+ "action=opensearch&format=json&search="
func mkError(message: String) -> NSError {
return NSError(domain: "", code: 0,
userInfo: [NSLocalizedDescriptionKey: message])
}
func err<T>(err: NSErrorPointer, message: String) -> T? {
if err != nil { err.memory = mkError(message) }
return nil
}
func err(err: NSErrorPointer, message: String) -> Bool {
if err != nil { err.memory = mkError(message) }
return false
}
func URLForSearch(search: String, #error: NSErrorPointer) -> NSURL? {
if let
encoded = search
.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
?? err(error, "Malformed Search: \(search)") {
return NSURL(string: queryBase + encoded)
?? err(error, "Malformed URL: \(encoded)")
}
return nil
}
func DataForURL(url: NSURL, #error: NSErrorPointer) -> NSData? {
return NSURLConnection.sendSynchronousRequest(NSURLRequest(URL: url),
returningResponse: nil, error: error)
}
func JSONForData(data: NSData, #error: NSErrorPointer) -> AnyObject? {
return NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions(0), error: error)
}
func ParseJSON(json: AnyObject, #error: NSErrorPointer) -> [String]? {
if let array = json as? [AnyObject] ?? err(error, "Expected array. Received: \(json)")
where array.count >= 2 || err(error, "Array incorrect size: \(array)") {
return array[1] as? [String] ?? err(error, "Malformed array: \(array)")
}
return nil
}
func pagesForSearch(search: String, #error: NSErrorPointer) -> [String]? {
if let
url = URLForSearch(search, error: error),
data = DataForURL(url, error: error),
json: AnyObject = JSONForData(data, error: error) {
return ParseJSON(json, error: error)
}
return nil
}
var error: NSError?
pagesForSearch("Albert ", error: &error)
@texastoland
Copy link

Wow err<T> is clever and I didn't appreciate what ceremony Result introduces.

The revamped if let construct works like Scala for comprehensions which are implemented with flatMap (i.e. bind) except exclusively for Optional. I guess it shouldn't be surprising how close it works like >>-.

The big benefit to the NSErrorPointer version is readability to someone intimidated by >>-. Result still looks more promising subjectively though:

  • NSErrorPointer obfuscates the signature because it's not an input or dependency.
  • Failures essentially get reported by both the Optional-boxed result and NSErrorPointer.
  • NSErrorPointer would need thread-local NSErrors in practice.
  • >>- could be extended for Optional, List, etc. while if let is Optional-only (native Result is on my wish list though).
  • >>- is functionally composable.

I really do like the succinctness of your NSErrorPointer application!

@ordovician
Copy link

Hard to say which methods is better. But I really liked you introduced error reporting for each optional unwrapping with the use of ?? and err function.

What makes me lean towards the latter approach is that it doesn't require a fundamental change from how Apple and Cocoa already does it, and is not too hard for people not that well versed in functional programming to understand. The ?? and err() trick should be familiar to a lot of old school C programmers. Ruby and Lua devs do that sort of thing a lot too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment