Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ready for the future

Ready for the future

Swift is brand spanking new. How can we possibly be expected to write idiomatic code? On the other hand, Objective-C has been around for more than thirty years. We know what it looks like and feels like. The Objective-C of our iOS youth is very different than today’s Objective-C. Seen in this light, Swift is more evolutionary than you might think. In this talk we’ll look at Swift code in the light of the code of Objective-C and other languages that have come before it.

Daniel Steinberg http://dimsumthinking.com

B94d88549eaca9755b9346a0383b41bb?s=128

do{iOS} conference

November 09, 2015
Tweet

Transcript

  1. None
  2. None
  3. None
  4. Handwritten Slides?

  5. Boring Handwritten Slides

  6. This Talk Sucks

  7. I'm Already nodding off

  8. Not Even Speaker Colleagues Everyone Parents

  9. Let's start

  10. Recipe

  11. Eugenia Cheng: "Cakes, Custard + Category Theory"

  12. Eugenia Cheng: "Cakes, Custard + Category Theory"

  13. Eugenia Cheng: "Cakes, Custard + Category Theory" I hope this

    talk isn't about Monads and Functors.
  14. Recipe

  15. Lasagne

  16. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan
  17. Context

  18. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan
  19. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan
  20. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan
  21. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan Béchamel is one of the mother sauces
  22. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan Bechamel Sauce » Butter » Flour » Milk » Salt and Pepper
  23. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan Bechamel Sauce » Butter » Flour » Milk » Salt and Pepper
  24. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan Bechamel Sauce » Butter » Flour » Milk » Salt and Pepper Context matters
  25. Context matters

  26. Context matters

  27. An Obj-C example

  28. An Obj-C example

  29. An Obj-C example

  30. The model

  31. - (NSTimeInterval)elapsed Time { NSDate *now = [[NSDate alloc] init];

    NSTimeInterval elapsed Time = [now timeIntervalS inceDate:self.startTime]; return elapsedTime; }
  32. - (NSTimeInterval)elapsed Time { NSDate *now = [[NSDate alloc] init];

    NSTimeInterval elapsed Time = [now timeIntervalS inceDate:self.startTime]; return elapsedTime; }
  33. - (NSTimeInterval)elapsed Time { NSDate *now = [[NSDate alloc] init];

    NSTimeInterval elapsed Time = [now timeIntervalS inceDate:self.startTime]; return elapsedTime; }
  34. - (NSTimeInterval)elapsed Time { NSDate *now = [[NSDate alloc] init];

    NSTimeInterval elapsed Time = [now timeIntervalS inceDate:self.startTime]; return elapsedTime; }
  35. - (NSTimeInterval)elapsed Time { NSDate *now = [[NSDate alloc] init];

    NSTimeInterval elapsed Time = [now timeIntervalS inceDate:self.startTime]; return elapsedTime; }
  36. - (NSTimeInterval)elapsed Time { return [self.startTime timeIntervalSinceNow]; }

  37. - (NSTimeInterval)elapsed Time { return [self.startTime timeIntervalSinceNow]; }

  38. None
  39. - (NSTimeInterval)elapsed Time { return -[self.startTime timeIntervalSinceNow]; }

  40. - (NSTimeInterval)elapsed Time { return -[self.startTime timeIntervalSinceNow]; }

  41. - (NSTimeInterval)elapsed Time { return [self.startTime timeIntervalUntilNow]; }

  42. #import <Foundation/Foundation.h> @interface NSDate (TimerCalculations) - (NSTimeInterval)timeIntervalUntilNow; @end

  43. #import <Foundation/Foundation.h> @interface NSDate (TimerCalculations) - (NSTimeInterval)timeIntervalUntilNow; @end

  44. #import "NSDate+TimerCalculations.h" @implementation NSDate (TimerCalculations) - (NSTimeInterval) timeIntervalUntilNow { return

    - [self timeIntervalSinceNow]; } @end
  45. #import "NSDate+TimerCalculations.h" @implementation NSDate (TimerCalculations) - (NSTimeInterval) timeIntervalUntilNow { return

    - [self timeIntervalSinceNow]; } @end
  46. #import "NSDate+TimerCalculations.h" @implementation NSDate (TimerCalculations) - (NSTimeInterval) timeIntervalUntilNow { return

    - [self timeIntervalSinceNow]; } @end
  47. #import "NSDate+TimerCalculations.h" @implementation NSDate (TimerCalculations) - (NSTimeInterval)dst_timeIntervalUntilNow { return -

    [self timeIntervalSinceNow]; } @end
  48. Context

  49. Clean

  50. Clear

  51. Lasagne

  52. - (NSTimeInterval)elapsedTime { return [self.startTime timeInterval UntilNow]; }

  53. - (NSTimeInterval)elapsedTime { return startTime.timeIntervalUntilNow }

  54. - (NSTimeInterval)elapsedTime { return startTime.timeIntervalUntilNow } Exactly

  55. - (NSTimeInterval)elapsedTime { return startTime.timeIntervalUntilNow } Let's get to some

    swift
  56. - (NSTimeInterval)elapsedTime { return startTime.timeIntervalUntilNow } Show me some clever

    unreadable code
  57. - (NSTimeInterval)elapsedTime { return startTime.timeIntervalUntilNow } Let's see some Generics

    and Custom operators
  58. The model in Swift

  59. struct Timer { let startTime = NSDate() var elapsedTime: NSTimeInterval

    { return startTime.timeIntervalUntilNow } }
  60. struct Timer { let startTime = NSDate() var elapsedTime: NSTimeInterval

    { return startTime.timeIntervalUntilNow } }
  61. struct Timer { let startTime = NSDate() var elapsedTime: NSTimeInterval

    { return startTime.timeIntervalUntilNow } }
  62. struct Timer { let startTime = NSDate() var elapsedTime: NSTimeInterval

    { return startTime.timeIntervalUntilNow } }
  63. } } extension NSDate { var timeIntervalUntilNow: NSTimeInterval { return

    -timeIntervalSinceNow } }
  64. extension NSDate { var timeIntervalUntilNow: NSTimeInterval { return -timeIntervalSinceNow }

    }
  65. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan Bechamel Sauce » Butter » Flour » Milk » Salt and Pepper
  66. Clean

  67. Compact

  68. Clear

  69. UmmM, Daniel

  70. You Started with Obj-C

  71. And converted it to Swift

  72. What about Real Swift

  73. Show me Map, Filter, reduce!

  74. Silly Example

  75. App Sales

  76. import GameplayKit struct AppSales: SequenceType { let numberOfDays: Int let

    randomDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 10) func generate() -> Any Generator<Int> { var count = 0 return anyGenerator({ if count++ < self .numberOfDays { return self .randomDistribution.nextInt() } else { return nil } }) } }
  77. import GameplayKit struct AppSales: SequenceType { let numberOfDays: Int let

    randomDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 10) func generate() -> Any Generator<Int> { var count = 0 return anyGenerator({ if count++ < self .numberOfDays { return self .randomDistribution.nextInt() } else { return nil } }) } }
  78. import GameplayKit struct AppSales: SequenceType { let numberOfDays: Int let

    randomDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 10) func generate() -> Any Generator<Int> { var count = 0 return anyGenerator({ if count++ < self .numberOfDays { return self .randomDistribution.nextInt() } else { return nil } }) } }
  79. import GameplayKit struct AppSales: SequenceType { let numberOfDays: Int let

    randomDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 10) func generate() -> Any Generator<Int> { var count = 0 return anyGenerator({ if count++ < self .numberOfDays { return self .randomDistribution.nextInt() } else { return nil } }) } }
  80. import GameplayKit struct AppSales: SequenceType { let numberOfDays: Int let

    randomDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 10) func generate() -> Any Generator<Int> { var count = 0 return anyGenerator({ if count++ < self .numberOfDays { return self .randomDistribution.nextInt() } else { return nil } }) } }
  81. import GameplayKit struct AppSales: SequenceType { let numberOfDays: Int let

    randomDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 10) func generate() -> Any Generator<Int> { var count = 0 return anyGenerator({ if count++ < self .numberOfDays { return self .randomDistribution.nextInt() } else { return nil } }) } }
  82. import GameplayKit struct AppSales: SequenceType { let numberOfDays: Int let

    randomDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 10) func generate() -> Any Generator<Int> { var count = 0 return anyGenerator({ if count++ < self .numberOfDays { return self .randomDistribution.nextInt() } else { return nil } }) } }
  83. let lastWeeksSales = AppSales(numberOfDays: 7) for dailySales in lastWeeksSales {

    print(dailySales) }
  84. AppSales is a SequenceType

  85. for in

  86. let lastWeeksSales = AppSales(numberOfDays: 7) for dailySales in lastWeeksSales {

    print(dailySales) }
  87. 6 5 6 4 6 4 3 let lastWeeksSales =

    AppSales(numberOfDays: 7) for dailySales in lastWeeksSales { print(dailySales) }
  88. AppSales is a SequenceType

  89. map, filter, reduce, flatmap, …

  90. let lastWeeksSales = AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in

    Double(dailySales) * 1.99 * 0.70 }
  91. let lastWeeksSales = AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in

    Double(dailySales) * 1.99 * 0.70 }
  92. let lastWeeksSales = AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in

    Double(dailySales) * 1.99 * 0.70 }
  93. let lastWeeksSales = AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in

    Double(dailySales) * 1.99 * 0.70 }
  94. [6.964999999999999, 8.357999999999999, 5.572, 6.964999999999999, 5.572, 4.178999999999999, 4.178999999999999] let lastWeeksSales =

    AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in Double(dailySales) * 1.99 * 0.70 }
  95. let lastWeeksSales = AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in

    Double(dailySales) * 1.99 * 0.70 }
  96. let lastWeeksSales = AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in

    Double(dailySales) * 1.99 * 0.70 }
  97. Context

  98. let lastWeeksSales = AppSales(numberOfDays: 7) let lastWeeksRevenues = lastWeeksSales.map{dailySales in

    Double(dailySales) * 1.99 * 0.70 }
  99. let lastWeeksSales = AppSales(numberOfDays: 7) let unitPrice = 1.99 let

    lastWeeksRevenues = lastWeeksSales.map{dailySales in Double(dailySales) * unitPrice * 0.70 }
  100. let lastWeeksSales = AppSales(numberOfDays: 7) let unitPrice = 1.99 let

    lastWeeksRevenues = lastWeeksSales.map{dailySales in Double(dailySales) * unitPrice * 0.70 }
  101. let lastWeeksSales = AppSales(numberOfDays: 7) let unitPrice = 1.99 let

    sellersPercentage = 0.70 let lastWeeksRevenues = lastWeeksSales.map{dailySales in Double(dailySales) * unitPrice * sellersPercentage }
  102. let lastWeeksSales = AppSales(numberOfDays: 7) let unitPrice = 1.99 let

    sellersPercentage = 0.70 let lastWeeksRevenues = lastWeeksSales.map{dailySales in Double(dailySales) * unitPrice * sellersPercentage }
  103. let lastWeeksSales = AppSales(numberOfDays: 7) let unitPrice = 1.99 let

    sellersPercentage = 0.70 func revenuesForCopies Sold(numberOfCopies: Int) -> Double { return Double(number OfCopies) * unitPrice * sellersPercentage } let lastWeeksRevenues = lastWeeksSales.map{dailySales in revenuesForCopiesSold(dailySales) }
  104. let lastWeeksRevenues = lastWeeksSales.map{dailySales in revenuesForCopiesSold(dailySales) }

  105. let lastWeeksRevenues = lastWeeksSales.map{revenuesForCopies Sold($0) }

  106. let lastWeeksRevenues = lastWeeksSales.map(revenuesForCopies Sold)

  107. let lastWeeksRevenues = lastWeeksSales.map(revenuesForCopies Sold)

  108. Adjust the distribution

  109. Wider

  110. let randomDistribution = GKGaussianDistribution(lowestValue: -5, highestValue: 15)

  111. Negatives count as 0

  112. Could filter copies > 0

  113. let lastWeeksRevenues = lastWeeksSales.filter{$0 > 0} .map(revenuesForCopiesSold)

  114. Filter can change size of array

  115. Could map negatives -> 0

  116. let lastWeeksRevenues = lastWeeksSales.map{$0 > 0 ? $0 : 0}

    .map(revenuesForCopiesSold)
  117. "Doctor it hurts when I do that."

  118. "Don't do that."

  119. let lastWeeksRevenues = lastWeeksSales.map{$0 > 0 ? $0 : 0}

    .map(revenuesForCopiesSold)
  120. func negativeNumbersToZero (number: Int) -> Int { return max(0, number)

    } let lastWeeksRevenues = lastWeeksSales.map(negativeNumbersToZero) .map(revenuesForCopiesSold)
  121. Good News

  122. let lastWeeksRevenues = lastWeeksSales.map(negativeNumbersToZero) .map(revenuesForCopiesSold)

  123. let lastWeeksRevenues = lastWeeksSales.map(negativeNumbersToZero) .map(revenuesForCopiesSold)

  124. We'll come back to the Lasagne.

  125. First, the béchamel.

  126. func revenues ForCopiesSold(numberOfCopies: Int) -> Double { return Double(numberOfCopies) *

    unitPrice * sellersPercentage }
  127. func revenues ForCopiesSold(numberOfCopies: Int) -> Double { return Double(numberOfCopies) *

    unitPrice * sellersPercentage }
  128. typealias USDollars = Double func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars

    { return Double(numberOfCopies) * unitPrice * sellersPercentage }
  129. typealias USDollars = Double func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars

    { return Double(numberOfCopies) * unitPrice * sellersPercentage } func toThe NearestPenny(dollarAmount: USDollars) -> USDollars { return round(dollarAmount * 100)/100 }
  130. typealias USDollars = Double func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars

    { return Double(numberOfCopies) * unitPrice * sellersPercentage } func toThe NearestPenny(dollarAmount: USDollars) -> USDollars { return round(dollarAmount * 100)/100 } func revenues InDollarsForCopiesSold(numberOfCopies: Int) -> USDollars { return toTheNearestPenny(revenuesForCopiesSold (numberOfCopies)) }
  131. typealias USDollars = Double func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars

    { return Double(numberOfCopies) * unitPrice * sellersPercentage } func toThe NearestPenny(dollarAmount: USDollars) -> USDollars { return round(dollarAmount * 100)/100 } func revenues InDollarsForCopiesSold(numberOfCopies: Int) -> USDollars { return toTheNearestPenny(revenuesForCopiesSold (numberOfCopies)) }
  132. typealias USDollars = Double func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars

    { return Double(numberOfCopies) * unitPrice * sellersPercentage } func toThe NearestPenny(dollarAmount: USDollars) -> USDollars { return round(dollarAmount * 100)/100 } func revenues InDollarsForCopiesSold(numberOfCopies: Int) -> USDollars { return toTheNearestPenny(revenuesForCopiesSold (numberOfCopies)) }
  133. typealias USDollars = Double func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars

    { return Double(numberOfCopies) * unitPrice * sellersPercentage } func toThe NearestPenny(dollarAmount: USDollars) -> USDollars { return round(dollarAmount * 100)/100 } func revenues InDollarsForCopiesSold(numberOfCopies: Int) -> USDollars { return toTheNearestPenny(revenuesForCopiesSold (numberOfCopies)) }
  134. Backwards

  135. func revenues InDollarsForCopiesSold(numberOfCopies: Int) -> USDollars { return toTheNearestPenny(revenuesForCopiesSold (numberOfCopies))

    }
  136. Method chaining

  137. Extensions

  138. Extensions UmmM, Daniel

  139. Extensions How did your talk get accepted?

  140. Extensions No Generics.

  141. Extensions No Custom Operators

  142. Custom Operator

  143. Custom Operator What?

  144. Custom Operator

  145. Custom Operator And Generics?

  146. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  147. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  148. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  149. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  150. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  151. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  152. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  153. Clearly …

  154. Context

  155. infix operator » {associativity left} func »<T, U>(input: T, transform:

    T -> U) -> U { return transform(input) }
  156. Use it

  157. func revenuesInDollarsForCopiesSold(number OfCopies: Int) -> USDollars { return numberOfCopies »

    revenuesForCopiesSold » toTheNearestPenny }
  158. func revenuesInDollarsForCopiesSold(number OfCopies: Int) -> USDollars { return numberOfCopies »

    revenuesForCopiesSold » toTheNearestPenny }
  159. func revenuesInDollarsForCopiesSold(number OfCopies: Int) -> USDollars { return numberOfCopies »

    revenuesForCopiesSold » toTheNearestPenny }
  160. func revenuesInDollarsForCopiesSold(number OfCopies: Int) -> USDollars { return numberOfCopies »

    revenuesForCopiesSold » toTheNearestPenny }
  161. func revenuesInDollarsForCopiesSold(number OfCopies: Int) -> USDollars { return numberOfCopies »

    revenuesForCopiesSold » toTheNearestPenny }
  162. func revenuesInDollarsForCopiesSold(number OfCopies: Int) -> USDollars { return numberOfCopies »

    revenuesForCopiesSold » toTheNearestPenny }
  163. Back to the Lasagne

  164. let lastWeeksRevenues = lastWeeksSales .map(negativeNumbersToZero) .map(revenuesInDollarsForCopiesSold)

  165. map is like -

  166. Move map

  167. let lastWeeksRevenues = lastWeeksSales .map(negativeNumbersToZero) .map(revenuesInDollarsForCopiesSold)

  168. func replaceNegativeSalesWithZeroSales(sales: [Int]) -> [Int] { return sales.map(negativeNumbersToZero) } func

    calculateRevenuesFromSales(sales: [Int]) -> [USDollars] { return sales.map(revenuesInDollarsFor CopiesSold) }
  169. Types Match

  170. func replaceNegativeSalesWithZeroSales(sales: [Int]) -> [Int] { return sales.map(negativeNumbersToZero) } func

    calculateRevenuesFromSales(sales: [Int]) -> [USDollars] { return sales.map(revenuesInDollarsFor CopiesSold) }
  171. Types Don't Match

  172. let lastWeeksSales = AppSales(numberOfDays: 7) func replaceNegativeSalesWithZeroSales(sales: [Int]) -> [Int]

    { return sales.map(negativeNumbersToZero) } func calculateRevenuesFromSales(sales: [Int]) -> [USDollars] { return sales.map(revenuesInDollarsFor CopiesSold) }
  173. func anArrayOfDailySales(rawSales: AppSales) -> [Int] { return rawSales.map{$0} }

  174. Chain them together

  175. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  176. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  177. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  178. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  179. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  180. Lasagne

  181. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan
  182. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  183. We don't do this

  184. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    ̣ Butter ̣ Flour ̣ Milk ̣ Salt and Pepper » Grated parmesan
  185. let lastWeeksRevenues = lastWeeksSales .map{$0 > 0 ? $0 :

    0} .map{round(Double($0) * unitPrice * sellersPercentage * 100)/100 }
  186. We zoom in

  187. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan
  188. Lasagne » Bolognese sauce » Lasagne sheets » Béchamel sauce

    » Grated parmesan Bechamel Sauce » Butter » Flour » Milk » Salt and Pepper
  189. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  190. let lastWeeksRevenues = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales

  191. = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales func calculateRevenuesFromSales(sales:

    [Int]) -> [USDollars] { return sales.map(revenuesInDollars ForCopiesSold) }
  192. = lastWeeksSales » anArrayOfDailySales » replaceNegativeSalesWithZeroSales » calculateRevenuesFromSales func calculateRevenuesFromSales(sales:

    [Int]) -> [USDollars] { return sales.map(revenuesInDollarsForCopiesSold) }
  193. evenuesFromSales(sales: [Int]) -> [USDollars] { s.map(revenuesInDollarsForCopiesSold) func revenuesInDollarsForCopiesSold(numberOfCopies: Int) ->

    USDollars { return numberOfCopies » revenuesForCopiesSold » roundedToTheNearestPenny }
  194. evenuesFromSales(sales: [Int]) -> [USDollars] { s.map(revenuesInDollarsForCopiesSold) func revenuesInDollarsForCopiesSold(numberOfCopies: Int) ->

    USDollars { return numberOfCopies » revenuesForCopiesSold » roundedToTheNearestPenny }
  195. uesInDollarsForCopiesSold(numberOfCopies: Int) -> USDollars { numberOfCopies » revenuesForCopiesSold » roundedToTheNearestPenny

    func revenuesForCopiesSold(numberOfCopies: NumberOfCopies) -> USDollars { return Double(numberOfCopies) * unitPrice * sellersPercentage }
  196. func revenuesForCopiesSold(numberOfCopies: NumberOfCopies) -> USDollars { return Double(numberOfCopies) * unitPrice

    * sellersPercentage }
  197. None
  198. Clean

  199. Clear

  200. Testable

  201. Context

  202. And yet …

  203. »

  204. func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars { return Double(numberOfCopies) *

    unitPrice * sellersPercentage }
  205. func revenues ForCopiesSold(numberOfCopies: Int) -> USDollars { return Double(numberOfCopies) *

    unitPrice * sellersPercentage }
  206. typealias NumberOfCopies = Int func revenues ForCopiesSold(numberOfCopies: Number OfCopies) ->

    USDollars { return Double(numberOfCopies) * unitPrice * sellersPercentage }
  207. extension NumberOfCopies { private func revenuesForCopiesSold() -> USDollars { return

    Double(self) * unitPrice * sellersPercentage } }
  208. extension NumberOfCopies { private func revenuesForCopiesSold() -> USDollars { return

    Double(self) * unitPrice * sellersPercentage } }
  209. extension NumberOfCopies { private func revenuesForCopiesSold() -> USDollars { return

    Double(self) * unitPrice * sellersPercentage } }
  210. extension NumberOfCopies { private func revenuesForCopiesSold() -> USDollars { return

    Double(self) * unitPrice * sellersPercentage } }
  211. extension USDollars { private func roundedToTheNearestPenny() -> USDollars { return

    round(self * 100)/100 } }
  212. extension USDollars { private func roundedToTheNearestPenny() -> USDollars { return

    round(self * 100)/100 } }
  213. extension USDollars { private func roundedToTheNearestPenny() -> USDollars { return

    round(self * 100)/100 } }
  214. func revenuesInDollarsForCopiesSold(number OfCopies: Int) -> USDollars { return numberOfCopies »

    revenuesForCopiesSold » roundedToTheNearestPenny }
  215. extension NumberOfCopies { private func revenuesIn DollarsForCopiesSold() -> USDollars {

    return self.revenues ForCopiesSold().roundedToTheNearestPenny () } }
  216. extension NumberOfCopies { private func revenuesIn DollarsForCopiesSold() -> USDollars {

    return self.revenues ForCopiesSold().roundedToTheNearestPenny () } }
  217. extension NumberOfCopies { private func revenuesIn DollarsForCopiesSold() -> USDollars {

    return self.revenues ForCopiesSold().roundedToTheNearestPenny () } }
  218. extension NumberOfCopies { private func revenuesIn DollarsForCopiesSold() -> USDollars {

    return self.revenues ForCopiesSold().roundedToTheNearestPenny () } }
  219. extension NumberOfCopies { private func revenuesIn DollarsForCopiesSold() -> USDollars {

    return self.revenues ForCopiesSold().roundedToTheNearestPenny () } }
  220. extension NumberOfCopies { private func revenuesIn DollarsForCopiesSold() -> USDollars {

    return self.revenues ForCopiesSold().roundedToTheNearestPenny () } }
  221. extension NumberOfCopies { private func revenuesIn DollarsForCopiesSold() -> USDollars {

    return self.revenues ForCopiesSold().roundedToTheNearestPenny () } }
  222. 7.revenuesInDollarsForCopiesSold()

  223. 7.revenuesInDollarsForCopiesSold() 9.75

  224. extension NumberOfCopies { private func revenues InDollarsForCopiesSold() -> USDollars {

    return revenues ForCopiesSold().roundedToTheNearestPenny () } }
  225. Removed »

  226. That's Béchamel

  227. What about Lasagne?

  228. func replace NegativeSalesWithZeroSales(sales: [Int]) -> [Int] { return sales.map(negativeNumbersToZero) }

  229. Where?

  230. extension [NumberOfCopies] { }

  231. Can't

  232. extension [Int] { }

  233. Still Can't

  234. Rich Hickey's Trolley

  235. Not bad for map()

  236. Wait a minute

  237. What does this have to do with Lasagne?

  238. Everything at once

  239. Fancy made to order

  240. Back to map()

  241. Combine

  242. func anArrayOf DailySales(rawSales: AppSales) -> [Int] { return rawSales.map{$0} }

    func replace NegativeSalesWithZeroSales(sales: [Int]) -> [Int] { return sales.map(negativeNumbersToZero) } func calcu lateRevenuesFromSales(sales: [Int]) -> [USDollars] { return sales.map(revenuesInDollarsForCopiesSold) }
  243. func processEndToEnd(sales: AppSales) -> USDollars { return sales.map(transform: Int ->

    U) }
  244. Combine

  245. func anArrayOf DailySales(rawSales: AppSales) -> [Int] { return rawSales.map{$0} }

    func replace NegativeSalesWithZeroSales(sales: [Int]) -> [Int] { return sales.map(negativeNumbersToZero) } func calcu lateRevenuesFromSales(sales: [Int]) -> [USDollars] { return sales.map(revenuesInDollarsForCopiesSold) } func processEndToEnd(sales: AppSales) -> USDollars { return sales.map(transform: Int -> U) }
  246. func processEndToEnd(sales: AppSales) -> USDollars { return sales.map(transform: Int ->

    U) }
  247. This doesn't work

  248. func process EndToEnd(sales: AppSales) -> USDollars { return sales .map(negativeNumbersToZero

    » revenuesInDollarsForCopiesSold) }
  249. Types don't match

  250. This works …

  251. func combinedSteps(input: Int) -> USDollars { return input » negativeNumbersToZero

    » revenuesInDollarsForCopiesSold } func processEndToEnd(sales: AppSales) -> [USDollars] { return sales.map(combinedSteps) }
  252. func combinedSteps(input: Int) -> USDollars { return input » negativeNumbersToZero

    » revenuesInDollarsForCopiesSold } func processEndToEnd(sales: AppSales) -> [USDollars] { return sales.map(combinedSteps) }
  253. func combinedSteps(input: Int) -> USDollars { return input » negativeNumbersToZero

    » revenuesInDollarsForCopiesSold } func processEndToEnd(sales: AppSales) -> [USDollars] { return sales.map(combinedSteps) }
  254. func combinedSteps(input: Int) -> USDollars { return input » negativeNumbersToZero

    » revenuesInDollarsForCopiesSold } func processEndToEnd(sales: AppSales) -> [USDollars] { return sales.map(combinedSteps) }
  255. Want…

  256. func processEndToEnd(sales: AppSales) -> [USDollars] { return sales.map(negativeNumbersToZero » revenuesInDollarsForCopiesSold)

    }
  257. The types

  258. Overload »

  259. func »<T,U,V> (firstFunction: T -> U, secondFunction: U -> V)

    -> T -> V { return {x in secondFunction(firstFunction(x)) } }
  260. func »<T,U,V> (firstFunction: T -> U, secondFunction: U -> V)

    -> T -> V { return {x in secondFunction(firstFunction(x)) } }
  261. func »<T,U,V> (firstFunction: T -> U, secondFunction: U -> V)

    -> T -> V { return {x in secondFunction(firstFunction(x)) } }
  262. func »<T,U,V> (firstFunction: T -> U, secondFunction: U -> V)

    -> T -> V { return {x in secondFunction(firstFunction(x)) } }
  263. func »<T,U,V> (firstFunction: T -> U, secondFunction: U -> V)

    -> T -> V { return {x in secondFunction(firstFunction(x)) } }
  264. let lastWeeksLazyRevenues = lastWeeksSales .map( negativeNumbersToZero » revenuesInDollarsForCopiesSold)

  265. let lastWeeksLazyRevenues = lastWeeksSales .map( negativeNumbersToZero » revenuesInDollarsForCopiesSold)

  266. let lastWeeksLazyRevenues = lastWeeksSales .map( negativeNumbersToZero » revenuesInDollarsForCopiesSold)

  267. let lastWeeksLazyRevenues = lastWeeksSales .map( negativeNumbersToZero » revenuesInDollarsForCopiesSold)

  268. map

  269. Rich Hickey's Trolley

  270. flatmap T -> [U] [T] -> [ U]

  271. filter T -> Bool [T] -> [ T]

  272. map T -> U [T] -> [ U]

  273. reduce U, (U, T) -> U [T] -> U

  274. None
  275. reduce

  276. Béchamel

  277. Mother Sauce

  278. reduce

  279. Mother Sauce

  280. map with reduce

  281. filter with reduce

  282. flatmap with reduce

  283. Inefficient

  284. Instructive

  285. reduce

  286. reduce U, (U, T) -> U

  287. reduce U, (U, T) -> U initial value

  288. reduce U, (U, T) -> U step

  289. reduce U, (U, T) -> U

  290. map with reduce

  291. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  292. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  293. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  294. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  295. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  296. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  297. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  298. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  299. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  300. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  301. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  302. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  303. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  304. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  305. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  306. extension SequenceType { func mapUsingReduce <U>(transform: Generator.Element -> U) ->

    [U] { let initialValue = [U]() func step(accu mulatedValue: [U], nextElement: Generator .Element) -> [U] { return acc umulatedValue + [transform(nextElement)] } return reduce (initialValue, combine: step) } }
  307. map with reduce

  308. filter with reduce

  309. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  310. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  311. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  312. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  313. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  314. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  315. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  316. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  317. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  318. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  319. func filterUsingReduce (includeElement: Generator.Element -> Bool) -> [Generator.Element] { let

    initialValue = Array<Generator.Element>() func step(accumula tedValue: [Generator.Element], next Element: Generator.Element) -> [Generator .Element] { guard include Element(nextElement) else { return acc umulatedValue } return accumula tedValue + [nextElement] } return reduce(ini tialValue, combine: step) }
  320. filter with reduce

  321. Use these?

  322. func anArrayOfDailySales(rawSales: AppSales) -> [Int] { return rawSales.map{$0} } func

    replaceNegativeSalesWithZero Sales(sales: [Int]) -> [Int] { return sales.map(negative NumbersToZero) } func calculateRevenuesFromSales(sales: [Int]) -> [USDollars] { return sales.map(revenuesInDollars ForCopiesSold) }
  323. Easy!

  324. func anArrayOfDailySales(rawSales: AppSales) -> [Int] { return rawSales.mapUsingReduce{$0} } func

    replaceNegativeSalesWithZero Sales(sales: [Int]) -> [Int] { return sales.mapUsingReduce(negative NumbersToZero) } func calculateRevenuesFromSales(sales: [Int]) -> [USDollars] { return sales.mapUsingReduce(revenues InDollarsForCopiesSold) }
  325. Still need lots of trolleys

  326. None
  327. Performance is worse

  328. Compiler Optimizations

  329. Transducers

  330. None
  331. One reduce()

  332. Chain steps

  333. Swift solution?

  334. lazy?

  335. transducers?

  336. Nothing yet

  337. Nothing official yet

  338. Search: transducers in Swift

  339. Idea - chain steps in reduce

  340. reduce U, (U, T) -> U

  341. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  342. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  343. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  344. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  345. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  346. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  347. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  348. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  349. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  350. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  351. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  352. This seems confusing

  353. Are you sure you understand this?

  354. Not Even Speaker Colleagues Everyone Parents

  355. Not Even Speaker Parents

  356. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  357. Start from the last step

  358. Use reduce [USDollars], ([USDollars], NumberOfCopies) -> [USDollars] revenuesInDollarsForCopiesSold: NumberOfCopies ->

    USDollars [NumberOfCopies] -> [USDollars]
  359. Use reduce [USDollars], ([USDollars], NumberOfCopies) -> [USDollars] revenuesInDollarsForCopiesSold: NumberOfCopies ->

    USDollars [NumberOfCopies] -> [USDollars]
  360. Use reduce [USDollars], ([USDollars], NumberOfCopies) -> [USDollars] revenuesInDollarsForCopiesSold: NumberOfCopies ->

    USDollars [NumberOfCopies] -> [USDollars]
  361. Use reduce [USDollars], ([USDollars], NumberOfCopies) -> [USDollars] revenuesInDollarsForCopiesSold: NumberOfCopies ->

    USDollars [NumberOfCopies] -> [USDollars]
  362. Use reduce [USDollars], ([USDollars], NumberOfCopies) -> [USDollars] revenuesInDollarsForCopiesSold: NumberOfCopies ->

    USDollars [NumberOfCopies] -> [USDollars]
  363. Use reduce [USDollars], ([USDollars], NumberOfCopies) -> [USDollars] revenuesInDollarsForCopiesSold: NumberOfCopies ->

    USDollars [NumberOfCopies] -> [USDollars]
  364. Chain in another operation

  365. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  366. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  367. transducer U, (U, T) -> U U, (U, S) ->

    U f: S -> T [T] -> U [S] -> U
  368. transducer [USDollars], ([USDollars], T) -> [USDollars] [USDollars], ([USDollars], S) ->

    [USDollars] f: S -> T [T] -> [USDollars] [S] -> [USDollars]
  369. transducer [USDollars], ([USDollars], T) -> [USDollars] [USDollars], ([USDollars], S) ->

    [USDollars] f: S -> T [T] -> [USDollars] [S] -> [USDollars]
  370. transducer [USDollars], ([USDollars], NumberOfCopies) -> USDollars [USDollars], ([USDollars], S) ->

    [USDollars] f: S -> NumberOfCopies [NumberOfCopies] -> [USDollars] [S] -> [USDollars]
  371. transducer [USDollars], ([USDollars], NumberOfCopies) -> USDollars [USDollars], ([USDollars], S) ->

    [USDollars] f: S -> NumberOfCopies [NumberOfCopies] -> [USDollars] [S] -> [USDollars] reduce with revenueInDollarsForCopiesSold
  372. transducer [USDollars], ([USDollars], NumberOfCopies) -> USDollars [USDollars], ([USDollars], S) ->

    [USDollars] f: S -> NumberOfCopies [NumberOfCopies] -> [USDollars] [S] -> [USDollars]
  373. transducer [USDollars], ([USDollars], NumberOfCopies) -> USDollars [USDollars], ([USDollars], S) ->

    [USDollars] negativeNumbersToZero: NumberOfCopies -> NumberOfCopies [NumberOfCopies] -> [USDollars] [S] -> [USDollars]
  374. transducer [USDollars], ([USDollars], NumberOfCopies) -> USDollars [USDollars], ([USDollars], S) ->

    [USDollars] negativeNumbersToZero: Int -> NumberOfCopies [NumberOfCopies] -> [USDollars] [S] -> [USDollars]
  375. transducer [USDollars], ([USDollars], NumberOfCopies) -> USDollars [USDollars], ([USDollars], Int) ->

    [USDollars] negativeNumbersToZero: Int -> NumberOfCopies [NumberOfCopies] -> [USDollars] [Int] -> [USDollars]
  376. transducer [USDollars], ([USDollars], NumberOfCopies) -> USDollars [USDollars], ([USDollars], Int) ->

    [USDollars] negativeNumbersToZero: Int -> NumberOfCopies [NumberOfCopies] -> [USDollars] [Int] -> [USDollars] reduce implementation of the composition
  377. transducer

  378. One reduce()

  379. Chain steps

  380. Swift Solution?

  381. Not yet

  382. Keep your code…

  383. Clear

  384. Clean

  385. Concise

  386. Well factored

  387. Testable

  388. Swift

  389. None