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

Functional Swift - An Introduction

Functional Swift - An Introduction

A presentation about Functional Programming in Swift. The talk was given by Farley Caesar at the TACOW Meetup (http://www.meetup.com/tacow-org/) on March 10, 2015.

Video of the talk available at: https://youtu.be/7EuNH10k-BM

publicfarley

March 10, 2015
Tweet

Other Decks in Programming

Transcript

  1. Agenda * Your Speaker * What is Functional Programming? *

    Why Functional? Why Functional in Swift? * Key Aspects of a Functional Approach * Your Functional Tool Box * Swift Value Types vs Reference Types * Application Architecture * Your Advanced Functional Tool Box * Next Steps v1.0%(March%2015) ©"Farley"Caesar,"2015 2
  2. struct Speaker { let identifyingTraits: [String:String] let languagesTimeline: [String] let

    languagesUsedInAnger: [String] let currentLanguageOfChoice: String } ©"Farley"Caesar,"2015 4
  3. let identifyingTraits = ["Name" : "Farley Caesar", "Title" : "Solution

    Architect", "Company" : "Royal Bank of Canada", "Interests" : "Coding, Basketball", "Twitter" : "@publicfarley", "email" : "[email protected]", "blog" : "http://farley.tumblr.com", "LinkedIn" : "Farley Caesar"] ©"Farley"Caesar,"2015 5
  4. let languagesTimeline = ["AppleSoft Basic", "6502 Assembler", "Pascal", "Ada", "COBOL",

    "Prolog", "Lisp", "Object Oriented Turing", "C", "C++", "Java", "Groovy", "Clojure", "Ruby", "Objective-C", "Swift"] ©"Farley"Caesar,"2015 6
  5. let currentLanguageOfChoice = "Swift" var farleyCaesar = Speaker( identifyingTraits: identifyingTraits,

    languagesTimeline: languagesTimeline, languagesUsedInAnger: languagesUsedExtensivelyAndOfCourseInAnger, currentLanguageOfChoice: currentLanguageOfChoice) ©"Farley"Caesar,"2015 8
  6. The$Free$Lunch$is$Over!2 • The%number%of%transistors%con2nues%to% climb,%but%CPU%speed%has%plateaued. • Need%someplace%to%put%those% transistors...%Cores!% • Concurrent%programming%is%our%future. •

    A%func2onal%approach%to%programming% makes%concurrent%programming%simpler. 2"The"Free"Lunch"Is"Over:"A"Fundamental"Turn"Toward"Concurrency"in" So=ware">"h?p:/ /www.gotw.ca/publicaEons/concurrency>ddj.htm ©"Farley"Caesar,"2015 22
  7. Learn&Lisp&in&1&Slide! \\ C: add(1,2); \\ Obj-C: [adder addNumber:1 toNumber:2]; ;

    Lisp (add 1 2) Now$you$know$Lisp! ©"Farley"Caesar,"2015 25
  8. Immutable)Data)+)Benefits • Persistent)*)Values)can't)change)out) from)underneath)you) • Can)be)shared)without)worry • Thread)Safe)*)Values)don't)mind)many) observers •

    Simple)*)Whole)classes)of)concerns)go) away • Allows)avoidance)of)thinking)of)Bme) and)sequence) ©"Farley"Caesar,"2015 40
  9. Immutable)Data)+)Benefits Excellent(talks(that(go(deep(into(this( subject:( • The$Value$of$Values"#"Rich"Hickey" h,p:/ /www.infoq.com/presenta;ons/Value#Values " • Controlling$Complexity$in$Swi8$or$

    Making$Friends$with$Value"#"Andy" Matuschak h,p:/ /realm.io/news/andy#matuschak#controlling#complexity/ h,p:/ /www.objc.io/issue#16/swiH#classes#vs#structs.html ©"Farley"Caesar,"2015 41
  10. Swi$%&%Func+on%Syntax Func%ons: // func [function name][Args] -> [Return Type] {

    // [Body] // } // Declaration func doubleIt(x: Int) -> (Int) { return 2 * x } // Use doubleIt(4) // => 16 ©"Farley"Caesar,"2015 52
  11. Swi$%&%Closure%Syntax // Block Signature (Closure that takes no arguments and

    returns Void): // ()->() // eg. Closure that takes an Int and returns an Int: // (Int) -> (Int) // Closure that takes an Int and returns an Int let doubleIt: (Int)->(Int) = { (x: Int) in return 2 * x } // Use doubleIt(4) // => 16 ©"Farley"Caesar,"2015 53
  12. Closure(Example: let x: Int = 5 func add5(y: Int) ->

    Int { return x+y } // This named closure (a function) captures the value of x at time of declaration add5(50) // => 55 // Notice how the closure can be stored and used later let functionDictionary: [String : (Int)->Int] = ["addAFiver":add5] if let anAdd5Function = functionDictionary["addAFiver"] { println(anAdd5Function(50)) } // => 55 println(functionDictionary["addAFiver"]!(50)) // => 55 ©"Farley"Caesar,"2015 55
  13. Higher'Order'Func.ons'1'Func.ons'as'Arguments We're%already%used%to%this%with%comple4on%blocks.%We'll%see,%in% later%slides,%how%to%apply%this%with%more%purity%using%map,%filter,% reduce,%etc.. // Completion block example... import EventKit

    EKEventStore().requestAccessToEntityType(EKEntityTypeEvent, completion: { (isHasAccessToApplicationCalendar: Bool, error: NSError?) in if isHasAccessToApplicationCalendar { println("I'm in baby!") } else { let message = "Access not granted to events. " + (error?.description ?? "") println(message) } }) ©"Farley"Caesar,"2015 56
  14. Higher'Order'Func.ons'1'Func.ons'as'Return'Values func greaterThanFunctionFor(n: Int) -> (Int -> Bool) { let

    greaterThanFunc: Int -> Bool = { m in return m > n } return greaterThanFunc } let isGreaterThan10 = greaterThanFunctionFor(10) isGreaterThan10(20) // => true isGreaterThan10(5) // => false ©"Farley"Caesar,"2015 57
  15. MAP ───── ┌───────┬───────┬───────┬───────┐ │ ┌─┐ │ ┌─┐ │ ┌─┐ │

    ┌─┐ │ │ └─┘ │ └─┘ │ └─┘ │ └─┘ │ └───────┴───────┴───────┴───────┘ │ transformation function │ ▼ ┌───────┬───────┬───────┬───────┐ │ ┌▪┐ │ ┌▪┐ │ ┌▪┐ │ ┌▪┐ │ │ └▪┘ │ └▪┘ │ └▪┘ │ └▪┘ │ └───────┴───────┴───────┴───────┘ ©"Farley"Caesar,"2015 60
  16. Impera've)Approach: let firstTenPositiveIntegers = [Int](1...10) var firstTenSquares = [Int]() for

    number in firstTenPositiveIntegers { firstTenSquares.append(number*number) } println(firstTenSquares) // => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] ©"Farley"Caesar,"2015 63
  17. Problems)with)the)Impera1ve)Approach: • Mixes'the'mechanism'(step'through'and'select'each'item)'with' the'policy'of'transforma9on'of'the'items.' • Not'declara9ve • Also,'what'if'now'we'do'the'following... firstTenSquares.append(23) println(firstTenSquares)

    => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 23] // Oops! That's not the 1st 10 squares. // Must remember to make result immutable to avoid incorrect modification. ©"Farley"Caesar,"2015 64
  18. Func%onal)Approach: let firstTenPositiveIntegers = [Int](1...10) let firstTenSquares = firstTenPositiveIntegers.map {$0

    * $0} println(firstTenSquares) // => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] ©"Farley"Caesar,"2015 65
  19. Func%onal)Approach: Some%may%find%the%following%more%readable..%YMMV. let firstTenPositiveIntegers = [Int](1...10) func squareInt(num: Int) ->

    Int { return num * num } let firstTenSquares = firstTenPositiveIntegers.map(squareInt) println(firstTenSquares) // => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] ©"Farley"Caesar,"2015 66
  20. Func%onal)Approach: Some%may%find%the%following%more%readable..%YMMV. let firstTenPositiveIntegers = [Int](1...10) let firstTenSquares = firstTenPositiveIntegers.map

    { number in return (number * number) } println(firstTenSquares) // => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] ©"Farley"Caesar,"2015 67
  21. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    // | // | map (item * item) // | // ▼ [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] ©"Farley"Caesar,"2015 69
  22. What%if%now... firstTenSquares.append(23) /* Not gonna' be able ta' do it!

    => error: immutable value of type 'Array<Int>' only has mutating members named 'append' */ ©"Farley"Caesar,"2015 70
  23. FILTER ────── ┌───────┬───────┬───────┬───────┐ │ ┌─┐ │ ┌─┐ │ ┌─┐ │

    ┌─┐ │ │ └─┘ │ └─┘ │ └─┘ │ └─┘ │ └───────┴───────┴───────┴───────┘ │ transformation function │ ▼ ┌───────┬───────┐ │ ┌─┐ │ ┌─┐ │ │ └─┘ │ └─┘ │ └───────┴───────┘ ©"Farley"Caesar,"2015 71
  24. Impera've)Approach: let firstTenPositiveIntegers = [Int](1...10) var evenSquaresInFirstTen = [Int]() for

    number in firstTenPositiveIntegers { let square = number * number if (square % 2) == 0 { evenSquaresInFirstTen.append(square) } } println(evenSquaresInFirstTen) // => [4, 16, 36, 64, 100] ©"Farley"Caesar,"2015 74
  25. Func%onal)Approach): let firstTenPositiveIntegers = [Int](1...10) let evenSquaresInFirstTen = firstTenPositiveIntegers.map({$0 *

    $0}) .filter({($0 % 2) == 0}) println(evenSquaresInFirstTen) // => [4, 16, 36, 64, 100] ©"Farley"Caesar,"2015 75
  26. REDUCE ────── ┌───────┬───────┬───────┬───────┐ │ ┌─┐ │ ┌─┐ │ ┌─┐ │

    ┌─┐ │ │ └─┘ │ └─┘ │ └─┘ │ └─┘ │ └───────┴───────┴───────┴───────┘ │ transformation function │ ▼ ┌▪┐ └▪┘ ©"Farley"Caesar,"2015 76
  27. Reduce&Example&for&Array<T> Declara'on: func reduce<U>(initial: U, combine: (U, T) -> U)

    -> U Returns(a(single(value(represen/ng(the(result(of(applying(a( provided(reduc/on(closure(for(each(element. ©"Farley"Caesar,"2015 77
  28. Impera've)Approach: let firstTenPositiveIntegers = [Int](1...10) var sumOfEvenSquaresInFirstTen = 0 for

    number in firstTenPositiveIntegers { let square = number * number if (square % 2) == 0 { sumOfEvenSquaresInFirstTen += square } } println(sumOfEvenSquaresInFirstTen) // => 220 ©"Farley"Caesar,"2015 79
  29. Func%onal)Approach): let firstTenPositiveIntegers = [Int](1...10) let sumOfEvenSquaresInFirstTen = firstTenPositiveIntegers.map({$0 *

    $0}) .filter({($0 % 2) == 0}) .reduce(0,combine: {$0 + $1}) println(sumOfEvenSquaresInFirstTen) // => 220 ©"Farley"Caesar,"2015 80
  30. Sequence'Protocol'-'Strings'are'just'sequences'of'Characters! func replaceAllOf(candidate: Character, with replacement: Character, inPassage passage: String)

    -> String { let replacedCharSequence = map(passage) { item -> Character in return (item == candidate ? replacement : item) } return String(replacedCharSequence) } replaceAllOf("o", with: "_", inPassage: "ooooooh this is pretty cool!") // => "______h this is pretty c__l!" ©"Farley"Caesar,"2015 84
  31. Ruby [1,2,3].each_with_index { |item,index| puts("[#{index}]:#{item}") } ; Outputs the following

    to the console: ; [0]:1 ; [1]:2 ; [2]:3 ©"Farley"Caesar,"2015 88
  32. I"❤️"this"pa)ern!"So"in"my"Swi3... extension Array { // Adds iteratng functions to array.

    // They take a closure that returns Void. // Specifically to be used for side effects func doForEach(closure: (T) -> Void) { for index in indices(self) { closure(self[index]) } } func doForEachWithIndex(closure: (T, Int) -> Void) { for index in indices(self) { closure(self[index], index) } } } ©"Farley"Caesar,"2015 89
  33. Swi$ []1,2,3].doForEach { println($0) } // Outputs the following to

    the console: // 1 // 2 // 3 ©"Farley"Caesar,"2015 90
  34. Swi$ [1,2,3].doForEachWithIndex { item, index in println("[\(index)]:\(item)") } // Outputs

    the following to the console: // [0]:1 // [1]:2 // [2]:3 ©"Farley"Caesar,"2015 91
  35. Advanced(Func+onal(Tool(Box Will$not$be$touching$upon$these$concepts$ in$this$talk$(maybe&in&a&subsequent& presenta/on...&!).$ Some%good%resources: • Func%onal)Programming)in)Swi1" h$p:/ /www.objc.io/books/ •

    Why)is)a)Monad)Like)a)Wri%ng)Desk?" h$p:/ /www.infoq.com/presenta9ons/Why<is<a<Monad<Like<a< Wri9ng<Desk • Learn)You)a)Haskell)for)Great)Good Chapters"11&12:"h$p:/ /learnyouahaskell.com ©"Farley"Caesar,"2015 98