Successfully reported this slideshow.
Upcoming SlideShare
×

# 7 Habits For a More Functional Swift

10,065 views

Published on

Functional programming tips in swift.

Published in: Mobile
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Hey guys! Who wants to chat with me? More photos with me here 👉 http://www.bit.ly/katekoxx

Are you sure you want to  Yes  No

### 7 Habits For a More Functional Swift

1. 1. 7 Habits For a More Functional Swift
2. 2. Jason Larsen @jarsen
3. 3. 7 Habits 1. Avoid mutability 2. Avoid for-loops 3. Combine map/filter/reduce 4. Be lazy 5. Curry functions 6. Write DSLs 7. Stop objectifying code
4. 4. What is a Function? f(x) = x * x
5. 5. Functions Are Mappings 4 Do not mutate input 4 Do not change external state 4 Determined only by explicit inputs
6. 6. Consequences of Pure Functions 4 Return the same values every time for input 4 No Side Effects 4 Purity allows laziness (since the value will be the same whenever its computed, we can compute it only when we need it) 4 Concurrency is easy, b/c no shared state
7. 7. Consequences of Pure Functions 4 No I/O (user input, printing, random values, etc) 4 No state 4 No variables (writing to variables is a side effect) 4 No Side Effects
8. 8. Is Swift Functional?
9. 9. #1 Let it be
10. 10. Bad // find the bug, and don't tell me you've never done this func square(x: Int) -> Int { return x * x } var a = [1,2,3,4,5] var b = [Int]() for x in a { a.append(square(x)) }
11. 11. Good func square(x: Int) -> Int { return x * x } let a = [1,2,3,4,5] let b = a.map({x in square(x)})
12. 12. Beautiful func square(x: Int) -> Int { return x * x } let a = [1,2,3,4,5] let b = a.map(square)
13. 13. Immutable structs struct Person { let name: String let age: Int } let alice = Person(name: "Alice", age: 22) let alice2 = Person(name: alice.name, age: 23) // transform data
14. 14. Transforming Immutable Objects extension Dictionary { func dictionaryByUpdatingKey(key: Key, value: Value) -> Dictionary { var mutable = self mutable.updateValue(value, forKey: key) return mutable } } let animalNoiseMap = ["cow" : "moo", "cat" : "meow"] let animalNoiseMapImproved = animalNoiseMap.dictionaryByUpdatingKey("dog", value: "woof")
15. 15. Transforming Immutable Objects struct Person { let name: String let age: Int func age(age: Int) -> Person { return Person(name: self.name, age: age) } } let bob = Person(name: "Bob", age: 25) let birthdayBob = bob.age(bob.age + 1)
16. 16. Transforming Immutable Objects struct Person { let name: String let age: Int static func age(person: Person, age: Int) -> Person { return Person(name: person.name, age: age) } } let bob = Person(name: "Bob", age: 25) let birthdayBob = Person.age(bob, age: bob.age + 1)
17. 17. Transforming Immutable Objects class Person { let name: String let age: Int init(name: String, age: Int) { self.name = name self.age = age } init(person: Person, name: String? = nil, age: Int? = nil) { self.name = name ?? person.name self.age = age ?? person.age } } let bob = Person(name: "Bob", age: 25) let birthdayBob = Person(person: bob, age: bob.age + 1)
18. 18. #2 for the love of loops!
19. 19. Map Map each item in an existing collection to something else.
20. 20. Filter Find objects in a collection that match your criteria by filtering out everything that doesn't match.
21. 21. Ugly var bestStudents = [Student]() for student in students { if (student.grade > 90) { bestStudents.append(student) } }
22. 22. Beautiful let bestStudents = students.filter { \$0.grade > 90 }
23. 23. Also Beautiful func isBestStudent(student: Student) -> Bool { return student.grade > 90 } let bestStudents = students.filter(isBestStudent)
24. 24. Reduce Reduces all sequence elements into one value. Takes an initial value, passes that value through as an accumulator, which may be updated in each iteration.
25. 25. Ugly let a = [1,2,3,4,5] var sum = 0 for x in a { sum += x }
26. 26. Calculating Sums With Reduce let a = [1,2,3,4] let sum = a.reduce(0, combine: { (accumulator, value) in return accumulator + value })
27. 27. Calculating Sums With Reduce let a = [1,2,3,4] let sum = a.reduce(0, combine: { (accumulator, value) in accumulator + value })
28. 28. Calculating Sums With Reduce let a = [1,2,3,4] let sum = a.reduce(0, combine: { \$0 + \$1 })
29. 29. Calculating Sums With Reduce let a = [1,2,3,4] let sum = a.reduce(0, +)
30. 30. Finding the Max Value With Reduce let numbers = [1,4,15,23,9] if let initial = numbers.first { let numMax = numbers.reduce(initial) { (m, x) in return x > m ? x : m } }
31. 31. Finding the Max Value With Reduce let numbers = [1,4,15,23,9] if let initial = numbers.first { let numberMax = numbers.reduce(initial) { (m, x) in return max(m, x) } }
32. 32. Finding the Max Value With Reduce let numbers = [1,4,15,23,9] if let initial = numbers.first { let numberMax = numbers.reduce(initial, max) }
33. 33. Counting Frequencies with Reduce let numbers = [1,4,15,23,1,1,9,9,23,9] let histogram = numbers.reduce([Int: Int]()) { (acc, x) in if let count = acc[x] { return acc.dictionaryByUpdatingKey(x, value: count + 1) } else { return acc.dictionaryByUpdatingKey(x, value: 1) } }
34. 34. Composing Filters typealias Filter = CIImage -> CIImage let filters: [Filter] = [colorOverlay, blur, drawTitle] let filteredImage = filters.reduce(image, combine: { \$1(\$0) } )
35. 35. #3 By our powers combined
36. 36. struct Person { let name: String let age: UInt } let people = [Person(name: "Alice", age: 22), Person(name: "Bob", age: 23), Person(name: "Mallory", age: 25)] let ageSum = people.map({\$0.age}).reduce(0, combine: +)
37. 37. let people = [Person(name: "Alice", age: 22), Person(name: "Bob", age: 23), Person(name: "Mallory", age: 25)] let namesBeforeJason = people.map({\$0.name}).filter { name in name.compare("Jason") == NSComparisonResult.OrderedAscending }
38. 38. Zip it up let a = Array(1...5) let b = Array(6...10) let result = map(Zip2(a,b), +) // [7, 9, 11, 13, 15]
39. 39. #4 Be Lazy
40. 40. class EvenNaturalNumbers: SequenceType { typealias GeneratorType = EvenNaturalNumbersGenerator func generate() -> EvenNaturalNumbersGenerator { return EvenNaturalNumbersGenerator() } } class EvenNaturalNumbersGenerator : GeneratorType { var current = 2 typealias Element = Int func next() -> Int? { let ret = current current += 2 return ret } }
41. 41. class Fibonacci : SequenceType { typealias GeneratorType = FibonacciGenerator func generate() -> FibonacciGenerator { return FibonacciGenerator() } } class FibonacciGenerator : GeneratorType { var current = 0, nextValue = 1 typealias Element = Int func next() -> Int? { let ret = current current = nextValue nextValue = nextValue + ret return ret } }
42. 42. func take<T, S : SequenceType where S.Generator.Element == T>(n: Int, sequence: S) -> [T] { var gen = sequence.generate() var values = [T]() for _ in (1...n) { if let value = gen.next() { values.append(value) } } return values } take(5, [1,2,5,12,31,4,2]) take(10, EvenNaturalNumbers()) take(10, Fibonacci())
43. 43. func filter<S : SequenceType> (source: S, includeElement: (S.Generator.Element) -> Bool) -> [S.Generator.Element] func map<S : SequenceType, T> (source: S, transform: (S.Generator.Element) -> T) -> [T]
44. 44. #5 Curried Functions. Yum.
45. 45. func addNormal(x:Int, y : Int) -> Int { return x + y } let sum = addNormal(1, 2)
46. 46. func addCurried(x:Int) -> Int -> Int { return {y in return x + y} } let sum = addCurried(1)(2)
47. 47. let numbers = Array(0...5) let numbersIncrementedBy1 = numbers.map(addCurried(1)) let numbersIncrementedBy2 = numbers.map(addCurried(2))
48. 48. // taken from the excellent WIP "Functional Programming in Swift" // http://www.objc.io/books/ typealias Filter = CIImage -> CIImage func blur(radius: Double) -> Filter { return { image in let parameters : Parameters = [kCIInputRadiusKey: radius, kCIInputImageKey: image] let filter = CIFilter(name:"CIGaussianBlur", parameters:parameters) return filter.outputImage } } let blurredImage = blur(2.0)(image)
49. 49. Instance Methods are Curried class BankAccount { var balance: Double = 0.0 func deposit(amount: Double) { balance += amount } } let account = BankAccount() account.deposit(100) // balance is now 100 let depositor = BankAccount.deposit depositor(account)(100) // balance is now 200
50. 50. #6 Domain-Specific Langauges
51. 51. Custom Flow Control func unless(condition: Bool, then: () -> ()) { if (!condition) { then() } } unless(1 != 1) { println("Phew. Identity holds.") }
52. 52. Cucumber-Style BDD Framework given("I have entered (.*) into the calculator") { n in let calculator = Calculator() calculator.push(n) }
53. 53. Sinatra-Style Web Framework GET("/greetings/:name") { request, params in let name = params["name"] ?? "Anonymous" let greeting = "<h1>Hello, (name)!</h1>" return Response(body: greeting, code: 200) }
54. 54. Custom Operators infix operator |> { associativity left } func |> (filter1: Filter, filter2: Filter) -> Filter { return {img in filter1(filter2(img))} } let myFilter = blur(blurRadius) |> colorOverlay(overlayColor) let result = myFilter(image)
55. 55. #7 Stop Objectifying Code
56. 56. Objectification 4 Class - Noun 4 Properties - Nouns related to noun above 4 Instance Methods - Actions instance of Class can perform 4 Class Methods - Actions related to Class in general
57. 57. Functionalization 4 Data 4 Functions transform data