├── .gitignore ├── LICENSE ├── README.md ├── transducers.css ├── transducers.md └── transducers.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-32.html ├── section-34.html ├── section-36.html ├── section-38.html ├── section-4.html ├── section-40.html ├── section-6.html ├── section-8.html └── stylesheet.css ├── 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-31.swift ├── section-32-original.swift ├── section-32.swift ├── section-33.swift ├── section-34-original.swift ├── section-34.swift ├── section-35.swift ├── section-36-original.swift ├── section-36.swift ├── section-37.swift ├── section-38-original.swift ├── section-38.swift ├── section-39.swift ├── section-4-original.swift ├── section-4.swift ├── section-40-original.swift ├── section-40.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 /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Brandon Williams 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Learn about transducers 2 | 3 | A little tutorial written in Swift playgrounds to play around with transducers. Open the `transducers.playground` to view the tutorial. 4 | 5 | ### About 6 | 7 | The `transducers.playground` file is generated from `transducers.md` using this [playground](https://github.com/jas/playground) tool. 8 | -------------------------------------------------------------------------------- /transducers.css: -------------------------------------------------------------------------------- 1 | /* basic styles taken from basscss: https://github.com/jxnblk/basscss */ 2 | body,button{margin:0}button,input,select,textarea{font-family:inherit;font-size:100%}img{max-width:100%}svg{max-height:100%}body{font-family:'Helvetica Neue',Helvetica,sans-serif;line-height:1.5;font-size:100%}h1,h2,h3,h4,h5,h6{font-family:'Helvetica Neue',Helvetica,sans-serif;font-weight:700;line-height:1.25;margin-top:1em;margin-bottom:.5em}dl,ol,p,ul{font-size:1rem;margin-top:0;margin-bottom:1rem}ol,ul{padding-left:2rem}code,pre,samp{font-family:'Source Code Pro',Consolas,monospace;font-size:inherit}pre{margin-top:0;margin-bottom:1rem;overflow-x:scroll}hr{margin-top:2rem;margin-bottom:2rem}blockquote{margin-top:2rem;margin-bottom:2rem;margin-left:0;padding-left:1rem;padding-right:1rem}blockquote,blockquote p{font-size:1.25rem;font-style:italic}h1{font-size:2rem}h2{font-size:1.5rem}h3{font-size:1.25rem}h4{font-size:1rem}h5{font-size:.875rem}h6{font-size:.75rem}body{color:#333;background-color:#fff}a{color:#0076df;text-decoration:none}a:hover{text-decoration:underline}code,pre{background-color:#eee;border-radius:3px}hr{border:0;border-bottom-style:solid;border-bottom-width:1px;border-bottom-color:#ccc} 3 | 4 | section { 5 | max-width: 800px; 6 | padding: 12px; 7 | -webkit-overflow-scrolling: touch; 8 | } 9 | -------------------------------------------------------------------------------- /transducers.md: -------------------------------------------------------------------------------- 1 | ## Transducers 2 | 3 | Transducers provide a nice way of efficiently processing arrays (and list-like types) by combining many operations into a single one. To illustrate this we will come up with a situation where the naive way of mapping and filtering an array of integers has some obvious flaws, and then look at how we can remedy it. 4 | 5 | Before we get to that we need to build up some tools to make function composition a little nicer. Afterall, bolstering function composition is one of the most important properties of functional programming. Here are a few simple combinators to start things off with: 6 | 7 | ```swift 8 | // x |> f = f(x) 9 | infix operator |> {associativity left} 10 | func |> (x: A, f: A -> B) -> B { 11 | return f(x) 12 | } 13 | 14 | // (f |> g)(x) = f(g(x)) 15 | func |> (f: A -> B, g: B -> C) -> A -> C { 16 | return { g(f($0)) } 17 | } 18 | ``` 19 | 20 | Using these combinators we can now be way more expressive with the flow of data: 21 | 22 | ```swift 23 | import Foundation 24 | 25 | 4.0 |> sqrt |> cos |> exp 26 | 27 | // compare with: 28 | exp(cos(sqrt(4.0))) 29 | ``` 30 | 31 | We will also re-define `map` to have a signature that is easier to work with: 32 | 33 | ```swift 34 | func fmap (f: A -> B) -> [A] -> [B] { 35 | return { map($0, f) } 36 | } 37 | ``` 38 | 39 | This signature makes it clear that `map` simply lifts a function `A -> B` to a function `[A] -> [B]`. For example: 40 | 41 | ```swift 42 | let nums = [1.0, 3.0, 4.0, 5.5] 43 | 44 | nums |> fmap(sqrt) |> fmap(cos) |> fmap(exp) 45 | nums |> fmap(sqrt |> cos |> exp) 46 | ``` 47 | 48 | Finally, we need to re-define `filter` just like we did for `map`: 49 | 50 | ```swift 51 | func filter (p: A -> Bool) -> [A] -> [A] { 52 | return {xs in 53 | var ys = [A]() 54 | for x in xs { 55 | if p(x) { ys.append(x) } 56 | } 57 | return ys 58 | } 59 | } 60 | ``` 61 | 62 | This signature helps clarify that `filter` simply lifts a predicate `A -> Bool` to a function on arrays `[A] -> [A]`. 63 | 64 | Using these combinators and array processing functions we can do all types of fun stuff. Let's take a large array of integers and run them through a few functions: 65 | 66 | ```swift 67 | let xs = Array(1...100) 68 | 69 | func square (n: Int) -> Int { 70 | return n * n 71 | } 72 | 73 | func incr (n: Int) -> Int { 74 | return n + 1 75 | } 76 | 77 | xs |> fmap(square) |> fmap(incr) 78 | ``` 79 | 80 | This isn't very efficient because we know that this will process the array of `xs` twice: once to `square` and again to `incr`. Instead we could compose `square` and `incr` once and feed that into `fmap`: 81 | 82 | ```swift 83 | xs |> fmap(square |> incr) 84 | ``` 85 | 86 | Now we are simultaneously squaring and incrementing the `xs` in a single pass. What if we then wanted to `filter` that array of numbers to find all of the primes? 87 | 88 | ```swift 89 | // Simple function to check primality of an integer. Details of this 90 | // function aren't important, but it works. 91 | func isPrime (p: Int) -> Bool { 92 | if p == 2 { return true } 93 | for i in 2...Int(sqrtf(Float(p))) { 94 | if p % i == 0 { return false } 95 | } 96 | return true 97 | } 98 | 99 | xs |> fmap(square |> incr) |> filter(isPrime) 100 | ``` 101 | 102 | We're back to being inefficient again since we are processing the `xs` twice: once to `square` and `incr`, and again to check `isPrime`. 103 | 104 | Transducers aim to remedy this by collecting all mapping and filtering functions into a single function that can be run once to process the `xs`. The idea stems from the observation that most of the functions we write for processing arrays can actually be written in terms of `reduce`. (In fact, one can make a precise statement about *all* array functions being rewritten as `reduce`.) 105 | 106 | For example, here is how one might write `map`, `filter` and `take` in terms of `reduce`: 107 | 108 | ```swift 109 | func map_from_reduce (f: A -> B) -> [A] -> [B] { 110 | return {xs in 111 | return xs.reduce([]) { accum, x in accum + [f(x)] } 112 | } 113 | } 114 | 115 | func filter_from_reduce (p: A -> Bool) -> [A] -> [A] { 116 | return {xs in 117 | return xs.reduce([]) { accum, x in 118 | return p(x) ? accum + [x] : accum 119 | } 120 | } 121 | } 122 | 123 | func take_from_reduce (n: Int) -> [A] -> [A] { 124 | return {xs in 125 | return xs.reduce([]) { accum, x in 126 | return accum.count < n ? accum + [x] : accum 127 | } 128 | } 129 | } 130 | 131 | [1.0, 2.0, 3.0] |> map_from_reduce(sqrt) 132 | 133 | [1.0, 2.0, 3.0, 1.0/0.0] |> filter_from_reduce(isfinite) 134 | 135 | [1, 2, 3, 4, 5, 6, 7, 8] |> take_from_reduce(5) 136 | ``` 137 | 138 | Now that we know `reduce` is in some sense "universal" among functions that process arrays we can try unifying all of our array processing under `reduce` and see if that aids in composition. To get to that point we are going to define some more things. First a term: given data types `A` and `C` we call a function of the form `(C, A) -> C` a **reducer** on `A`. These are precisely the kinds of functions we could feed into `reduce`. The first argument is called the **accumulation** and the second element is just the element of the array being inspected. A function that takes a reducer on `A` and returns a reducer on `B` is called a **transducer**. A simple example would be the following: 139 | 140 | ```swift 141 | func mapping (f: A -> B) -> ((C, B) -> C) -> ((C, A) -> C) { 142 | return { reducer in 143 | return { accum, x in reducer(accum, f(x)) } 144 | } 145 | } 146 | ``` 147 | 148 | It takes any function `A -> B` and lifts it to a transducer from `B` to `A`. It is very important to note that the direction changed! This is called contravariance. Note that the implementation of this function is pretty much the only thing we could do to make it compile. It is almost as if the compiler is writing it for us. 149 | 150 | Another example: 151 | 152 | ```swift 153 | func filtering (p: A -> Bool) -> ((C, A) -> C) -> (C, A) -> C { 154 | return { reducer in 155 | return { accum, x in 156 | return p(x) ? reducer(accum, x) : accum 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | This lifts a predicate `A -> Bool` to a transducer from `A` to `A`. 163 | 164 | We can use these functions to lift functions and predicates to transducers, and then feed them into `reduce`. In particular, consider `mapping(square)`. That lifts `square` to a transducer `((C, Int) -> C) -> ((C, Int) -> C)`, where `C` can be any data type. If we feed a reducer into `mapping(square)` we get another reducer. A simple reducer that comes up often when dealing with arrays is `append`, which Swift doesn't have natively implemented but we can do easily enough: 165 | 166 | ```swift 167 | func append (xs: [A], x: A) -> [A] { 168 | return xs + [x] 169 | } 170 | 171 | append([1, 2, 3, 4], 5) 172 | ``` 173 | 174 | Then what does `mapping(square)(append)` do? It just squares an integer and appends it to an array of integers. 175 | 176 | ```swift 177 | mapping(square)(append)([1, 2, 3, 4], 5) 178 | ``` 179 | 180 | Feeding the reducer `mapping(square)(append)` into `reduce` we see that we get the same thing had we mapped with `square`: 181 | 182 | ```swift 183 | reduce(xs, [], mapping(square)(append)) 184 | 185 | xs |> fmap(square) 186 | ``` 187 | 188 | Ok, but now we've just made this code more verbose to seemingly accomplish the same thing. The reason to do this is because transducers are highly composable, whereas regular reducers are not. We can also do: 189 | 190 | ```swift 191 | reduce(xs, [], append |> mapping(incr) |> mapping(square)) 192 | 193 | xs |> fmap(square |> incr) 194 | ``` 195 | 196 | Well, once again we didn't produce anything new that `map` didn't provide before. However, now we will mix in filters! 197 | 198 | ```swift 199 | reduce(xs, [], append |> filtering(isPrime) |> mapping(incr) |> mapping(square)) 200 | 201 | xs |> fmap(square |> incr) |> filter(isPrime) 202 | ``` 203 | 204 | There we go. This is the first time we've written something equivalently with `reduce` and `map`, but the `reduce` way resulted in processing the `xs` a single time, whereas the `map` way needed to iterate over `xs` twice. 205 | 206 | Let's add another wrinkle. Say we didn't just want those primes that are of the form `n*n+1` for `2 <= n <= 100`, but we wanted to find their sum. It's a very easy change: 207 | 208 | ```swift 209 | reduce(xs, 0, (+) |> filtering(isPrime) |> mapping(incr) |> mapping(square)) 210 | ``` 211 | 212 | Now that looks pretty good! Some really terrific code reusability going on right there. 213 | 214 | Sometimes it's useful to truncate an array to the first `n` elements, especially when dealing with large data sets. How can we introduce truncation into our `reduce` processing pipeline? Well, we need another transducer. Given an integer and a reducer we need to construct a new reducer that simply accumulates until the accumulation has reached size `n`. Or in code: 215 | 216 | ```swift 217 | func taking (n: Int) -> (([C], A) -> [C]) -> (([C], A) -> [C]) { 218 | return { reducer in 219 | return { accum, x in 220 | return accum.count < n ? reducer(accum, x) : accum 221 | } 222 | } 223 | } 224 | ``` 225 | 226 | Note that this transducer is specialized in that it can only work with reducers of the type `([C], A) -> [C]`, whereas our other transducers allowed for the more general case of `(C, A) -> C`, i.e. the accumulation needs to be an array. 227 | 228 | Now say that we don't want to just find the primes of the form `n^2+1` for `2 <= n <= 100`. Say we want to find the first 10 *twin primes* of the form `n^2+1` (a *twin prime* is a prime `p` such that `p+2` is also prime). First we need a function for twin primality testing: 229 | 230 | ```swift 231 | func isTwinPrime (p: Int) -> Bool { 232 | return isPrime(p) && isPrime(p+2) 233 | } 234 | ``` 235 | 236 | Then we can find the first 10 twin primes of the form `n^2+1` via: 237 | 238 | ```swift 239 | reduce(1...200, [], 240 | append |> filtering(isTwinPrime) 241 | |> mapping(incr) 242 | |> mapping(square) 243 | |> taking(10)) 244 | ``` 245 | 246 | This is all done with a single pass of the array of integers `1...200`, and it stops the moment 10 twin primes are found. 247 | 248 | 249 | Note that we had to choose a large enough range (`1...200` in this case) to ensure that we would fine all the twin primes we were looking for. That's an unfortunate choice to make. Instead, it would be nice to work on the full sequence of positive integers so we didn't have to worry about this. In fact, transducers are great for working on *any* list-like data type (streams, sequences, arrays, ...), and we could easily beef up everything we've done so far to work in the more general setting. 250 | 251 | *To be continued...* 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 1 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Transducers

15 |

Transducers provide a nice way of efficiently processing arrays (and list-like types) by combining many operations into a single one. To illustrate this we will come up with a situation where the naive way of mapping and filtering an array of integers has some obvious flaws, and then look at how we can remedy it.

16 |

Before we get to that we need to build up some tools to make function composition a little nicer. Afterall, bolstering function composition is one of the most important properties of functional programming. Here are a few simple combinators to start things off with:

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

This signature helps clarify that filter simply lifts a predicate A -> Bool to a function on arrays [A] -> [A].

15 |

Using these combinators and array processing functions we can do all types of fun stuff. Let's take a large array of integers and run them through a few functions:

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

This isn't very efficient because we know that this will process the array of xs twice: once to square and again to incr. Instead we could compose square and incr once and feed that into fmap:

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

Now we are simultaneously squaring and incrementing the xs in a single pass. What if we then wanted to filter that array of numbers to find all of the primes?

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

We're back to being inefficient again since we are processing the xs twice: once to square and incr, and again to check isPrime.

15 |

Transducers aim to remedy this by collecting all mapping and filtering functions into a single function that can be run once to process the xs. The idea stems from the observation that most of the functions we write for processing arrays can actually be written in terms of reduce. (In fact, one can make a precise statement about all array functions being rewritten as reduce.)

16 |

For example, here is how one might write map, filter and take in terms of reduce:

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

Now that we know reduce is in some sense "universal" among functions that process arrays we can try unifying all of our array processing under reduce and see if that aids in composition. To get to that point we are going to define some more things. First a term: given data types A and C we call a function of the form (C, A) -> C a reducer on A. These are precisely the kinds of functions we could feed into reduce. The first argument is called the accumulation and the second element is just the element of the array being inspected. A function that takes a reducer on A and returns a reducer on B is called a transducer. A simple example would be the following:

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

Using these combinators we can now be way more expressive with the flow of data:

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

It takes any function A -> B and lifts it to a transducer from B to A. It is very important to note that the direction changed! This is called contravariance. Note that the implementation of this function is pretty much the only thing we could do to make it compile. It is almost as if the compiler is writing it for us.

15 |

Another example:

16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-22.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 23 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

This lifts a predicate A -> Bool to a transducer from A to A.

15 |

We can use these functions to lift functions and predicates to transducers, and then feed them into reduce. In particular, consider mapping(square). That lifts square to a transducer ((C, Int) -> C) -> ((C, Int) -> C), where C can be any data type. If we feed a reducer into mapping(square) we get another reducer. A simple reducer that comes up often when dealing with arrays is append, which Swift doesn't have natively implemented but we can do easily enough:

16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-24.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 25 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Then what does mapping(square)(append) do? It just squares an integer and appends it to an array of integers.

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

Feeding the reducer mapping(square)(append) into reduce we see that we get the same thing had we mapped with square:

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-28.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 29 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Ok, but now we've just made this code more verbose to seemingly accomplish the same thing. The reason to do this is because transducers are highly composable, whereas regular reducers are not. We can also do:

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

Well, once again we didn't produce anything new that map didn't provide before. However, now we will mix in filters!

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-32.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 33 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

There we go. This is the first time we've written something equivalently with reduce and map, but the reduce way resulted in processing the xs a single time, whereas the map way needed to iterate over xs twice.

15 |

Let's add another wrinkle. Say we didn't just want those primes that are of the form n*n+1 for 2 <= n <= 100, but we wanted to find their sum. It's a very easy change:

16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-34.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 35 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Now that looks pretty good! Some really terrific code reusability going on right there.

15 |

Sometimes it's useful to truncate an array to the first n elements, especially when dealing with large data sets. How can we introduce truncation into our reduce processing pipeline? Well, we need another transducer. Given an integer and a reducer we need to construct a new reducer that simply accumulates until the accumulation has reached size n. Or in code:

16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-36.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 37 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Note that this transducer is specialized in that it can only work with reducers of the type ([C], A) -> [C], whereas our other transducers allowed for the more general case of (C, A) -> C, i.e. the accumulation needs to be an array.

15 |

Now say that we don't want to just find the primes of the form n^2+1 for 2 <= n <= 100. Say we want to find the first 10 twin primes of the form n^2+1 (a twin prime is a prime p such that p+2 is also prime). First we need a function for twin primality testing:

16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-38.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 39 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Then we can find the first 10 twin primes of the form n^2+1 via:

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

We will also re-define map to have a signature that is easier to work with:

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/section-40.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Section 41 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

This is all done with a single pass of the array of integers 1...200, and it stops the moment 10 twin primes are found.

15 |

Note that we had to choose a large enough range (1...200 in this case) to ensure that we would fine all the twin primes we were looking for. That's an unfortunate choice to make. Instead, it would be nice to work on the full sequence of positive integers so we didn't have to worry about this. In fact, transducers are great for working on any list-like data type (streams, sequences, arrays, ...), and we could easily beef up everything we've done so far to work in the more general setting.

16 |

To be continued...

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

This signature makes it clear that map simply lifts a function A -> B to a function [A] -> [B]. For example:

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

Finally, we need to re-define filter just like we did for map:

15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /transducers.playground/Documentation/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* basic styles taken from basscss: https://github.com/jxnblk/basscss */ 2 | body,button{margin:0}button,input,select,textarea{font-family:inherit;font-size:100%}img{max-width:100%}svg{max-height:100%}body{font-family:'Helvetica Neue',Helvetica,sans-serif;line-height:1.5;font-size:100%}h1,h2,h3,h4,h5,h6{font-family:'Helvetica Neue',Helvetica,sans-serif;font-weight:700;line-height:1.25;margin-top:1em;margin-bottom:.5em}dl,ol,p,ul{font-size:1rem;margin-top:0;margin-bottom:1rem}ol,ul{padding-left:2rem}code,pre,samp{font-family:'Source Code Pro',Consolas,monospace;font-size:inherit}pre{margin-top:0;margin-bottom:1rem;overflow-x:scroll}hr{margin-top:2rem;margin-bottom:2rem}blockquote{margin-top:2rem;margin-bottom:2rem;margin-left:0;padding-left:1rem;padding-right:1rem}blockquote,blockquote p{font-size:1.25rem;font-style:italic}h1{font-size:2rem}h2{font-size:1.5rem}h3{font-size:1.25rem}h4{font-size:1rem}h5{font-size:.875rem}h6{font-size:.75rem}body{color:#333;background-color:#fff}a{color:#0076df;text-decoration:none}a:hover{text-decoration:underline}code,pre{background-color:#eee;border-radius:3px}hr{border:0;border-bottom-style:solid;border-bottom-width:1px;border-bottom-color:#ccc} 3 | 4 | section { 5 | max-width: 800px; 6 | padding: 12px; 7 | -webkit-overflow-scrolling: touch; 8 | } 9 | -------------------------------------------------------------------------------- /transducers.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 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /transducers.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // x |> f = f(x) 2 | infix operator |> {associativity left} 3 | func |> (x: A, f: A -> B) -> B { 4 | return f(x) 5 | } 6 | 7 | // (f |> g)(x) = f(g(x)) 8 | func |> (f: A -> B, g: B -> C) -> A -> C { 9 | return { g(f($0)) } 10 | } -------------------------------------------------------------------------------- /transducers.playground/section-10-original.swift: -------------------------------------------------------------------------------- 1 | func filter
(p: A -> Bool) -> [A] -> [A] { 2 | return {xs in 3 | var ys = [A]() 4 | for x in xs { 5 | if p(x) { ys.append(x) } 6 | } 7 | return ys 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /transducers.playground/section-10.swift: -------------------------------------------------------------------------------- 1 | func filter (p: A -> Bool) -> [A] -> [A] { 2 | return {xs in 3 | var ys = [A]() 4 | for x in xs { 5 | if p(x) { ys.append(x) } 6 | } 7 | return ys 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /transducers.playground/section-11.swift: -------------------------------------------------------------------------------- 1 | let xs = Array(1...100) 2 | 3 | func square (n: Int) -> Int { 4 | return n * n 5 | } 6 | 7 | func incr (n: Int) -> Int { 8 | return n + 1 9 | } 10 | 11 | xs |> fmap(square) |> fmap(incr) -------------------------------------------------------------------------------- /transducers.playground/section-12-original.swift: -------------------------------------------------------------------------------- 1 | let xs = Array(1...100) 2 | 3 | func square (n: Int) -> Int { 4 | return n * n 5 | } 6 | 7 | func incr (n: Int) -> Int { 8 | return n + 1 9 | } 10 | 11 | xs |> fmap(square) |> fmap(incr) 12 | -------------------------------------------------------------------------------- /transducers.playground/section-12.swift: -------------------------------------------------------------------------------- 1 | let xs = Array(1...100) 2 | 3 | func square (n: Int) -> Int { 4 | return n * n 5 | } 6 | 7 | func incr (n: Int) -> Int { 8 | return n + 1 9 | } 10 | 11 | xs |> fmap(square) |> fmap(incr) 12 | -------------------------------------------------------------------------------- /transducers.playground/section-13.swift: -------------------------------------------------------------------------------- 1 | xs |> fmap(square |> incr) -------------------------------------------------------------------------------- /transducers.playground/section-14-original.swift: -------------------------------------------------------------------------------- 1 | xs |> fmap(square |> incr) 2 | -------------------------------------------------------------------------------- /transducers.playground/section-14.swift: -------------------------------------------------------------------------------- 1 | xs |> fmap(square |> incr) 2 | -------------------------------------------------------------------------------- /transducers.playground/section-15.swift: -------------------------------------------------------------------------------- 1 | // Simple function to check primality of an integer. Details of this 2 | // function aren't important, but it works. 3 | func isPrime (p: Int) -> Bool { 4 | if p == 2 { return true } 5 | for i in 2...Int(sqrtf(Float(p))) { 6 | if p % i == 0 { return false } 7 | } 8 | return true 9 | } 10 | 11 | xs |> fmap(square |> incr) |> filter(isPrime) -------------------------------------------------------------------------------- /transducers.playground/section-16-original.swift: -------------------------------------------------------------------------------- 1 | // Simple function to check primality of an integer. Details of this 2 | // function aren't important, but it works. 3 | func isPrime (p: Int) -> Bool { 4 | if p == 2 { return true } 5 | for i in 2...Int(sqrtf(Float(p))) { 6 | if p % i == 0 { return false } 7 | } 8 | return true 9 | } 10 | 11 | xs |> fmap(square |> incr) |> filter(isPrime) 12 | -------------------------------------------------------------------------------- /transducers.playground/section-16.swift: -------------------------------------------------------------------------------- 1 | // Simple function to check primality of an integer. Details of this 2 | // function aren't important, but it works. 3 | func isPrime (p: Int) -> Bool { 4 | if p == 2 { return true } 5 | for i in 2...Int(sqrtf(Float(p))) { 6 | if p % i == 0 { return false } 7 | } 8 | return true 9 | } 10 | 11 | xs |> fmap(square |> incr) |> filter(isPrime) 12 | -------------------------------------------------------------------------------- /transducers.playground/section-17.swift: -------------------------------------------------------------------------------- 1 | func map_from_reduce (f: A -> B) -> [A] -> [B] { 2 | return {xs in 3 | return xs.reduce([]) { accum, x in accum + [f(x)] } 4 | } 5 | } 6 | 7 | func filter_from_reduce (p: A -> Bool) -> [A] -> [A] { 8 | return {xs in 9 | return xs.reduce([]) { accum, x in 10 | return p(x) ? accum + [x] : accum 11 | } 12 | } 13 | } 14 | 15 | func take_from_reduce (n: Int) -> [A] -> [A] { 16 | return {xs in 17 | return xs.reduce([]) { accum, x in 18 | return accum.count < n ? accum + [x] : accum 19 | } 20 | } 21 | } 22 | 23 | [1.0, 2.0, 3.0] |> map_from_reduce(sqrt) 24 | 25 | [1.0, 2.0, 3.0, 1.0/0.0] |> filter_from_reduce(isfinite) 26 | 27 | [1, 2, 3, 4, 5, 6, 7, 8] |> take_from_reduce(5) -------------------------------------------------------------------------------- /transducers.playground/section-18-original.swift: -------------------------------------------------------------------------------- 1 | func map_from_reduce (f: A -> B) -> [A] -> [B] { 2 | return {xs in 3 | return xs.reduce([]) { accum, x in accum + [f(x)] } 4 | } 5 | } 6 | 7 | func filter_from_reduce (p: A -> Bool) -> [A] -> [A] { 8 | return {xs in 9 | return xs.reduce([]) { accum, x in 10 | if p(x) { 11 | return accum + [x] 12 | } else { 13 | return accum 14 | } 15 | } 16 | } 17 | } 18 | 19 | func take_from_reduce (n: Int) -> [A] -> [A] { 20 | return {xs in 21 | return xs.reduce([]) { accum, x in 22 | if accum.count < n { 23 | return accum + [x] 24 | } else { 25 | return accum 26 | } 27 | } 28 | } 29 | } 30 | 31 | [1.0, 2.0, 3.0] |> map_from_reduce(sqrt) 32 | 33 | [1.0, 2.0, 3.0, 1.0/0.0] |> filter_from_reduce(isfinite) 34 | 35 | [1, 2, 3, 4, 5, 6, 7, 8] |> take_from_reduce(5) 36 | -------------------------------------------------------------------------------- /transducers.playground/section-18.swift: -------------------------------------------------------------------------------- 1 | func map_from_reduce (f: A -> B) -> [A] -> [B] { 2 | return {xs in 3 | return xs.reduce([]) { accum, x in accum + [f(x)] } 4 | } 5 | } 6 | 7 | func filter_from_reduce (p: A -> Bool) -> [A] -> [A] { 8 | return {xs in 9 | return xs.reduce([]) { accum, x in 10 | if p(x) { 11 | return accum + [x] 12 | } else { 13 | return accum 14 | } 15 | } 16 | } 17 | } 18 | 19 | func take_from_reduce (n: Int) -> [A] -> [A] { 20 | return {xs in 21 | return xs.reduce([]) { accum, x in 22 | if accum.count < n { 23 | return accum + [x] 24 | } else { 25 | return accum 26 | } 27 | } 28 | } 29 | } 30 | 31 | [1.0, 2.0, 3.0] |> map_from_reduce(sqrt) 32 | 33 | [1.0, 2.0, 3.0, 1.0/0.0] |> filter_from_reduce(isfinite) 34 | 35 | [1, 2, 3, 4, 5, 6, 7, 8] |> take_from_reduce(5) 36 | -------------------------------------------------------------------------------- /transducers.playground/section-19.swift: -------------------------------------------------------------------------------- 1 | func mapping (f: A -> B) -> ((C, B) -> C) -> ((C, A) -> C) { 2 | return { reducer in 3 | return { accum, x in reducer(accum, f(x)) } 4 | } 5 | } -------------------------------------------------------------------------------- /transducers.playground/section-2-original.swift: -------------------------------------------------------------------------------- 1 | // x |> f = f(x) 2 | infix operator |> {associativity left} 3 | func |> (x: A, f: A -> B) -> B { 4 | return f(x) 5 | } 6 | 7 | // (f |> g)(x) = f(g(x)) 8 | func |> (f: A -> B, g: B -> C) -> A -> C { 9 | return { g(f($0)) } 10 | } 11 | -------------------------------------------------------------------------------- /transducers.playground/section-2.swift: -------------------------------------------------------------------------------- 1 | // x |> f = f(x) 2 | infix operator |> {associativity left} 3 | func |> (x: A, f: A -> B) -> B { 4 | return f(x) 5 | } 6 | 7 | // (f |> g)(x) = f(g(x)) 8 | func |> (f: A -> B, g: B -> C) -> A -> C { 9 | return { g(f($0)) } 10 | } 11 | -------------------------------------------------------------------------------- /transducers.playground/section-20-original.swift: -------------------------------------------------------------------------------- 1 | func mapping (f: A -> B) -> ((C, B) -> C) -> ((C, A) -> C) { 2 | return { reducer in 3 | return { accum, x in 4 | return reducer(accum, f(x)) 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /transducers.playground/section-20.swift: -------------------------------------------------------------------------------- 1 | func mapping (f: A -> B) -> ((C, B) -> C) -> ((C, A) -> C) { 2 | return { reducer in 3 | return { accum, x in 4 | return reducer(accum, f(x)) 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /transducers.playground/section-21.swift: -------------------------------------------------------------------------------- 1 | func filtering (p: A -> Bool) -> ((C, A) -> C) -> (C, A) -> C { 2 | return { reducer in 3 | return { accum, x in 4 | return p(x) ? reducer(accum, x) : accum 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /transducers.playground/section-22-original.swift: -------------------------------------------------------------------------------- 1 | func filtering (p: A -> Bool) -> ((C, A) -> C) -> (C, A) -> C { 2 | return { reducer in 3 | return { accum, x in 4 | return p(x) ? reducer(accum, x) : accum 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /transducers.playground/section-22.swift: -------------------------------------------------------------------------------- 1 | func filtering (p: A -> Bool) -> ((C, A) -> C) -> (C, A) -> C { 2 | return { reducer in 3 | return { accum, x in 4 | return p(x) ? reducer(accum, x) : accum 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /transducers.playground/section-23.swift: -------------------------------------------------------------------------------- 1 | func append (xs: [A], x: A) -> [A] { 2 | return xs + [x] 3 | } 4 | 5 | append([1, 2, 3, 4], 5) -------------------------------------------------------------------------------- /transducers.playground/section-24-original.swift: -------------------------------------------------------------------------------- 1 | func append (xs: [A], x: A) -> [A] { 2 | return xs + [x] 3 | } 4 | 5 | append([1, 2, 3, 4], 5) 6 | -------------------------------------------------------------------------------- /transducers.playground/section-24.swift: -------------------------------------------------------------------------------- 1 | func append (xs: [A], x: A) -> [A] { 2 | return xs + [x] 3 | } 4 | 5 | append([1, 2, 3, 4], 5) 6 | -------------------------------------------------------------------------------- /transducers.playground/section-25.swift: -------------------------------------------------------------------------------- 1 | mapping(square)(append)([1, 2, 3, 4], 5) -------------------------------------------------------------------------------- /transducers.playground/section-26-original.swift: -------------------------------------------------------------------------------- 1 | mapping(square)(append)([1, 2, 3, 4], 5) 2 | -------------------------------------------------------------------------------- /transducers.playground/section-26.swift: -------------------------------------------------------------------------------- 1 | mapping(square)(append)([1, 2, 3, 4], 5) 2 | -------------------------------------------------------------------------------- /transducers.playground/section-27.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], mapping(square)(append)) 2 | 3 | xs |> fmap(square) -------------------------------------------------------------------------------- /transducers.playground/section-28-original.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], mapping(square)(append)) 2 | 3 | xs |> fmap(square) 4 | -------------------------------------------------------------------------------- /transducers.playground/section-28.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], mapping(square)(append)) 2 | 3 | xs |> fmap(square) 4 | -------------------------------------------------------------------------------- /transducers.playground/section-29.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], append |> mapping(incr) |> mapping(square)) 2 | 3 | xs |> fmap(square |> incr) -------------------------------------------------------------------------------- /transducers.playground/section-3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4.0 |> sqrt |> cos |> exp 4 | 5 | // compare with: 6 | exp(cos(sqrt(4.0))) -------------------------------------------------------------------------------- /transducers.playground/section-30-original.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], append |> mapping(incr) |> mapping(square)) 2 | 3 | xs |> fmap(square |> incr) 4 | -------------------------------------------------------------------------------- /transducers.playground/section-30.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], append |> mapping(incr) |> mapping(square)) 2 | 3 | xs |> fmap(square |> incr) 4 | -------------------------------------------------------------------------------- /transducers.playground/section-31.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], append |> filtering(isPrime) |> mapping(incr) |> mapping(square)) 2 | 3 | xs |> fmap(square |> incr) |> filter(isPrime) -------------------------------------------------------------------------------- /transducers.playground/section-32-original.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], append |> filtering(isPrime) |> mapping(incr) |> mapping(square)) 2 | 3 | xs |> fmap(square |> incr) |> filter(isPrime) 4 | -------------------------------------------------------------------------------- /transducers.playground/section-32.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, [], append |> filtering(isPrime) |> mapping(incr) |> mapping(square)) 2 | 3 | xs |> fmap(square |> incr) |> filter(isPrime) 4 | -------------------------------------------------------------------------------- /transducers.playground/section-33.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, 0, (+) |> filtering(isPrime) |> mapping(incr) |> mapping(square)) -------------------------------------------------------------------------------- /transducers.playground/section-34-original.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, 0, (+) |> filtering(isPrime) |> mapping(incr) |> mapping(square)) 2 | -------------------------------------------------------------------------------- /transducers.playground/section-34.swift: -------------------------------------------------------------------------------- 1 | reduce(xs, 0, (+) |> filtering(isPrime) |> mapping(incr) |> mapping(square)) 2 | -------------------------------------------------------------------------------- /transducers.playground/section-35.swift: -------------------------------------------------------------------------------- 1 | func taking (n: Int) -> (([C], A) -> [C]) -> (([C], A) -> [C]) { 2 | return { reducer in 3 | return { accum, x in 4 | return accum.count < n ? reducer(accum, x) : accum 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /transducers.playground/section-36-original.swift: -------------------------------------------------------------------------------- 1 | func taking (n: Int) -> (([C], A) -> [C]) -> (([C], A) -> [C]) { 2 | return { reducer in 3 | return { accum, x in 4 | if accum.count < n { 5 | return reducer(accum, x) 6 | } else { 7 | return accum 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /transducers.playground/section-36.swift: -------------------------------------------------------------------------------- 1 | func taking (n: Int) -> (([C], A) -> [C]) -> (([C], A) -> [C]) { 2 | return { reducer in 3 | return { accum, x in 4 | if accum.count < n { 5 | return reducer(accum, x) 6 | } else { 7 | return accum 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /transducers.playground/section-37.swift: -------------------------------------------------------------------------------- 1 | func isTwinPrime (p: Int) -> Bool { 2 | return isPrime(p) && isPrime(p+2) 3 | } -------------------------------------------------------------------------------- /transducers.playground/section-38-original.swift: -------------------------------------------------------------------------------- 1 | func isTwinPrime (p: Int) -> Bool { 2 | return isPrime(p) && isPrime(p+2) 3 | } 4 | -------------------------------------------------------------------------------- /transducers.playground/section-38.swift: -------------------------------------------------------------------------------- 1 | func isTwinPrime (p: Int) -> Bool { 2 | return isPrime(p) && isPrime(p+2) 3 | } 4 | -------------------------------------------------------------------------------- /transducers.playground/section-39.swift: -------------------------------------------------------------------------------- 1 | reduce(1...200, [], 2 | append |> filtering(isTwinPrime) 3 | |> mapping(incr) 4 | |> mapping(square) 5 | |> taking(10)) -------------------------------------------------------------------------------- /transducers.playground/section-4-original.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4.0 |> sqrt |> cos |> exp 4 | 5 | // compare with: 6 | exp(cos(sqrt(4.0))) 7 | -------------------------------------------------------------------------------- /transducers.playground/section-4.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4.0 |> sqrt |> cos |> exp 4 | 5 | // compare with: 6 | exp(cos(sqrt(4.0))) 7 | -------------------------------------------------------------------------------- /transducers.playground/section-40-original.swift: -------------------------------------------------------------------------------- 1 | reduce(1...200, [], 2 | append |> filtering(isTwinPrime) 3 | |> mapping(incr) 4 | |> mapping(square) 5 | |> taking(10)) 6 | -------------------------------------------------------------------------------- /transducers.playground/section-40.swift: -------------------------------------------------------------------------------- 1 | reduce(1...200, [], 2 | append |> filtering(isTwinPrime) 3 | |> mapping(incr) 4 | |> mapping(square) 5 | |> taking(10)) 6 | -------------------------------------------------------------------------------- /transducers.playground/section-5.swift: -------------------------------------------------------------------------------- 1 | func fmap (f: A -> B) -> [A] -> [B] { 2 | return { map($0, f) } 3 | } -------------------------------------------------------------------------------- /transducers.playground/section-6-original.swift: -------------------------------------------------------------------------------- 1 | func fmap (f: A -> B) -> [A] -> [B] { 2 | return { map($0, f) } 3 | } 4 | -------------------------------------------------------------------------------- /transducers.playground/section-6.swift: -------------------------------------------------------------------------------- 1 | func fmap (f: A -> B) -> [A] -> [B] { 2 | return { map($0, f) } 3 | } 4 | -------------------------------------------------------------------------------- /transducers.playground/section-7.swift: -------------------------------------------------------------------------------- 1 | let nums = [1.0, 3.0, 4.0, 5.5] 2 | 3 | nums |> fmap(sqrt) |> fmap(cos) |> fmap(exp) 4 | nums |> fmap(sqrt |> cos |> exp) -------------------------------------------------------------------------------- /transducers.playground/section-8-original.swift: -------------------------------------------------------------------------------- 1 | let nums = [1.0, 3.0, 4.0, 5.5] 2 | 3 | nums |> fmap(sqrt) |> fmap(cos) |> fmap(exp) 4 | nums |> fmap(sqrt |> cos |> exp) 5 | -------------------------------------------------------------------------------- /transducers.playground/section-8.swift: -------------------------------------------------------------------------------- 1 | let nums = [1.0, 3.0, 4.0, 5.5] 2 | 3 | nums |> fmap(sqrt) |> fmap(cos) |> fmap(exp) 4 | nums |> fmap(sqrt |> cos |> exp) 5 | -------------------------------------------------------------------------------- /transducers.playground/section-9.swift: -------------------------------------------------------------------------------- 1 | func filter (p: A -> Bool) -> [A] -> [A] { 2 | return {xs in 3 | var ys = [A]() 4 | for x in xs { 5 | if p(x) { ys.append(x) } 6 | } 7 | return ys 8 | } 9 | } -------------------------------------------------------------------------------- /transducers.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------