Last active
February 3, 2018 23:22
-
-
Save rnapier/72f50e8ef1e08ef4aa7c to your computer and use it in GitHub Desktop.
Comparing Result vs ErrorPointer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) |
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
Wow
err<T>
is clever and I didn't appreciate what ceremonyResult
introduces.The revamped
if let
construct works like Scalafor
comprehensions which are implemented withflatMap
(i.e.bind
) except exclusively forOptional
. 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.Optional
-boxed result andNSErrorPointer
.NSErrorPointer
would need thread-localNSError
s in practice.>>-
could be extended forOptional
,List
, etc. whileif let
isOptional
-only (nativeResult
is on my wish list though).>>-
is functionally composable.I really do like the succinctness of your
NSErrorPointer
application!