Share
Embed
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
Transcript
109 slides
-
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 -
Speaker:(Farley(Caesar • Programming*since*the*early*80's • BSc.*in*Computer*Science*from*the* University*of*Toronto • Programming*professionally*since*1997* (Bell*Sygma,*IBM) •
Programming*on*iOS*since*2011 ©"Farley"Caesar,"2015 3 -
struct Speaker { let identifyingTraits: [String:String] let languagesTimeline: [String] let
languagesUsedInAnger: [String] let currentLanguageOfChoice: String } ©"Farley"Caesar,"2015 4 -
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 -
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 -
let currentLanguageOfChoice = "Swift" var farleyCaesar = Speaker( identifyingTraits: identifyingTraits,
languagesTimeline: languagesTimeline, languagesUsedInAnger: languagesUsedExtensivelyAndOfCourseInAnger, currentLanguageOfChoice: currentLanguageOfChoice) ©"Farley"Caesar,"2015 8 -
Survey'Time Show%of%hands... • Familiar(with:( • Swi.? • Func3onal(Programming? • A(Func3onal(Language:
• Haskell,(F#,(Scala,(Clojure? ©"Farley"Caesar,"2015 9 -
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 -
Quick&Aside&on&Clojure One$of$the$earliest$programming$languages$that$embodied$ func5onal$concepts$was$Lisp.$My$func5onal$weapon$of$choice,$ un5l$a$li<le$while$ago$was$Clojure. • Clojure)is)a)heavily)func3onal)programming)language.) • Clojure)is)a)Lisp)that)runs)primarily)on)the)Java)Virtual)Machine. • For)the)most)part,)cannot)build)1st)class)Cocoa/Cocoa)Touch)
apps)in)Clojure. ©"Farley"Caesar,"2015 23 -
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 -
Func%onal)Swi- Swi$%has%first%class%support%for%the%aforemen5oned%func5onal% aspects,%plus%a%nice%consistent%syntax%for%func5ons%and%closures. Okay, yes. But can't I do all
that in Objective-C? ©"Farley"Caesar,"2015 36 -
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 -
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 -
Value&Types:"Int,"Float,"Bool,"String,"Array,"Dic4onary,"Structs," Enums,"Tuples,".. Example(from:(h.ps://speakerdeck.com/andymatuschak/controlling;complexity;in;swi=;or;making;friends;with;value " var a = 10 var b
= a // a = 10; b = 10 b = 5 // a = 10; b = 5 ©"Farley"Caesar,"2015 44 -
Reference'Types:"Classes Example(from:(h.ps://speakerdeck.com/andymatuschak/controlling;complexity;in;swi=;or;making;friends;with;value " // Objects var a = UIView() var
b = a // a.alpha = 1; b.alpha = 1 b.alpha = 0 // a.alpha = 0; b.alpha = 0 ©"Farley"Caesar,"2015 46 -
Pure%Func)ons 1. The&func+on&always&evaluates&to&the&same&result&value&given&the& same&argument&value(s).....&The&result&value&need¬&depend&on& all&(or&any)&of&the&argument&values.&However,&it&must&depend&on& nothing&other&than&the&argument&values. 2. Evalua+on&of&the&result&does¬&cause&any&seman+cally& observable&side&effect&or&output,&such&as&muta+on&of&mutable& objects&or&output&to&I/O&devices&
!!"Wikipedia"(h,p:/ /en.wikipedia.org/wiki/Pure_func:on) ©"Farley"Caesar,"2015 48 -
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 -
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 -
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 -
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 -
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 -
MAP ───── ┌───────┬───────┬───────┬───────┐ │ ┌─┐ │ ┌─┐ │ ┌─┐ │
┌─┐ │ │ └─┘ │ └─┘ │ └─┘ │ └─┘ │ └───────┴───────┴───────┴───────┘ │ transformation function │ ▼ ┌───────┬───────┬───────┬───────┐ │ ┌▪┐ │ ┌▪┐ │ ┌▪┐ │ ┌▪┐ │ │ └▪┘ │ └▪┘ │ └▪┘ │ └▪┘ │ └───────┴───────┴───────┴───────┘ ©"Farley"Caesar,"2015 60 -
Map$Example$for$Array<T> Declara'on: func map<U>(transform: (T) -> U) -> [U] Returns(a(new(array(with(the(transformClosure(applied(to(all(
elements(in(the(receiver. ©"Farley"Caesar,"2015 61 -
Map$Example$for$Array<T> Requirement: Produce)a)list)of)the)squares)of)the)first)10)posi6ve)numbers. let firstTenPositiveIntegers = [Int](1...10) // => [1,
2, 3, 4, 5, 6, 7, 8, 9, 10] ©"Farley"Caesar,"2015 62 -
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 -
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 -
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 -
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 -
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 -
[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 -
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 -
FILTER ────── ┌───────┬───────┬───────┬───────┐ │ ┌─┐ │ ┌─┐ │ ┌─┐ │
┌─┐ │ │ └─┘ │ └─┘ │ └─┘ │ └─┘ │ └───────┴───────┴───────┴───────┘ │ transformation function │ ▼ ┌───────┬───────┐ │ ┌─┐ │ ┌─┐ │ │ └─┘ │ └─┘ │ └───────┴───────┘ ©"Farley"Caesar,"2015 71 -
Filter'Example'for'Array<T> Declara'on: func filter(includeElement: (T) -> Bool) -> [T] Returns(a(new(array(containing(the(elements(of(the(original(array(
for(which(a(provided(closure(indicates(a(match. ©"Farley"Caesar,"2015 72 -
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 -
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 -
REDUCE ────── ┌───────┬───────┬───────┬───────┐ │ ┌─┐ │ ┌─┐ │ ┌─┐ │
┌─┐ │ │ └─┘ │ └─┘ │ └─┘ │ └─┘ │ └───────┴───────┴───────┴───────┘ │ transformation function │ ▼ ┌▪┐ └▪┘ ©"Farley"Caesar,"2015 76 -
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 -
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 -
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 -
Func%onal)Approach): Can$even$use$operators$as$the$argument$to$combine..$which$is$cool. let firstTenPositiveIntegers = [Int](1...10) let sumOfEvenSquaresInFirstTen = firstTenPositiveIntegers.map({$0
* $0}) .filter({($0 % 2) == 0}) .reduce(0,combine: +) println(sumOfEvenSquaresInFirstTen) // => 220 ©"Farley"Caesar,"2015 81 -
Sequence'Protocol'-'Ranges'are'Sequences'Too map(1...3) {$0 * 2 } // => [2, 4,
6] ©"Farley"Caesar,"2015 83 -
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 -
I"❤️"this"pa)ern! Even%works%for%side.affecty%impera6ve%code. For$example$in$Ruby [1,2,3].each { |item| puts item } ;
Outputs the following to the console: ; 1 ; 2 ; 3 ©"Farley"Caesar,"2015 87 -
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 -
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 -
Swi$ []1,2,3].doForEach { println($0) } // Outputs the following to
the console: // 1 // 2 // 3 ©"Farley"Caesar,"2015 90 -
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 -
So#how#do#we#infuse#func.onal#thinking#into#our#Cococa# applica.ons? • Func%onal)Core)/)Impera%ve)Shell"is"one"approach." • Proposed"as"an"applica1on"architecture"approach"(using"Ruby)"by" Gary"Bernhardt" h<ps:/ /[email protected]/screencasts/catalog/func1onalCcoreCimpera1veCshell •
Great"talk"on"this"from"a"Cocoa"perspec1ve:"Enemy"of"the"State"C" Jus1n"SpahrCSummers" h<ps:/ /speakerdeck.com/jspahrsummers/enemyCofCtheCstate ©"Farley"Caesar,"2015 94 -
Advanced(Func+onal(Tool(Box • Laziness • Flatmap • Currying • Functors,5Applica7ve5Functors •
Monads ©"Farley"Caesar,"2015 97 -
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 -
Func%onal)UI),)The)Holy)Grail As#far#as#I#have#seen,#we're#not#quite#there.#But,#watch#this#space.# Some%research%in%this%area:% • Reac%ve'Cocoa" h$ps:/ /github.com/Reac5veCocoa/Reac5veCocoa • React'Na%ve" h$p:/
/www.reactna5ve.com/ • Func%onal'View'Controllers" h$p:/ /chris.eidhof.nl/posts/func5onal>view>controllers.html ©"Farley"Caesar,"2015 101 -
Where%to%go%from%here? • Check'out'the'resources'men/oned'in'earlier'slides'of'this'talk. • Check'out'the'presenta/ons'from'the'inaugural'Func/onal'Swi;' conference h<p:/ /2014.funswi;conf.com • Read'the'Func%onal)Programming)in)Swi1'book.
h<p:/ /www.objc.io/books/ • Take'/me'to'learn'a'func/onal'language'(Clojure,'Haskell,'Scala,' etc.). ©"Farley"Caesar,"2015 104