├── Functor and Monad in Swift ├── FunctorAndMonad.md └── playground │ ├── FunctorAndMonadPlayground.md │ ├── FunctorAndMonadPlayground.playground │ ├── Documentation │ │ ├── section-0.html │ │ ├── section-10.html │ │ ├── section-12.html │ │ ├── section-14.html │ │ ├── section-16.html │ │ ├── section-18.html │ │ ├── section-2.html │ │ ├── section-20.html │ │ ├── section-22.html │ │ ├── section-24.html │ │ ├── section-26.html │ │ ├── section-28.html │ │ ├── section-30.html │ │ ├── section-4.html │ │ ├── section-6.html │ │ ├── section-8.html │ │ └── stylesheet.css │ ├── Resources │ │ └── file.txt │ ├── contents.xcplayground │ ├── section-1.swift │ ├── section-10-original.swift │ ├── section-10.swift │ ├── section-11.swift │ ├── section-12-original.swift │ ├── section-12.swift │ ├── section-13.swift │ ├── section-14-original.swift │ ├── section-14.swift │ ├── section-15.swift │ ├── section-16-original.swift │ ├── section-16.swift │ ├── section-17.swift │ ├── section-18-original.swift │ ├── section-18.swift │ ├── section-19.swift │ ├── section-2-original.swift │ ├── section-2.swift │ ├── section-20-original.swift │ ├── section-20.swift │ ├── section-21.swift │ ├── section-22-original.swift │ ├── section-22.swift │ ├── section-23.swift │ ├── section-24-original.swift │ ├── section-24.swift │ ├── section-25.swift │ ├── section-26-original.swift │ ├── section-26.swift │ ├── section-27.swift │ ├── section-28-original.swift │ ├── section-28.swift │ ├── section-29.swift │ ├── section-3.swift │ ├── section-30-original.swift │ ├── section-30.swift │ ├── section-4-original.swift │ ├── section-4.swift │ ├── section-5.swift │ ├── section-6-original.swift │ ├── section-6.swift │ ├── section-7.swift │ ├── section-8-original.swift │ ├── section-8.swift │ ├── section-9.swift │ └── timeline.xctimeline │ └── Makefile ├── README.md └── ReactiveCocoa └── Reactive Cocoa.md /Functor and Monad in Swift/FunctorAndMonad.md: -------------------------------------------------------------------------------- 1 | # Functor and Monad in Swift 2 | 3 | I have been trying to teach myself Functional Programming since late 2013. Many of the concepts are very daunting because of their somewhat academic nature. 4 | 5 | Since I'm obviously not an expert, I intend this to be a very practical post. You will find many posts trying to explain what a Monad is, [some of them trying a bit too hard to come up with similes](http://blog.plover.com/prog/burritos.html), but hopefully the sample code here will illustrate some of the concepts better. 6 | 7 | It wasn't until recently that I finally could say that I *got* what Monad means. Let's explore why this concept even exists, and how it can help you when writing Swift code. 8 | 9 | ## Map 10 | One of the first things that we got to see at the 2014 WWDC with the introduction of Swift was that we could use the `map` function with the collection types. Let's focus on Swift's `Array`. 11 | 12 | ```swift 13 | let numbers = [1, 2, 3] 14 | 15 | let doubledNumbers = numbers.map { $0 * 2 } 16 | // doubledNumbers: 2, 4, 6 17 | ``` 18 | 19 | The benefit of this pattern is that we can very clearly express the transformation that we're trying to apply on the list of elements (in this case, doubling their value). Compare this with the imperative approach: 20 | 21 | ```swift 22 | var doubledImperative: [Int] = [] 23 | for number in numbers { 24 | doubledImperative.append(number * 2) 25 | } 26 | // doubledImperative: 2, 4, 6 27 | ``` 28 | 29 | It's not about solving it in a one-liner vs 3 lines, but with the former concise implementation, there's a significantly higher signal-to-noise ratio. `map` allows us to express *what* we want to achieve, rather than *how* this is implemented. This eases our ability to reason about code when we read it. 30 | 31 | But `map` doesn't only make sense on `Array`. `map` is a higher-order function that can be implemented on just any container type. That is, any type that, one way or another, wraps one or multiple values inside. 32 | 33 | Let's look at another example: `Optional`. `Optional` is a container type that wraps a value, or the absence of it. 34 | 35 | ```swift 36 | let number = Optional(815) 37 | 38 | let transformedNumber = number.map { $0 * 2 }.map { $0 % 2 == 0 } 39 | // transformedNumber: Optional.Some(true) 40 | ``` 41 | 42 | The benefit of `map` in `Optional` is that it will handle nil values for us. If we're trying to operate on a value that *may* be `nil`, we can use `Optional.map` to apply those transformations, and end up with `nil` if the original value was `nil`, but without having to resort to nested `if let` to unwrap the optional. 43 | 44 | ```swift 45 | let nilNumber: Int? = .None 46 | 47 | let transformedNilNumber = nilNumber.map { $0 * 2 }.map { $0 % 2 == 0 } 48 | // transformedNilNumber: None 49 | ``` 50 | 51 | From this we can extrapolate that **`map`, when implemented on different container types, can have slightly different behaviors**, depending on the semantics of that type. For example, it only makes sense to transform the value inside an `Optional` when there's actually a value inside. 52 | 53 | This is the general **signature of a `map` method**, when implemented on a `Container` type, that wraps values of type `T`: 54 | 55 | ```swift 56 | func map(transformFunction: T -> U) -> Container 57 | ``` 58 | 59 | Let's analyze that signature by looking at the types. 60 | `T` is the type of elements in the current container, `U` will be the type of the elements in the container that will be returned. This allows us to, for example, map an array of strings, to an array of `Int`s that contains the lengths of each of the `String`s in the original array. 61 | 62 | We provide a function that takes a `T` value, and returns a value of type `U`. `map` will then use this function to create another `Container` instance, where the original values are replaced by the ones returned by the `transformFunction`. 63 | 64 | ## Implementing `map` with our own type 65 | 66 | Let's implement our own container type. A `Result` enum is a pattern that you will see in a lot of open source Swift code today. [This brings several benefits to an API when used instead of the old Obj-C NSError-by-reference argument](https://gist.github.com/andymatuschak/2b311461caf740f5726f#comment-1364205). 67 | 68 | We could define it like this: 69 | 70 | ```swift 71 | enum Result { 72 | case Value(T) 73 | case Error(NSError) 74 | } 75 | ``` 76 | 77 | This is an implementation of a type known as `Either` in some programming languages. Only in this case we're forcing one of the types to be an `NSError` instead of being generic, since we're going to use it to report the result of an operation. 78 | 79 | Conceptually, `Result` is very similar to `Optional`: it wraps a value of an arbitrary type, that may or may not be present. In this case, however, it may additional tell us why the value is not there. 80 | 81 | To see an example, let's implement a function that reads the contents of a file and returns the result as a `Result` object: 82 | 83 | ```swift 84 | func dataWithContentsOfFile(file: String, encoding: NSStringEncoding) -> Result { 85 | var error: NSError? 86 | 87 | if let data = NSData(contentsOfFile: file, options: .allZeros, error: &error) { 88 | return .Value(data) 89 | } 90 | else { 91 | return .Error(error!) 92 | } 93 | } 94 | ``` 95 | 96 | Easy enough. This function will return *either* an `NSData` object, or an `NSError` in case the file can't be read. 97 | 98 | Like we did before, we may want to apply some transformation to the read value. However, like in the case before, we would need to check that we have a value every step of the way, which may result in ugly nested `if let`s or `switch` statements. Let's leverage `map` like we did before. In this case, we will only want to apply such transformation if we have a value. If we don't, we can simply pass the same error through. 99 | 100 | Imagine that we wanted to read a file with string contents. We would get an `NSData`, that then we need to transform into a `String`. Then say that we want to turn it into uppercase: 101 | 102 | ``` 103 | NSData -> String -> String 104 | ``` 105 | 106 | We can do this with a series of `map` transformations (*we'll discuss the implementation of `map` later*): 107 | 108 | ```swift 109 | let data: Result = dataWithContentsOfFile(path, NSUTF8StringEncoding) 110 | 111 | let uppercaseContents: Result = data.map { NSString(data: $0, encoding: NSUTF8StringEncoding)! }.map { $0.uppercaseString } 112 | ``` 113 | 114 | Similar to the early example with `map` on `Array`s, this code is a lot more expressive. It simply declares what we want to accomplish, with no boilerplate. 115 | 116 | In comparison, this is what the above code would look like without the use of `map`: 117 | 118 | ```swift 119 | let data: Result = dataWithContentsOfFile(path, NSUTF8StringEncoding) 120 | 121 | var stringContents: String? 122 | 123 | switch data { 124 | case let .Value(value): 125 | stringContents = NSString(data: value, encoding: NSUTF8StringEncoding) 126 | case let .Error(error): 127 | break 128 | } 129 | 130 | let uppercaseContents: String? = stringContents?.uppercaseString 131 | ``` 132 | 133 | How would `Result.map` be implemented? Let's take a look: 134 | 135 | ```swift 136 | extension Result { 137 | func map(f: T -> U) -> Result { 138 | switch self { 139 | case let .Value(value): 140 | return Result.Value(f(value)) 141 | case let .Error(error): 142 | return Result.Error(error) 143 | } 144 | } 145 | } 146 | ``` 147 | 148 | Again, the transformation function `f` takes a value of type `T` (in the above example, `NSData`) and returns a value of type `U` (`String`). After calling `map`, we'll get a `Result` (`Result`) from an initial `Result` (`Result`). 149 | We only call `f` whenever we start with a value, and we simply return another `Result` with the same error otherwise. 150 | 151 | ## Functors 152 | 153 | We've seen what `map` can do when implemented on a container type, like `Optional`, `Array` or `Result`. To recap, it allows us to get a new container, where the value(s) wrapped inside are transformed according to a function. 154 | So **what's a Functor** you may ask? A Functor is any type that implements `map`. That's the whole story. 155 | 156 | Once you know what a functor is, we can talk about some types like `Dictionary` or even closures, and by saying that they're functors, you will immediately know of something you can do with them. 157 | 158 | ## Monads 159 | 160 | In the earlier example, we used the transformation function to return another *value*, but what if we wanted to use it to return a new `Result` object? Put another way, what if the transformation operation that we're passing to `map` can fail with an error as well? Let's look at what the types would look like. 161 | 162 | ```swift 163 | func map(f: T -> U) -> Result 164 | ``` 165 | 166 | In our example, `T` is an `NSData` that we're converting into `U`, a `Result`. So let's replace that in the signature: 167 | 168 | ```swift 169 | func map(f: NSData -> Result) -> Result> 170 | ``` 171 | 172 | Notice the nested `Result`s in the return type. This is probably not what we'll want. But it's OK. We can implement a function that takes the nested `Result`, and *flattens* it into a simple `Result`: 173 | 174 | ```swift 175 | extension Result { 176 | static func flatten(result: Result>) -> Result { 177 | switch result { 178 | case let .Value(innerResult): 179 | return innerResult 180 | case let .Error(error): 181 | return Result.Error(error) 182 | } 183 | } 184 | } 185 | ``` 186 | 187 | This `flatten` function takes a nested `Result` with a `T` inside, and return a single `Result` simply by extracting the inner object inside the `Value`, or the `Error`. 188 | 189 | A `flatten` function can be found in other contexts. For example, one can `flatten` an array of arrays into a contiguous, one-dimensional array. 190 | 191 | With this, we can implement our `Result -> Result` transformation by combining `map` and `flatten`: 192 | 193 | ```swift 194 | let stringResult = Result.flatten(data.map { (data: NSData) -> (Result) in 195 | if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { 196 | return Result.Value(string) 197 | } 198 | else { 199 | return Result.Error(NSError(domain: "com.javisoto.es.error_domain", code: JSErrorCodeInvalidStringData, userInfo: nil)) 200 | } 201 | }) 202 | ``` 203 | 204 | This is so common, that you will find this defined in many places as `flatMap` or `flattenMap`, which we could implement for `Result` like this: 205 | 206 | ```swift 207 | extension Result { 208 | func flatMap(f: T -> Result) -> Result { 209 | return Result.flatten(map(f)) 210 | } 211 | } 212 | ``` 213 | 214 | And with that, we turned our `Result` type into a Monad! **A Monad is a type of Functor. A type which, along with `map`, implements a `flatMap` function** (*sometimes also known as `bind`*) with a signature similar to the one we've seen here. Container types like the ones we presented here are usually Monads, but you will also see that pattern for example in types that encapsulate deferred computation, like `Signal` or `Future`. 215 | 216 | The words **Functor** and **Monad** come from category theory, with which I'm not familiar at all. However, there's value in having names to refer to these concepts. Computer scientists love to come up with names for things. But it's those names that allow us to refer to abstract concepts (*some extremely abstract, like Monad*), and immediately know what we mean (of course, assuming we have the previous knowledge of their meaning). We get the same benefit out of sharing names for things like design patterns (decorator, factory...). 217 | 218 | It took me a very long time to assimilate all the ideas in this blog post, so if you're not familiar with any of this I don't expect you to finish reading this and immediately understand it. 219 | However, I encourage you to create an Xcode playground and try to come up with the implementation for `map`, `flatten` and `flatMap` for `Result` or a similar container type (perhaps try with `Optional` or even `Array`), and use some sample values to play with them. 220 | 221 | And next time you hear the words Functor or Monad, don't be scared :) They're simply design patterns to describe common operations that we can perform on different types. 222 | 223 | *Open source version of the article, where you can create an issue to ask a question or open pull requests: [https://github.com/JaviSoto/Blog-Posts/blob/master/Functor%20and%20Monad%20in%20Swift/FunctorAndMonad.md](https://github.com/JaviSoto/Blog-Posts/blob/master/Functor%20and%20Monad%20in%20Swift/FunctorAndMonad.md)* 224 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.md: -------------------------------------------------------------------------------- 1 | # Functor and Monad in Swift 2 | 3 | I have been trying to teach myself Functional Programming since late 2013. Many of the concepts are very daunting because of their somewhat academic nature. 4 | 5 | Since I'm obviously not an expert, I intend this to be a very practical post. You will find many posts trying to explain what a Monad is, [some of them trying a bit too hard to come up with similes](http://blog.plover.com/prog/burritos.html), but hopefully the sample code here will illustrate some of the concepts better. 6 | 7 | It wasn't until recently that I finally could say that I *got* what Monad means. Let's explore why this concept even exists, and how it can help you when writing Swift code. 8 | 9 | ## Map 10 | One of the first things that we got to see at the 2014 WWDC with the introduction of Swift was that we could use the `map` function with the collection types. Let's focus on Swift's `Array`. 11 | 12 | ```swift 13 | let numbers = [1, 2, 3] 14 | 15 | let doubledNumbers = numbers.map { $0 * 2 } 16 | doubledNumbers 17 | ``` 18 | 19 | The benefit of this pattern is that we can very clearly express the transformation that we're trying to apply on the list of elements (in this case, doubling their value). Compare this with the imperative approach: 20 | 21 | ```swift 22 | var doubledImperative: [Int] = [] 23 | for number in numbers { 24 | doubledImperative.append(number * 2) 25 | } 26 | doubledImperative 27 | ``` 28 | 29 | It's not about solving it in a one-liner vs 3 lines, but with the former concise implementation, there's a significantly higher signal-to-noise ratio. `map` allows us to express *what* we want to achieve, rather than *how* this is implemented. This eases our ability to reason about code when we read it. 30 | 31 | But `map` doesn't only make sense on `Array`. `map` is a higher-order function that can be implemented on just any container type. That is, any type that, one way or another, wraps one or multiple values inside. 32 | 33 | Let's look at another example: `Optional`. `Optional` is a container type that wraps a value, or the absence of it. 34 | 35 | ```swift 36 | let number = Optional(815) 37 | 38 | let transformedNumber = number.map { $0 * 2 }.map { $0 % 2 == 0 } 39 | transformedNumber 40 | ``` 41 | 42 | The benefit of `map` in `Optional` is that it will handle nil values for us. If we're trying to operate on a value that *may* be `nil`, we can use `Optional.map` to apply those transformations, and end up with `nil` if the original value was `nil`, but without having to resort to nested `if let` to unwrap the optional. 43 | 44 | ```swift 45 | let nilNumber: Int? = .None 46 | 47 | let transformedNilNumber = nilNumber.map { $0 * 2 }.map { $0 % 2 == 0 } 48 | transformedNilNumber 49 | ``` 50 | 51 | From this we can extrapolate that **`map`, when implemented on different container types, can have slightly different behaviors**, depending on the semantics of that type. For example, it only makes sense to transform the value inside an `Optional` when there's actually a value inside. 52 | 53 | This is the general **signature of a `map` method**, when implemented on a `Container` type, that wraps values of type `T`: 54 | 55 | ```swift 56 | // func map(transformFunction: T -> U) -> Container 57 | ``` 58 | 59 | Let's analyze that signature by looking at the types. 60 | `T` is the type of elements in the current container, `U` will be the type of the elements in the container that will be returned. This allows us to, for example, map an array of strings, to an array of `Int`s that contains the lengths of each of the `String`s in the original array. 61 | 62 | We provide a function that takes a `T` value, and returns a value of type `U`. `map` will then use this function to create another `Container` instance, where the original values are replaced by the ones returned by the `transformFunction`. 63 | 64 | ## Implementing `map` with our own type 65 | 66 | Let's implement our own container type. A `Result` enum is a pattern that you will see in a lot of open source Swift code today. [This brings several benefits to an API when used instead of the old Obj-C NSError-by-reference argument](https://gist.github.com/andymatuschak/2b311461caf740f5726f#comment-1364205). 67 | 68 | We could define it like this: 69 | 70 | ```swift 71 | import Foundation 72 | 73 | class Box { 74 | let unbox: T 75 | 76 | init(_ value: T) { 77 | self.unbox = value 78 | } 79 | } 80 | 81 | enum Result { 82 | case Value(Box) 83 | case Error(NSError) 84 | } 85 | 86 | extension Result: DebugPrintable { 87 | var debugDescription: String { 88 | switch self { 89 | case let .Value(value): 90 | return "Value: \(toDebugString(value.unbox))" 91 | case let .Error(error): 92 | return "Error: \(toDebugString(error))" 93 | } 94 | } 95 | } 96 | ``` 97 | 98 | *The `Box` class is required to work around a current Swift limitation (`unimplemented IR generation feature non-fixed multi-payload enum layout`)* 99 | 100 | This is an implementation of a type known as `Either` in some programming languages. Only in this case we're forcing one of the types to be an `NSError` instead of being generic, since we're going to use it to report the result of an operation. 101 | 102 | Conceptually, `Result` is very similar to `Optional`: it wraps a value of an arbitrary type, that may or may not be present. In this case, however, it may additional tell us why the value is not there. 103 | 104 | To see an example, let's implement a function that reads the contents of a file and returns the result as a `Result` object: 105 | 106 | ```swift 107 | func dataWithContentsOfFile(file: String, encoding: NSStringEncoding) -> Result { 108 | var error: NSError? 109 | 110 | if let data = NSData(contentsOfFile: file, options: .allZeros, error: &error) { 111 | return .Value(Box(data)) 112 | } 113 | else { 114 | return .Error(error!) 115 | } 116 | } 117 | ``` 118 | 119 | Easy enough. This function will return *either* an `NSData` object, or an `NSError` in case the file can't be read. 120 | 121 | Like we did before, we may want to apply some transformation to the read value. However, like in the case before, we would need to check that we have a value every step of the way, which may result in ugly nested `if let`s or `switch` statements. Let's leverage `map` like we did before. In this case, we will only want to apply such transformation if we have a value. If we don't, we can simply pass the same error through. 122 | 123 | How would `Result.map` be implemented? Let's take a look: 124 | 125 | ```swift 126 | extension Result { 127 | func map(f: T -> U) -> Result { 128 | switch self { 129 | case let .Value(value): 130 | return Result.Value(Box(f(value.unbox))) 131 | case let .Error(error): 132 | return Result.Error(error) 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | Again, the transformation function `f` takes a value of type `T` (in the above example, `NSData`) and returns a value of type `U` (`String`). After calling `map`, we'll get a `Result` (`Result`) from an initial `Result` (`Result`). 139 | We only call `f` whenever we start with a value, and we simply return another `Result` with the same error otherwise. 140 | 141 | Imagine that we wanted to read a file with string contents. We would get an `NSData`, that then we need to transform into a `String`. Then say that we want to turn it into uppercase: 142 | 143 | ``` 144 | NSData -> String -> String 145 | ``` 146 | 147 | We can do this with a series of `map` transformations: 148 | 149 | ```swift 150 | let path = NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent("file.txt") 151 | let data: Result = dataWithContentsOfFile(path, NSUTF8StringEncoding) 152 | 153 | let uppercaseContents: Result = data.map { NSString(data: $0, encoding: NSUTF8StringEncoding)! }.map { $0.uppercaseString } 154 | uppercaseContents 155 | uppercaseContents.debugDescription 156 | ``` 157 | 158 | Similar to the early example with `map` on `Array`s, this code is a lot more expressive. It simply declares what we want to accomplish, with no boilerplate. 159 | 160 | In comparison, this is what the above code would look like without the use of `map`: 161 | 162 | ```swift 163 | var stringContents: String? 164 | 165 | switch data { 166 | case let .Value(value): 167 | stringContents = NSString(data: value.unbox, encoding: NSUTF8StringEncoding) 168 | case let .Error(error): 169 | break 170 | } 171 | 172 | let uppercaseContents2: String? = stringContents?.uppercaseString 173 | uppercaseContents2 174 | ``` 175 | 176 | ## Functors 177 | 178 | We've seen what `map` can do when implemented on a container type, like `Optional`, `Array` or `Result`. To recap, it allows us to get a new container, where the value(s) wrapped inside are transformed according to a function. 179 | So **what's a Functor** you may ask? A Functor is any type that implements `map`. That's the whole story. 180 | 181 | Once you know what a functor is, we can talk about some types like `Dictionary` or even closures, and by saying that they're functors, you will immediately know of something you can do with them. 182 | 183 | ## Monads 184 | 185 | In the earlier example, we used the transformation function to return another *value*, but what if we wanted to use it to return a new `Result` object? Put another way, what if the transformation operation that we're passing to `map` can fail with an error as well? Let's look at what the types would look like. 186 | 187 | ```swift 188 | // func map(f: T -> U) -> Result 189 | ``` 190 | 191 | In our example, `T` is an `NSData` that we're converting into `U`, a `Result`. So let's replace that in the signature: 192 | 193 | ```swift 194 | // func map(f: NSData -> Result) -> Result> 195 | ``` 196 | 197 | Notice the nested `Result`s in the return type. This is probably not what we'll want. But it's OK. We can implement a function that takes the nested `Result`, and *flattens* it into a simple `Result`: 198 | 199 | ```swift 200 | extension Result { 201 | static func flatten(result: Result>) -> Result { 202 | switch result { 203 | case let .Value(innerResult): 204 | return innerResult.unbox 205 | case let .Error(error): 206 | return Result.Error(error) 207 | } 208 | } 209 | } 210 | ``` 211 | 212 | This `flatten` function takes a nested `Result` with a `T` inside, and return a single `Result` simply by extracting the inner object inside the `Value`, or the `Error`. 213 | 214 | A `flatten` function can be found in other contexts. For example, one can `flatten` an array of arrays into a contiguous, one-dimensional array. 215 | 216 | With this, we can implement our `Result -> Result` transformation by combining `map` and `flatten`: 217 | 218 | ```swift 219 | let JSErrorCodeInvalidStringData = 123 220 | 221 | let stringResult = Result.flatten(data.map { (data: NSData) -> (Result) in 222 | if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { 223 | return Result.Value(Box(string)) 224 | } 225 | else { 226 | return Result.Error(NSError(domain: "com.javisoto.es.error_domain", code: JSErrorCodeInvalidStringData, userInfo: nil)) 227 | } 228 | }) 229 | stringResult.debugDescription 230 | ``` 231 | 232 | This is so common, that you will find this defined in many places as `flatMap` or `flattenMap`, which we could implement for `Result` like this: 233 | 234 | ```swift 235 | extension Result { 236 | func flatMap(f: T -> Result) -> Result { 237 | return Result.flatten(map(f)) 238 | } 239 | } 240 | ``` 241 | 242 | And with that, we turned our `Result` type into a Monad! **A Monad is a type of Functor. A type which, along with `map`, implements a `flatMap` function** (*sometimes also known as `bind`*) with a signature similar to the one we've seen here. Container types like the ones we presented here are usually Monads, but you will also see that pattern for example in types that encapsulate deferred computation, like `Signal` or `Future`. 243 | 244 | The words **Functor** and **Monad** come from category theory, with which I'm not familiar at all. However, there's value in having names to refer to these concepts. Computer scientists love to come up with names for things. But it's those names that allow us to refer to abstract concepts (*some extremely abstract, like Monad*), and immediately know what we mean (of course, assuming we have the previous knowledge of their meaning). We get the same benefit out of sharing names for things like design patterns (decorator, factory...). 245 | 246 | It took me a very long time to assimilate all the ideas in this blog post, so if you're not familiar with any of this I don't expect you to finish reading this and immediately understand it. 247 | However, I encourage you to create an Xcode playground and try to come up with the implementation for `map`, `flatten` and `flatMap` for `Result` or a similar container type (perhaps try with `Optional` or even `Array`), and use some sample values to play with them. 248 | 249 | And next time you hear the words Functor or Monad, don't be scared :) They're simply design patterns to describe common operations that we can perform on different types. 250 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 1 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Functor and Monad in Swift

15 |

I have been trying to teach myself Functional Programming since late 2013. Many of the concepts are very daunting because of their somewhat academic nature.

16 |

Since I'm obviously not an expert, I intend this to be a very practical post. You will find many posts trying to explain what a Monad is, some of them trying a bit too hard to come up with similes, but hopefully the sample code here will illustrate some of the concepts better.

17 |

It wasn't until recently that I finally could say that I got what Monad means. Let's explore why this concept even exists, and how it can help you when writing Swift code.

18 |

Map

19 |

One of the first things that we got to see at the 2014 WWDC with the introduction of Swift was that we could use the map function with the collection types. Let's focus on Swift's Array.

20 | 21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 11 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Let's analyze that signature by looking at the types. 15 | T is the type of elements in the current container, U will be the type of the elements in the container that will be returned. This allows us to, for example, map an array of strings, to an array of Ints that contains the lengths of each of the Strings in the original array.

16 |

We provide a function that takes a T value, and returns a value of type U. map will then use this function to create another Container instance, where the original values are replaced by the ones returned by the transformFunction.

17 |

Implementing map with our own type

18 |

Let's implement our own container type. A Result enum is a pattern that you will see in a lot of open source Swift code today. This brings several benefits to an API when used instead of the old Obj-C NSError-by-reference argument.

19 |

We could define it like this:

20 | 21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-12.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 13 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

The Box class is required to work around a current Swift limitation (unimplemented IR generation feature non-fixed multi-payload enum layout)

15 |

This is an implementation of a type known as Either in some programming languages. Only in this case we're forcing one of the types to be an NSError instead of being generic, since we're going to use it to report the result of an operation.

16 |

Conceptually, Result is very similar to Optional: it wraps a value of an arbitrary type, that may or may not be present. In this case, however, it may additional tell us why the value is not there.

17 |

To see an example, let's implement a function that reads the contents of a file and returns the result as a Result object:

18 | 19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-14.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 15 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Easy enough. This function will return either an NSData object, or an NSError in case the file can't be read.

15 |

Like we did before, we may want to apply some transformation to the read value. However, like in the case before, we would need to check that we have a value every step of the way, which may result in ugly nested if lets or switch statements. Let's leverage map like we did before. In this case, we will only want to apply such transformation if we have a value. If we don't, we can simply pass the same error through.

16 |

How would Result.map be implemented? Let's take a look:

17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-16.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 17 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Again, the transformation function f takes a value of type T (in the above example, NSData) and returns a value of type U (String). After calling map, we'll get a Result<U> (Result<String>) from an initial Result<T> (Result<NSData>). 15 | We only call f whenever we start with a value, and we simply return another Result with the same error otherwise.

16 |

Imagine that we wanted to read a file with string contents. We would get an NSData, that then we need to transform into a String. Then say that we want to turn it into uppercase:

17 |
NSData -> String -> String
18 | 

We can do this with a series of map transformations:

19 | 20 |
21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-18.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 19 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Similar to the early example with map on Arrays, this code is a lot more expressive. It simply declares what we want to accomplish, with no boilerplate.

15 |

In comparison, this is what the above code would look like without the use of map:

16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 3 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

The benefit of this pattern is that we can very clearly express the transformation that we're trying to apply on the list of elements (in this case, doubling their value). Compare this with the imperative approach:

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-20.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 21 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Functors

15 |

We've seen what map can do when implemented on a container type, like Optional, Array or Result. To recap, it allows us to get a new container, where the value(s) wrapped inside are transformed according to a function. 16 | So what's a Functor you may ask? A Functor is any type that implements map. That's the whole story.

17 |

Once you know what a functor is, we can talk about some types like Dictionary or even closures, and by saying that they're functors, you will immediately know of something you can do with them.

18 |

Monads

19 |

In the earlier example, we used the transformation function to return another value, but what if we wanted to use it to return a new Result object? Put another way, what if the transformation operation that we're passing to map can fail with an error as well? Let's look at what the types would look like.

20 | 21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-22.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 23 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

In our example, T is an NSData that we're converting into U, a Result<String>. So let's replace that in the signature:

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-24.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 25 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Notice the nested Results in the return type. This is probably not what we'll want. But it's OK. We can implement a function that takes the nested Result, and flattens it into a simple Result:

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-26.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 27 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

This flatten function takes a nested Result with a T inside, and return a single Result<T> simply by extracting the inner object inside the Value, or the Error.

15 |

A flatten function can be found in other contexts. For example, one can flatten an array of arrays into a contiguous, one-dimensional array.

16 |

With this, we can implement our Result<NSData> -> Result<String> transformation by combining map and flatten:

17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-28.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 29 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

This is so common, that you will find this defined in many places as flatMap or flattenMap, which we could implement for Result like this:

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-30.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 31 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

And with that, we turned our Result type into a Monad! A Monad is a type of Functor. A type which, along with map, implements a flatMap function (sometimes also known as bind) with a signature similar to the one we've seen here. Container types like the ones we presented here are usually Monads, but you will also see that pattern for example in types that encapsulate deferred computation, like Signal or Future.

15 |

The words Functor and Monad come from category theory, with which I'm not familiar at all. However, there's value in having names to refer to these concepts. Computer scientists love to come up with names for things. But it's those names that allow us to refer to abstract concepts (some extremely abstract, like Monad), and immediately know what we mean (of course, assuming we have the previous knowledge of their meaning). We get the same benefit out of sharing names for things like design patterns (decorator, factory...).

16 |

It took me a very long time to assimilate all the ideas in this blog post, so if you're not familiar with any of this I don't expect you to finish reading this and immediately understand it. 17 | However, I encourage you to create an Xcode playground and try to come up with the implementation for map, flatten and flatMap for Result or a similar container type (perhaps try with Optional or even Array), and use some sample values to play with them.

18 |

And next time you hear the words Functor or Monad, don't be scared :) They're simply design patterns to describe common operations that we can perform on different types.

19 | 20 |
21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 5 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

It's not about solving it in a one-liner vs 3 lines, but with the former concise implementation, there's a significantly higher signal-to-noise ratio. map allows us to express what we want to achieve, rather than how this is implemented. This eases our ability to reason about code when we read it.

15 |

But map doesn't only make sense on Array. map is a higher-order function that can be implemented on just any container type. That is, any type that, one way or another, wraps one or multiple values inside.

16 |

Let's look at another example: Optional. Optional is a container type that wraps a value, or the absence of it.

17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 7 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

The benefit of map in Optional is that it will handle nil values for us. If we're trying to operate on a value that may be nil, we can use Optional.map to apply those transformations, and end up with nil if the original value was nil, but without having to resort to nested if let to unwrap the optional.

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/section-8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 9 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

From this we can extrapolate that map, when implemented on different container types, can have slightly different behaviors, depending on the semantics of that type. For example, it only makes sense to transform the value inside an Optional when there's actually a value inside.

15 |

This is the general signature of a map method, when implemented on a Container type, that wraps values of type T:

16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Documentation/stylesheet.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 62.5%; 3 | } 4 | 5 | body { 6 | background-color: #fff; 7 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 8 | font-size: 1.3rem; 9 | padding-left: 20px; 10 | } 11 | 12 | section { 13 | padding-top: 10px; 14 | padding-bottom: 20px; 15 | -webkit-overflow-scrolling: touch; 16 | } 17 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/Resources/file.txt: -------------------------------------------------------------------------------- 1 | File contents 2 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | let numbers = [1, 2, 3] 2 | 3 | let doubledNumbers = numbers.map { $0 * 2 } 4 | doubledNumbers -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-10-original.swift: -------------------------------------------------------------------------------- 1 | // func map(transformFunction: T -> U) -> Container 2 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-10.swift: -------------------------------------------------------------------------------- 1 | // func map(transformFunction: T -> U) -> Container 2 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-11.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Box { 4 | let unbox: T 5 | 6 | init(_ value: T) { 7 | self.unbox = value 8 | } 9 | } 10 | 11 | enum Result { 12 | case Value(Box) 13 | case Error(NSError) 14 | } 15 | 16 | extension Result: DebugPrintable { 17 | var debugDescription: String { 18 | switch self { 19 | case let .Value(value): 20 | return "Value: \(toDebugString(value.unbox))" 21 | case let .Error(error): 22 | return "Error: \(toDebugString(error))" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-12-original.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Box { 4 | let unbox: T 5 | 6 | init(_ value: T) { 7 | self.unbox = value 8 | } 9 | } 10 | 11 | enum Result { 12 | case Value(Box) 13 | case Error(NSError) 14 | } 15 | 16 | extension Result: DebugPrintable { 17 | var debugDescription: String { 18 | switch self { 19 | case let .Value(value): 20 | return "Value: \(toDebugString(value))" 21 | case let .Error(error): 22 | return "Error: \(toDebugString(error))" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-12.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Box { 4 | let unbox: T 5 | 6 | init(_ value: T) { 7 | self.unbox = value 8 | } 9 | } 10 | 11 | enum Result { 12 | case Value(Box) 13 | case Error(NSError) 14 | } 15 | 16 | extension Result: DebugPrintable { 17 | var debugDescription: String { 18 | switch self { 19 | case let .Value(value): 20 | return "Value: \(toDebugString(value.unbox))" 21 | case let .Error(error): 22 | return "Error: \(toDebugString(error))" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-13.swift: -------------------------------------------------------------------------------- 1 | func dataWithContentsOfFile(file: String, encoding: NSStringEncoding) -> Result { 2 | var error: NSError? 3 | 4 | if let data = NSData(contentsOfFile: file, options: .allZeros, error: &error) { 5 | return .Value(Box(data)) 6 | } 7 | else { 8 | return .Error(error!) 9 | } 10 | } -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-14-original.swift: -------------------------------------------------------------------------------- 1 | func dataWithContentsOfFile(file: String, encoding: NSStringEncoding) -> Result { 2 | var error: NSError? 3 | 4 | if let data = NSData(contentsOfFile: file, options: .allZeros, error: &error) { 5 | return .Value(Box(data)) 6 | } 7 | else { 8 | return .Error(error!) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-14.swift: -------------------------------------------------------------------------------- 1 | func dataWithContentsOfFile(file: String, encoding: NSStringEncoding) -> Result { 2 | var error: NSError? 3 | 4 | if let data = NSData(contentsOfFile: file, options: .allZeros, error: &error) { 5 | return .Value(Box(data)) 6 | } 7 | else { 8 | return .Error(error!) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-15.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | func map(f: T -> U) -> Result { 3 | switch self { 4 | case let .Value(value): 5 | return Result.Value(Box(f(value.unbox))) 6 | case let .Error(error): 7 | return Result.Error(error) 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-16-original.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | func map(f: T -> U) -> Result { 3 | switch self { 4 | case let .Value(value): 5 | return Result.Value(Box(f(value.unbox))) 6 | case let .Error(error): 7 | return Result.Error(error) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-16.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | func map(f: T -> U) -> Result { 3 | switch self { 4 | case let .Value(value): 5 | return Result.Value(Box(f(value.unbox))) 6 | case let .Error(error): 7 | return Result.Error(error) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-17.swift: -------------------------------------------------------------------------------- 1 | let path = NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent("file.txt") 2 | let data: Result = dataWithContentsOfFile(path, NSUTF8StringEncoding) 3 | 4 | let uppercaseContents: Result = data.map { NSString(data: $0, encoding: NSUTF8StringEncoding)! }.map { $0.uppercaseString } 5 | uppercaseContents 6 | uppercaseContents.debugDescription -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-18-original.swift: -------------------------------------------------------------------------------- 1 | let path = "file.txt" 2 | let data: Result = dataWithContentsOfFile(path, NSUTF8StringEncoding) 3 | 4 | let uppercaseContents: Result = data.map { NSString(data: $0, encoding: NSUTF8StringEncoding)! }.map { $0.uppercaseString } 5 | uppercaseContents 6 | uppercaseContents.debugDescription 7 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-18.swift: -------------------------------------------------------------------------------- 1 | let path = NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent("file.txt") 2 | let data: Result = dataWithContentsOfFile(path, NSUTF8StringEncoding) 3 | 4 | let uppercaseContents: Result = data.map { NSString(data: $0, encoding: NSUTF8StringEncoding)! }.map { $0.uppercaseString } 5 | uppercaseContents 6 | uppercaseContents.debugDescription 7 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-19.swift: -------------------------------------------------------------------------------- 1 | var stringContents: String? 2 | 3 | switch data { 4 | case let .Value(value): 5 | stringContents = NSString(data: value.unbox, encoding: NSUTF8StringEncoding) 6 | case let .Error(error): 7 | break 8 | } 9 | 10 | let uppercaseContents2: String? = stringContents?.uppercaseString 11 | uppercaseContents2 -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-2-original.swift: -------------------------------------------------------------------------------- 1 | let numbers = [1, 2, 3] 2 | 3 | let doubledNumbers = numbers.map { $0 * 2 } 4 | doubledNumbers 5 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-2.swift: -------------------------------------------------------------------------------- 1 | let numbers = [1, 2, 3] 2 | 3 | let doubledNumbers = numbers.map { $0 * 2 } 4 | doubledNumbers 5 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-20-original.swift: -------------------------------------------------------------------------------- 1 | var stringContents: String? 2 | 3 | switch data { 4 | case let .Value(value): 5 | stringContents = NSString(data: value.unbox, encoding: NSUTF8StringEncoding) 6 | case let .Error(error): 7 | break 8 | } 9 | 10 | let uppercaseContents2: String? = stringContents?.uppercaseString 11 | uppercaseContents2 12 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-20.swift: -------------------------------------------------------------------------------- 1 | var stringContents: String? 2 | 3 | switch data { 4 | case let .Value(value): 5 | stringContents = NSString(data: value.unbox, encoding: NSUTF8StringEncoding) 6 | case let .Error(error): 7 | break 8 | } 9 | 10 | let uppercaseContents2: String? = stringContents?.uppercaseString 11 | uppercaseContents2 12 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-21.swift: -------------------------------------------------------------------------------- 1 | // func map(f: T -> U) -> Result -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-22-original.swift: -------------------------------------------------------------------------------- 1 | // func map(f: T -> U) -> Result 2 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-22.swift: -------------------------------------------------------------------------------- 1 | // func map(f: T -> U) -> Result 2 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-23.swift: -------------------------------------------------------------------------------- 1 | // func map(f: NSData -> Result) -> Result> -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-24-original.swift: -------------------------------------------------------------------------------- 1 | // func map(f: NSData -> Result) -> Result> 2 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-24.swift: -------------------------------------------------------------------------------- 1 | // func map(f: NSData -> Result) -> Result> 2 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-25.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | static func flatten(result: Result>) -> Result { 3 | switch result { 4 | case let .Value(innerResult): 5 | return innerResult.unbox 6 | case let .Error(error): 7 | return Result.Error(error) 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-26-original.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | static func flatten(result: Result>) -> Result { 3 | switch result { 4 | case let .Value(innerResult): 5 | return innerResult.unbox 6 | case let .Error(error): 7 | return Result.Error(error) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-26.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | static func flatten(result: Result>) -> Result { 3 | switch result { 4 | case let .Value(innerResult): 5 | return innerResult.unbox 6 | case let .Error(error): 7 | return Result.Error(error) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-27.swift: -------------------------------------------------------------------------------- 1 | let JSErrorCodeInvalidStringData = 123 2 | 3 | let stringResult = Result.flatten(data.map { (data: NSData) -> (Result) in 4 | if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { 5 | return Result.Value(Box(string)) 6 | } 7 | else { 8 | return Result.Error(NSError(domain: "com.javisoto.es.error_domain", code: JSErrorCodeInvalidStringData, userInfo: nil)) 9 | } 10 | }) 11 | stringResult.debugDescription -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-28-original.swift: -------------------------------------------------------------------------------- 1 | let JSErrorCodeInvalidStringData = 123 2 | 3 | let stringResult = Result.flatten(data.map { (data: NSData) -> (Result) in 4 | if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { 5 | return Result.Value(Box(string)) 6 | } 7 | else { 8 | return Result.Error(NSError(domain: "com.javisoto.es.error_domain", code: JSErrorCodeInvalidStringData, userInfo: nil)) 9 | } 10 | }) 11 | stringResult.debugDescription 12 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-28.swift: -------------------------------------------------------------------------------- 1 | let JSErrorCodeInvalidStringData = 123 2 | 3 | let stringResult = Result.flatten(data.map { (data: NSData) -> (Result) in 4 | if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { 5 | return Result.Value(Box(string)) 6 | } 7 | else { 8 | return Result.Error(NSError(domain: "com.javisoto.es.error_domain", code: JSErrorCodeInvalidStringData, userInfo: nil)) 9 | } 10 | }) 11 | stringResult.debugDescription 12 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-29.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | func flatMap(f: T -> Result) -> Result { 3 | return Result.flatten(map(f)) 4 | } 5 | } -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-3.swift: -------------------------------------------------------------------------------- 1 | var doubledImperative: [Int] = [] 2 | for number in numbers { 3 | doubledImperative.append(number * 2) 4 | } 5 | doubledImperative -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-30-original.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | func flatMap(f: T -> Result) -> Result { 3 | return Result.flatten(map(f)) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-30.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | func flatMap(f: T -> Result) -> Result { 3 | return Result.flatten(map(f)) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-4-original.swift: -------------------------------------------------------------------------------- 1 | var doubledImperative: [Int] = [] 2 | for number in numbers { 3 | doubledImperative.append(number * 2) 4 | } 5 | doubledImperative 6 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-4.swift: -------------------------------------------------------------------------------- 1 | var doubledImperative: [Int] = [] 2 | for number in numbers { 3 | doubledImperative.append(number * 2) 4 | } 5 | doubledImperative 6 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-5.swift: -------------------------------------------------------------------------------- 1 | let number = Optional(815) 2 | 3 | let transformedNumber = number.map { $0 * 2 }.map { $0 % 2 == 0 } 4 | transformedNumber -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-6-original.swift: -------------------------------------------------------------------------------- 1 | let number = Optional(815) 2 | 3 | let transformedNumber = number.map { $0 * 2 }.map { $0 % 2 == 0 } 4 | transformedNumber 5 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-6.swift: -------------------------------------------------------------------------------- 1 | let number = Optional(815) 2 | 3 | let transformedNumber = number.map { $0 * 2 }.map { $0 % 2 == 0 } 4 | transformedNumber 5 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-7.swift: -------------------------------------------------------------------------------- 1 | let nilNumber: Int? = .None 2 | 3 | let transformedNilNumber = nilNumber.map { $0 * 2 }.map { $0 % 2 == 0 } 4 | transformedNilNumber -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-8-original.swift: -------------------------------------------------------------------------------- 1 | let nilNumber: Int? = .None 2 | 3 | let transformedNilNumber = nilNumber.map { $0 * 2 }.map { $0 % 2 == 0 } 4 | transformedNilNumber 5 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-8.swift: -------------------------------------------------------------------------------- 1 | let nilNumber: Int? = .None 2 | 3 | let transformedNilNumber = nilNumber.map { $0 * 2 }.map { $0 % 2 == 0 } 4 | transformedNilNumber 5 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/section-9.swift: -------------------------------------------------------------------------------- 1 | // func map(transformFunction: T -> U) -> Container -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/FunctorAndMonadPlayground.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Functor and Monad in Swift/playground/Makefile: -------------------------------------------------------------------------------- 1 | playground: 2 | playground FunctorAndMonadPlayground.md . 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JaviSoto's Blog Posts 2 | ========== 3 | 4 | My blog posts published on www.javiersoto.me. 5 | 6 | - [**ReactiveCocoa**](/ReactiveCocoa/Reactive%20Cocoa.md): http://www.javiersoto.me/post/77190645097 7 | - [**Functor and Monad in Swift**](/Functor%20and%20Monad%20in%20Swift/FunctorAndMonad.md): http://www.javiersoto.me/post/106875422394 8 | -------------------------------------------------------------------------------- /ReactiveCocoa/Reactive Cocoa.md: -------------------------------------------------------------------------------- 1 | # Reactive Cocoa 2 | 3 | A lot is being written these days about Reactive Cocoa and the paradigms it introduces. In my opinion, there are only two sides giving their opinion: the ones that know a lot about it and are therefore obviously biased, and the ones that refuse to learn it. 4 | 5 | I decided to write something myself because I think I'm right in the middle. Some months ago I knew nothing about it, and now I know enough to want to use it every day. I hope that by not being an expert, I can actual introduce a few really simple ideas in a much easier way to pick up by new-commers, to show why I think Reactive Cocoa is an incredibly powerful tool to help us write better, cleaner, and more maintainable code. 6 | 7 | ### Signals 8 | 9 | The base of **everything** that happens in Reactive Cocoa is signals. I'm going to try to explain what they *represent* and why they fit in the way we think about what happens on an app. A lot of the things you may read about Functional Reactive Programming end up confusing you when they introduce more advanced FP operators, etc. So let's avoid that. 10 | 11 | What is a signal? Think of it as a pipe that carries values. This flow of values is actually present everywhere in our apps when you think about it: the data downloaded from the network, the Boolean value "is the user logged in", the value of a setting "font size across the app", etc. Most values are not constant, they vary over time, and we manage that in a number of ways: callbacks, delegates, KVO, NSNotificationCenter.... 12 | 13 | Why is something like signals, which clearly represent a *real thing* and not some crazy mathematical model, not present in our language / frameworks / design patterns? Because the inherent C-way-of-doing-things rules the way we write code. Apart from the Object Orientation abstractions, the imperative code that we write isn't very far from Assembly: we feed instructions to the machine, and it executes them one by one, sequentially. We have to manage the data that it reads / writes in memory (state). i.e when we write imperative code, we tell the machine **how** to do the work, instead of **what** work to do. This is what a new paradigm tries to accomplish: to drive us away from this low-level universe into a place closer to the way we reason and express our intentions when writing code. 14 | 15 | Now think about something for a second. Say you call a method that returns some data, let's say a list of tweets. It doesn't return the *present* value, by the time the method returns, that's already the *past*. Just like the way our eyes don't observe the present but some not-very-distant past. But this is not a problem, you may think, whenever the list of tweets changes, we'll call the method again to feed the new list to our table view. I know! I'll use a delegate! Right? Right...? But what if we had an entity, a *thing* that represented exactly that: a flow of values that change over time. 16 | 17 | So let me introduce **one** construct that I hope will make you want to use Reactive Cocoa even if just for that. 18 | 19 | Let's say we want a signal that will carry the values of this list of tweets. Assuming that we have a JASTweetDataProvider class with a property like this: 20 | 21 | ```objc 22 | @property (nonatomic, readonly, strong) NSArray *tweets; 23 | ``` 24 | 25 | That is set whenever some other component queries the Twitter API and therefor sends KVO notifications. Reactive Cocoa makes it incredibly easy to get a signal that will send values (in this case, NSArrays) every time the propery changes: 26 | 27 | ```objc 28 | RACObserve(self.tweetDataProvider, tweets); 29 | ``` 30 | 31 | (Note the awesomeness of ReactiveCocoa handling the KVO subscription for us and removing it as soon as self is long to be deallocated, let's say whatever ViewController we're doing this in) 32 | 33 | So what we can do with this signal? We can **bind** its values to something else. Look over the code of the last 5 view controllers you worked on and ping me on Twitter if you don't think that 90% of the code there is glue-code to change some value whenever other value changes through some callback mechanism. The problem with this code is that it tends to be kind of spaghetti-y: there's the observations in one place, the values coming in in another, and then the processing of the values somewhere else. We're used to code like this, but we can't deny its complexity. Again: we focus on **how** the task has to be accomplished and not the **what** we're trying to do. And then there's all the underlying trickiness not obvious at first, common source of bugs: "is this happening in the right order". This forces us to use the debugger to analyze the state of the view controller, see what method gets called first, etc. Gross. 34 | 35 | So let's go back to our example. How do we bind our ever-changing list of tweets to something else? With a one liner. 36 | 37 | ```objc 38 | RAC(self.tableDataSource, tweets) = RACObserve(self.tweetDataProvider, tweets); 39 | ``` 40 | 41 | * When I started using Reactive Cocoa it helped me a lot to read the RAC() macro as "RACBind". So you could read this line as "Bind the tweets coming from the data provider into the table view." 42 | 43 | And before you start thinking "ah, magical code that abuses pre-processor macros", think about what we have achieved. I don't have to jump back and forth between 3 methods to understand what the flow of data is. This line is so descriptive that one can read it and forget about the implementation details and focus on the **what**. You can forget about whether the stringly-typed keypath is correct or has a typo, whether you're implementing KVO right or your breaking your parent class, you can forget about forgetting to unsubscribe, unsubscribing without having subscribed, and a long etc. And you can simply wonder "did I tell my code to do what I want it to do?". Honestly, since I use Reactive Cocoa I sleep better at night. 44 | 45 | And because you may say that this is a dumb example, I'm going to show one last example. And if you think that we don't need to use KVO for that and we could've used a protocol, think about the fact that we get being KVO-complaint for free in almost all cases and creating a protocol requires a lot of boiler-plate code. 46 | 47 | So say we want to show or hide a label that says "no tweets" depending on whether there are any tweets or not. I'm sure you're drawing the graph of method calls in your head to accomplish this. Or maybe you're imagining the growing - observeValueForKeyPath:ofObject:change:context:. Check out this descriptive code: 48 | 49 | ```objc 50 | RAC(self.noTweetsLabel, hidden) = [RACObserve(self.tweetDataProvider, tweets) map:NSNumber *^(NSArray *tweets) { 51 | return @(tweets.count > 0); 52 | }]; 53 | ``` 54 | 55 | When you call `-map:` on a signal, it returns another signal that gets a value every time the original signal gets a value, but "maps" them to another value. In this case we're converting NSArray values into boolean (*isHidden*?) values. 56 | 57 | 58 | If I have convinced you to at least give Reactive Cocoa a try, I would be very satisfied. But maybe you simply have to trust me when I tell you that you need to try it out for yourself to realize how incredibly useful it is. 59 | --------------------------------------------------------------------------------