From 9a92d2a49ea676dfd63ef1f2085ef170754058c8 Mon Sep 17 00:00:00 2001 From: Jason Larsen Date: Fri, 12 Jun 2015 11:52:42 -0600 Subject: [PATCH 1/5] add usage documentation to README --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 612008e..d35a886 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,74 @@ This is a Swift µframework providing `Result`. `Result` values are either successful (wrapping `Value`) or failed (wrapping `Error`). This is similar to Swift’s native `Optional` type, with the addition of an error value to pass some error code, message, or object along to be logged or displayed to the user. - ## Use -[API documentation](http://cocoadocs.org/docsets/Result/) is in the source. +Use `Result` whenever an operation has the possibility of failure. Consider the following example of a function that tries to extract a `String` for a given key from a JSON `Dictionary`. + +```swift +typealias JSONObject = [String:AnyObject] + +enum JSONError : ErrorType { + case NoSuchKey(String) + case TypeMismatch +} + +func stringForKey(json: JSONObject, key: String) -> Result { + guard let value = json[key] else { + return .Failure(.NoSuchKey(key)) + } + + if let value = value as? String { + return .Success(value) + } + else { + return .Failure(.TypeMismatch) + } +} +``` + +This function provides a more robust wrapper around the default subscripting provided by `Dictionary`. Rather than return `AnyObject?`, it returns a `Result` that either contains the `String` value for the given key, or an `ErrorType` detailing what went wrong. + +One simple way to handle a `Result` is to deconstruct it using a `switch` statement. + +```swift +switch stringForKey(json, key: "email") { + +case let .Success(email): + print("The email is \(email)") + +case let .Failure(JSONError.NoSuchKey(key)): + print("\(key) is not a valid key") + +case .Failure(JSONError.TypeMismatch): + print("Didn't have the right type") +} +``` + +Using a `switch` statement allows powerful pattern matching, and ensures all possible results are covered. Swift 2.0 offers new ways to deconstruct enums like the `if-case` statement, but be wary as such methods do not ensure errors are handled. + +Other methods available for processing `Result` are detailed in the [API documentation](http://cocoadocs.org/docsets/Result/). + +## Result vs. Throws + +Swift 2.0 introduced error handling via throwing error types. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction also enables powerful functionality such as `map` and `flatMap`. + +Since dealing with APIs that throw is common, you can convert functions such functions into a `Result` by using the `materialize` method. [Note: due to compiler issues, `materialize` is not currently available] + +## Higher Order Functions + +`map` is analagous to `map` for Swift's `Optional`. `map` transforms a `Result` into a `Result` of a new type. It does this by taking a block that processes the `Value` type of the `Result`. This block is only applied to the value if the `Result` is a success. In the case that the `Result` is a `Failure`, the error is re-wrapped in the new `Result`. + +```swift +let idResult: Result = parseInt(json, key:"id") +let idStringResult: Result = idResult.map { id in String(id) } +``` + +Here, `idStringResult` is either the id as a `String`, or carries over the `.Failure` from the previous expression. + +`flatMap` is similar to `map` in that in transforms the `Result` into another `Result`. However, the function passed into `flatMap` must return a `Result`. +An in depth discussion of `map` and `flatMap` is beyond the scope of this documentation. If you would like a deeper understanding, read about functors and monads. This article is a good place to [start](http://www.javiersoto.me/post/106875422394). ## Integration From 51bd84b6ae7aa3a86de998f27f4692d9a0c0a55a Mon Sep 17 00:00:00 2001 From: Jason Larsen Date: Fri, 12 Jun 2015 13:48:40 -0600 Subject: [PATCH 2/5] Update README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d35a886..7e2b3a1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ This is a Swift µframework providing `Result`. -`Result` values are either successful (wrapping `Value`) or failed (wrapping `Error`). This is similar to Swift’s native `Optional` type, with the addition of an error value to pass some error code, message, or object along to be logged or displayed to the user. +`Result` values are either successful (wrapping `Value`) or failed (wrapping `Error`). This is similar to Swift’s native `Optional` type: `Success` is like `Some`, and `Failure` is like `None` except with an associated `ErrorType` value. The addition of an associated `ErrorType` allows errors to be passed along for logging or displaying to the user. + +Using this µframework instead of rolling your own `Result` type allows you to easily interface with other frameworks that also use `Result`. ## Use @@ -57,20 +59,21 @@ Other methods available for processing `Result` are detailed in the [API documen ## Result vs. Throws -Swift 2.0 introduced error handling via throwing error types. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction also enables powerful functionality such as `map` and `flatMap`. +Swift 2.0 introduces error handling via throwing and catching `ErrorType`. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction also enables powerful functionality such as `map` and `flatMap`. Since dealing with APIs that throw is common, you can convert functions such functions into a `Result` by using the `materialize` method. [Note: due to compiler issues, `materialize` is not currently available] ## Higher Order Functions -`map` is analagous to `map` for Swift's `Optional`. `map` transforms a `Result` into a `Result` of a new type. It does this by taking a block that processes the `Value` type of the `Result`. This block is only applied to the value if the `Result` is a success. In the case that the `Result` is a `Failure`, the error is re-wrapped in the new `Result`. +`map` and `flatMap` operate the same as `Optional.map` and `Optional.flatMap` except they apply to `Result`. + +`map` transforms a `Result` into a `Result` of a new type. It does this by taking a function that transforms the `Value` type into a new value. This transformation is only applied in the case of a `Success`. In the case of a `Failure`, the associated error is re-wrapped in the new `Result`. ```swift -let idResult: Result = parseInt(json, key:"id") -let idStringResult: Result = idResult.map { id in String(id) } +let idResult: Result = parseInt(json, key:"id").map { id in String(id) } ``` -Here, `idStringResult` is either the id as a `String`, or carries over the `.Failure` from the previous expression. +Here, the final result is either the id as a `String`, or carries over the `.Failure` from the previous expression. `flatMap` is similar to `map` in that in transforms the `Result` into another `Result`. However, the function passed into `flatMap` must return a `Result`. From 27426e76810973157e4336261833777964d9d0e6 Mon Sep 17 00:00:00 2001 From: Jason Larsen Date: Fri, 12 Jun 2015 14:05:48 -0600 Subject: [PATCH 3/5] fix `map` example Make it fit on one line and fix the function name so it's consistent with the previous example --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e2b3a1..1396f9a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,8 @@ Since dealing with APIs that throw is common, you can convert functions such fun `map` transforms a `Result` into a `Result` of a new type. It does this by taking a function that transforms the `Value` type into a new value. This transformation is only applied in the case of a `Success`. In the case of a `Failure`, the associated error is re-wrapped in the new `Result`. ```swift -let idResult: Result = parseInt(json, key:"id").map { id in String(id) } +// transforms a Result to a Result +let idResult = intForKey(json, key:"id").map { id in String(id) } ``` Here, the final result is either the id as a `String`, or carries over the `.Failure` from the previous expression. From c4d86f165c6d556c758741d03c426c56d8aaabd5 Mon Sep 17 00:00:00 2001 From: Jason Larsen Date: Fri, 12 Jun 2015 14:06:47 -0600 Subject: [PATCH 4/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1396f9a..70e0a87 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Since dealing with APIs that throw is common, you can convert functions such fun let idResult = intForKey(json, key:"id").map { id in String(id) } ``` -Here, the final result is either the id as a `String`, or carries over the `.Failure` from the previous expression. +Here, the final result is either the id as a `String`, or carries over the `.Failure` from the previous result. `flatMap` is similar to `map` in that in transforms the `Result` into another `Result`. However, the function passed into `flatMap` must return a `Result`. From 33ba9609a2bb3da0cc479df857429cd95edaf8b8 Mon Sep 17 00:00:00 2001 From: Jason Larsen Date: Sat, 13 Jun 2015 15:52:34 -0600 Subject: [PATCH 5/5] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70e0a87..1245165 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ Other methods available for processing `Result` are detailed in the [API documen ## Result vs. Throws -Swift 2.0 introduces error handling via throwing and catching `ErrorType`. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction also enables powerful functionality such as `map` and `flatMap`. +Swift 2.0 introduces error handling via throwing and catching `ErrorType`. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction allows enables powerful functionality such as `map` and `flatMap`, making `Result` more composable than `throw`. -Since dealing with APIs that throw is common, you can convert functions such functions into a `Result` by using the `materialize` method. [Note: due to compiler issues, `materialize` is not currently available] +Since dealing with APIs that throw is common, you can convert functions such functions into a `Result` by using the `materialize` method. Conversely, a `Result` can be used to throw an error by calling `dematerialize`. [Note: due to compiler issues, `materialize` is not currently available] ## Higher Order Functions