66
7 Habits F or a More F unctional Swif t

7 Habits For a More Functional Swift

Embed Size (px)

DESCRIPTION

Functional programming tips in swift.

Citation preview

Page 1: 7 Habits For a More Functional Swift

7 Habits For a More Functional Swift

Page 2: 7 Habits For a More Functional Swift

Jason Larsen

@jarsen

Page 3: 7 Habits For a More Functional Swift

7 Habits1. Avoid mutability

2. Avoid for-loops

3. Combine map/filter/reduce

4. Be lazy

5. Curry functions

6. Write DSLs

7. Stop objectifying code

Page 4: 7 Habits For a More Functional Swift

What is a Function?

f(x) = x * x

Page 5: 7 Habits For a More Functional Swift

Functions Are Mappings4 Do not mutate input

4 Do not change external state

4 Determined only by explicit inputs

Page 6: 7 Habits For a More Functional Swift

Consequences of Pure Functions4 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

Page 7: 7 Habits For a More Functional Swift

Consequences of Pure Functions4 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

Page 8: 7 Habits For a More Functional Swift

Is Swift Functional?

Page 9: 7 Habits For a More Functional Swift

#1

Let it be

Page 10: 7 Habits For a More Functional Swift

Bad// find the bug, and don't tell me you've never done thisfunc 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))}

Page 11: 7 Habits For a More Functional Swift

Goodfunc square(x: Int) -> Int { return x * x}

let a = [1,2,3,4,5]let b = a.map({x in square(x)})

Page 12: 7 Habits For a More Functional Swift

Beautifulfunc square(x: Int) -> Int { return x * x}

let a = [1,2,3,4,5]let b = a.map(square)

Page 13: 7 Habits For a More Functional Swift

Immutable structsstruct Person { let name: String let age: Int}

let alice = Person(name: "Alice", age: 22)let alice2 = Person(name: alice.name, age: 23) // transform data

Page 14: 7 Habits For a More Functional Swift

Transforming Immutable Objectsextension 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")

Page 15: 7 Habits For a More Functional Swift

Transforming Immutable Objectsstruct 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)

Page 16: 7 Habits For a More Functional Swift

Transforming Immutable Objectsstruct 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)

Page 17: 7 Habits For a More Functional Swift

Transforming Immutable Objectsclass 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)

Page 18: 7 Habits For a More Functional Swift

#2

for the love of loops!

Page 19: 7 Habits For a More Functional Swift

MapMap each item in an existing collection to something else.

Page 20: 7 Habits For a More Functional Swift

FilterFind objects in a collection that match your criteria by filtering out everything that doesn't match.

Page 21: 7 Habits For a More Functional Swift

Uglyvar bestStudents = [Student]()

for student in students { if (student.grade > 90) { bestStudents.append(student) }}

Page 22: 7 Habits For a More Functional Swift

Beautifullet bestStudents = students.filter { $0.grade > 90 }

Page 23: 7 Habits For a More Functional Swift

Also Beautifulfunc isBestStudent(student: Student) -> Bool { return student.grade > 90}

let bestStudents = students.filter(isBestStudent)

Page 24: 7 Habits For a More Functional Swift

ReduceReduces all sequence elements into one value. Takes an initial value, passes that value through as an accumulator, which may be updated in each iteration.

Page 25: 7 Habits For a More Functional Swift

Uglylet a = [1,2,3,4,5]

var sum = 0for x in a { sum += x}

Page 26: 7 Habits For a More Functional Swift

Calculating Sums With Reducelet a = [1,2,3,4]let sum = a.reduce(0, combine: { (accumulator, value) in return accumulator + value})

Page 27: 7 Habits For a More Functional Swift

Calculating Sums With Reducelet a = [1,2,3,4]let sum = a.reduce(0, combine: { (accumulator, value) in accumulator + value })

Page 28: 7 Habits For a More Functional Swift

Calculating Sums With Reducelet a = [1,2,3,4]let sum = a.reduce(0, combine: { $0 + $1 })

Page 29: 7 Habits For a More Functional Swift

Calculating Sums With Reducelet a = [1,2,3,4]let sum = a.reduce(0, +)

Page 30: 7 Habits For a More Functional Swift

Finding the Max Value With Reducelet numbers = [1,4,15,23,9]

if let initial = numbers.first { let numMax = numbers.reduce(initial) { (m, x) in return x > m ? x : m }}

Page 31: 7 Habits For a More Functional Swift

Finding the Max Value With Reducelet numbers = [1,4,15,23,9]

if let initial = numbers.first { let numberMax = numbers.reduce(initial) { (m, x) in return max(m, x) }}

Page 32: 7 Habits For a More Functional Swift

Finding the Max Value With Reducelet numbers = [1,4,15,23,9]

if let initial = numbers.first { let numberMax = numbers.reduce(initial, max)}

Page 33: 7 Habits For a More Functional Swift

Counting Frequencies with Reducelet 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) }}

Page 34: 7 Habits For a More Functional Swift

Composing Filterstypealias Filter = CIImage -> CIImage

let filters: [Filter] = [colorOverlay, blur, drawTitle]let filteredImage = filters.reduce(image, combine: { $1($0) } )

Page 35: 7 Habits For a More Functional Swift

#3

By our powers combined

Page 36: 7 Habits For a More Functional Swift

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: +)

Page 37: 7 Habits For a More Functional Swift

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}

Page 38: 7 Habits For a More Functional Swift

Zip it uplet a = Array(1...5)let b = Array(6...10)let result = map(Zip2(a,b), +) // [7, 9, 11, 13, 15]

Page 39: 7 Habits For a More Functional Swift

#4

Be Lazy

Page 40: 7 Habits For a More Functional Swift

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 }}

Page 41: 7 Habits For a More Functional Swift

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 }}

Page 42: 7 Habits For a More Functional Swift

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())

Page 43: 7 Habits For a More Functional Swift

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]

Page 44: 7 Habits For a More Functional Swift

#5

Curried Functions. Yum.

Page 45: 7 Habits For a More Functional Swift

func addNormal(x:Int, y : Int) -> Int { return x + y}

let sum = addNormal(1, 2)

Page 46: 7 Habits For a More Functional Swift

func addCurried(x:Int) -> Int -> Int { return {y in return x + y}}

let sum = addCurried(1)(2)

Page 47: 7 Habits For a More Functional Swift

let numbers = Array(0...5)let numbersIncrementedBy1 = numbers.map(addCurried(1))let numbersIncrementedBy2 = numbers.map(addCurried(2))

Page 48: 7 Habits For a More Functional Swift

// 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)

Page 49: 7 Habits For a More Functional Swift

Instance Methods are Curriedclass 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.depositdepositor(account)(100) // balance is now 200

Page 50: 7 Habits For a More Functional Swift

#6

Domain-Specific Langauges

Page 51: 7 Habits For a More Functional Swift

Custom Flow Controlfunc unless(condition: Bool, then: () -> ()) { if (!condition) { then() }}

unless(1 != 1) { println("Phew. Identity holds.")}

Page 52: 7 Habits For a More Functional Swift

Cucumber-Style BDD Frameworkgiven("I have entered (.*) into the calculator") { n in let calculator = Calculator() calculator.push(n)}

Page 53: 7 Habits For a More Functional Swift

Sinatra-Style Web FrameworkGET("/greetings/:name") { request, params in let name = params["name"] ?? "Anonymous" let greeting = "<h1>Hello, \(name)!</h1>" return Response(body: greeting, code: 200)}

Page 54: 7 Habits For a More Functional Swift

Custom Operatorsinfix 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)

Page 55: 7 Habits For a More Functional Swift

#7

Stop Objectifying Code

Page 56: 7 Habits For a More Functional Swift

Objectification4 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

Page 57: 7 Habits For a More Functional Swift

Functionalization4 Data

4 Functions transform data

Page 58: 7 Habits For a More Functional Swift

Thinking About Data

Page 59: 7 Habits For a More Functional Swift

Arraysgreat for variable length data of the same type

4 list of students in a class

4 lines in a document

4 search results in a JSON response

Page 60: 7 Habits For a More Functional Swift

Tuples / Named Tuplesfixed length list. can hold mixed types, but probably best to prefer same types

4 Points/Vectors

4 functions with multiple return values

typealias Vector2D = (x: Double, y: Double)let foo = Vector2D(2, 4)

Page 61: 7 Habits For a More Functional Swift

DictionariesDictionaries are maps.

4 anything that needs to be mapped to something else

4 a JSON response

Page 62: 7 Habits For a More Functional Swift

StructsEncapsulate properties of multiple types. No inheritance. Free constructor for all the immutable properties.

4 Anything you might use a tuple for

4 Data related to a student - grade, first name, last name

Page 63: 7 Habits For a More Functional Swift

EnumsAnytime something has a set of options

4 HTTP Methods

4 Errors

4 Optionals

Page 64: 7 Habits For a More Functional Swift

ClassesObjects. Object Oriented Programming. Not a bad thing, but not terribly functional.

Page 65: 7 Habits For a More Functional Swift

Typealias All The Thingstypealias Filter = Request->Requesttypealias Handler = (Request,Parameters)->Responsetypealias Model = [String : [String]]typealias Renderer = Model -> Stringtypealias Parameters = [String: String]

Page 66: 7 Habits For a More Functional Swift

Resources4 http://www.drewag.me/posts/practical-use-for-curried-functions-in-swift

4 https://www.skillsmatter.com/skillscasts/5678-an-introduction-to-haskell

4 http://matt.might.net/articles/implementing-laziness

4 http://www.scottlogic.com/blog/2014/06/26/swift-sequences.html

4 http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/