Slide 1

Slide 1 text

Demystifying Tomás Ruiz-López @tomasruizlopez Functional Programming Swifters December 2020

Slide 2

Slide 2 text

HARD… Functional Programming is

Slide 3

Slide 3 text

EASY! Functional Programming is

Slide 4

Slide 4 text

Functional Programming? What is

Slide 5

Slide 5 text

Functions Programming with

Slide 6

Slide 6 text

Provide output for every input Total

Slide 7

Slide 7 text

Total func divide(_ x: Double, _ y: Double) -> Double

Slide 8

Slide 8 text

Total func divide(_ x: Double, _ y: Double) -> Double { guard y != 0 else { fatalError("Denominator cannot be zero") } return x / y }

Slide 9

Slide 9 text

Total func divide(_ x: Double, _ y: Double) throws -> Double { guard y != 0 else { throw DivideError.divisionByZero } return x / y }

Slide 10

Slide 10 text

Total func divide(_ x: Double, _ y: Double) -> Result { guard y != 0 else { return .failure(.divisionByZero) } return .success(x / y) }

Slide 11

Slide 11 text

Deterministic Provide the same output for a given input

Slide 12

Slide 12 text

Deterministic func makeFilename(prefix: String, extension ext: String) -> String { let now = Date() return "\(prefix)-\(now).\(ext)" }

Slide 13

Slide 13 text

Deterministic func makeFilename(prefix: String, extension ext: String, now: Date) -> String { return "\(prefix)-\(now).\(ext)" }

Slide 14

Slide 14 text

Pure Only compute output, and nothing else

Slide 15

Slide 15 text

Pure “How can I do something useful in my app without side e f f ects?”

Slide 16

Slide 16 text

Pure func greet(name: String) -> Void { print("Hello \(name)!") } greet(name: “Swifters") > Hello Swifters!

Slide 17

Slide 17 text

Pure func greet(name: String) -> AnyPublisher { Future { promise in print("Hello \(name)!") promise(.success(())) }.eraseToAnyPublisher() }

Slide 18

Slide 18 text

Pure func greet(name: String) -> AnyPublisher { Future { promise in print("Hello \(name)!") promise(.success(())) }.eraseToAnyPublisher() } let publisher = greet(name: “Swifters") > Hello Swifters!

Slide 19

Slide 19 text

Pure func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() }

Slide 20

Slide 20 text

Pure func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() } let publisher = greet(name: “Swifters") >

Slide 21

Slide 21 text

Pure func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() } let cancellable = greet(name: “Swifters").sink { _ in } > Hello Swifters!

Slide 22

Slide 22 text

Pure Why does it matter?

Slide 23

Slide 23 text

reasoning We want to have a sound about our code

Slide 24

Slide 24 text

Quiz How many times does it print “Hello Swifters!”? func greet(name: String) -> AnyPublisher { Future { promise in print("Hello \(name)!") promise(.success(())) }.eraseToAnyPublisher() }

Slide 25

Slide 25 text

Quiz How many times does it print “Hello Swifters!”? func greet(name: String) -> AnyPublisher { Future { promise in print("Hello \(name)!") promise(.success(())) }.eraseToAnyPublisher() } let cancellable = greet(name: "Swifters").flatMap { _ in greet(name: “Swifters") }.sink { _ in }

Slide 26

Slide 26 text

Quiz How many times does it print “Hello Swifters!”? func greet(name: String) -> AnyPublisher { Future { promise in print("Hello \(name)!") promise(.success(())) }.eraseToAnyPublisher() } let cancellable = greet(name: "Swifters").flatMap { _ in greet(name: “Swifters") }.sink { _ in } Solution: 2

Slide 27

Slide 27 text

Quiz How many times does it print “Hello Swifters!”? func greet(name: String) -> AnyPublisher { Future { promise in print("Hello \(name)!") promise(.success(())) }.eraseToAnyPublisher() } let publisher = greet(name: “Swifters") let cancellable = publisher.flatMap { _ in publisher }.sink { _ in }

Slide 28

Slide 28 text

Quiz How many times does it print “Hello Swifters!”? func greet(name: String) -> AnyPublisher { Future { promise in print("Hello \(name)!") promise(.success(())) }.eraseToAnyPublisher() } let publisher = greet(name: “Swifters") let cancellable = publisher.flatMap { _ in publisher }.sink { _ in } Solution: 1

Slide 29

Slide 29 text

Referential Transparency Eager evaluation breaks

Slide 30

Slide 30 text

Quiz How many times does it print “Hello Swifters!”? func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() }

Slide 31

Slide 31 text

Quiz How many times does it print “Hello Swifters!”? let cancellable = greet(name: "Swifters").flatMap { _ in greet(name: “Swifters") }.sink { _ in } func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() }

Slide 32

Slide 32 text

Quiz How many times does it print “Hello Swifters!”? let cancellable = greet(name: "Swifters").flatMap { _ in greet(name: “Swifters") }.sink { _ in } Solution: 2 func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() }

Slide 33

Slide 33 text

Quiz How many times does it print “Hello Swifters!”? func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() } let publisher = greet(name: “Swifters") let cancellable = publisher.flatMap { _ in publisher }.sink { _ in }

Slide 34

Slide 34 text

Quiz How many times does it print “Hello Swifters!”? Solution: 2 func greet(name: String) -> AnyPublisher { Deferred { Future { promise in print("Hello \(name)!") promise(.success(())) } }.eraseToAnyPublisher() } let publisher = greet(name: “Swifters") let cancellable = publisher.flatMap { _ in publisher }.sink { _ in }

Slide 35

Slide 35 text

Pure func greet(name: String) -> IO { IO.invoke { print("Hello \(name)!") } } greet(name: “Swifters").unsafeRunSync() > Hello Swifters! bow-swift.io

Slide 36

Slide 36 text

side effects Model as values

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Functional Programming This is all you need to know about

Slide 39

Slide 39 text

Composition Everything else is

Slide 40

Slide 40 text

Composition func compose( _ g: @escaping (B) -> C, _ f: @escaping (A) -> B ) -> (A) -> C { { a in g(f(a)) } }

Slide 41

Slide 41 text

Composition func compose( _ g: @escaping (B) -> C, _ f: @escaping (A) -> B ) -> (A) -> C { { a in g(f(a)) } } B C A

Slide 42

Slide 42 text

Composition func compose( _ g: @escaping (B) -> C, _ f: @escaping (A) -> B ) -> (A) -> C { { a in g(f(a)) } } B C A g

Slide 43

Slide 43 text

Composition func compose( _ g: @escaping (B) -> C, _ f: @escaping (A) -> B ) -> (A) -> C { { a in g(f(a)) } } B C A f g

Slide 44

Slide 44 text

Composition func compose( _ g: @escaping (B) -> C, _ f: @escaping (A) -> B ) -> (A) -> C { { a in g(f(a)) } } B C A f g g · f

Slide 45

Slide 45 text

Composition func andThen( _ f: @escaping (A) -> B, _ g: @escaping (B) -> C ) -> (A) -> C { { a in g(f(a)) } } B C A f g g · f

Slide 46

Slide 46 text

Higher-order Functions Possible thanks to

Slide 47

Slide 47 text

Composition is not always as easy

Slide 48

Slide 48 text

Composition func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C ) -> (A) -> ??? { // What goes here? }

Slide 49

Slide 49 text

Composition B? A f func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C ) -> (A) -> ??? { // What goes here? }

Slide 50

Slide 50 text

Composition B? A f C g B func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C ) -> (A) -> ??? { // What goes here? }

Slide 51

Slide 51 text

Composition B? A f C g B func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C ) -> (A) -> ??? { // What goes here? }

Slide 52

Slide 52 text

Composition B? A f C g B C? map func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C ) -> (A) -> ??? { // What goes here? }

Slide 53

Slide 53 text

Composition func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C ) -> (A) -> C? { { a in f(a).map(g) } } B? A f C g B C? map

Slide 54

Slide 54 text

Composition func compose( _ f: @escaping (A) -> [B], _ g: @escaping (B) -> C ) -> (A) -> [C] { { a in f(a).map(g) } } func compose( _ f: @escaping (A) -> Result, _ g: @escaping (B) -> C ) -> (A) -> Result { { a in f(a).map(g) } }

Slide 55

Slide 55 text

Functor Compose functions using map func compose( _ f: @escaping (A) -> F, _ g: @escaping (B) -> C ) -> (A) -> F { { a in f(a).map(g) } }

Slide 56

Slide 56 text

Higher-Kinded Types Not possible in Swift, we need

Slide 57

Slide 57 text

Functor Compose functions using map func compose( _ f: @escaping (A) -> Kind, _ g: @escaping (B) -> C ) -> (A) -> Kind { { a in f(a).map(g) } } bow-swift.io

Slide 58

Slide 58 text

Composition func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C? ) -> ??? { // What goes here? }

Slide 59

Slide 59 text

Composition B? A f func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C? ) -> ??? { // What goes here? }

Slide 60

Slide 60 text

Composition B? A f C? g B func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C? ) -> ??? { // What goes here? }

Slide 61

Slide 61 text

Composition B? A f C? g B func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C? ) -> ??? { // What goes here? }

Slide 62

Slide 62 text

Composition B? A f C? g B C? f latMap func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C? ) -> ??? { // What goes here? }

Slide 63

Slide 63 text

Composition B? A f C? g B C? f latMap func compose( _ f: @escaping (A) -> B?, _ g: @escaping (B) -> C? ) -> (A) -> C? { { a in f(a).flatMap(g) } }

Slide 64

Slide 64 text

Composition func compose( _ f: @escaping (A) -> [B], _ g: @escaping (B) -> [C] ) -> (A) -> [C] { { a in f(a).flatMap(g) } } func compose( _ f: @escaping (A) -> Result, _ g: @escaping (B) -> Result ) -> (A) -> Result { { a in f(a).flatMap(g) } }

Slide 65

Slide 65 text

Monad Compose functions using f latMap func compose( _ f: @escaping (A) -> F, _ g: @escaping (B) -> F ) -> (A) -> F { { a in f(a).flatMap(g) } }

Slide 66

Slide 66 text

Monad Compose functions using f latMap func compose( _ f: @escaping (A) -> Kind, _ g: @escaping (B) -> Kind ) -> (A) -> Kind { { a in f(a).flatMap(g) } } bow-swift.io

Slide 67

Slide 67 text

Composition func zip( _ a: A?, _ b: B?, with f: @escaping (A, B) -> C ) -> ??? { // What goes here? }

Slide 68

Slide 68 text

Composition func zip( _ a: A?, _ b: B?, with f: @escaping (A, B) -> C ) -> ??? { // What goes here? } A? B?

Slide 69

Slide 69 text

Composition func zip( _ a: A?, _ b: B?, with f: @escaping (A, B) -> C ) -> ??? { // What goes here? } A? B? A B C f

Slide 70

Slide 70 text

Composition func zip( _ a: A?, _ b: B?, with f: @escaping (A, B) -> C ) -> ??? { // What goes here? } A? B? A B C f

Slide 71

Slide 71 text

Composition func zip( _ a: A?, _ b: B?, with f: @escaping (A, B) -> C ) -> ??? { // What goes here? } A? B? A B C f C? zip

Slide 72

Slide 72 text

Composition func zip( _ a: A?, _ b: B?, with f: @escaping (A, B) -> C ) -> C? { a.flatMap { x in b.map { y in f(x, y) } } } A? B? A B C f C? zip

Slide 73

Slide 73 text

Composition func zip( _ a: [A], _ b: [B], with f: @escaping (A, B) -> C ) -> [C] { a.flatMap { x in b.map { y in f(x, y) } } } func zip( _ a: Result, _ b: Result, with f: @escaping (A, B) -> C ) -> Result { a.flatMap { x in b.map { y in f(x, y) } } }

Slide 74

Slide 74 text

cumbersome Functional Programming terminology is

Slide 75

Slide 75 text

unfamiliar Functional Programming terminology is

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

Composition func loadWebResource(_ path: String) -> Resource? func decodeImage(_ r1: Resource, _ r2: Resource) -> Image? func dewarpAndCleanup(_ image: Image) -> Image? func processImageData() -> Image? { loadWebResource("dataprofile.txt").flatMap { dataRes in loadWebResource("imagedata.dat").flatMap { imageRes in decodeImage(dataRes, imageRes).flatMap { imageTmp in dewarpAndCleanup(imageTmp) } } } }

Slide 78

Slide 78 text

Composition func loadWebResource(_ path: String) -> Resource? func decodeImage(_ r1: Resource, _ r2: Resource) -> Image? func dewarpAndCleanup(_ image: Image) -> Image? func processImageData() -> Image? { loadWebResource("dataprofile.txt").flatMap { dataRes in loadWebResource("imagedata.dat").flatMap { imageRes in decodeImage(dataRes, imageRes).flatMap { imageTmp in dewarpAndCleanup(imageTmp) } } } }

Slide 79

Slide 79 text

Composition func loadWebResource(_ path: String) -> Resource? func decodeImage(_ r1: Resource, _ r2: Resource) -> Image? func dewarpAndCleanup(_ image: Image) -> Image? func processImageData() -> Image? { if let dataRes = loadWebResource("dataprofile.txt"), let imageRes = loadWebResource("imagedata.dat"), let imageTmp = decodeImage(dataRes, imageRes) { return dewarpAndCleanup(imageTmp) } else { return nil } }

Slide 80

Slide 80 text

Composition func loadWebResource(_ path: String) -> Result func decodeImage(_ r1: Resource, _ r2: Resource) -> Result func dewarpAndCleanup(_ image: Image) -> Result func processImageData() -> Result { loadWebResource("dataprofile.txt").flatMap { dataRes in loadWebResource("imagedata.dat").flatMap { imageRes in decodeImage(dataRes, imageRes).flatMap { imageTmp in dewarpAndCleanup(imageTmp) } } } }

Slide 81

Slide 81 text

Composition func loadWebResource(_ path: String) -> AnyPublisher func decodeImage(_ r1: Resource, _ r2: Resource) -> AnyPublisher func dewarpAndCleanup(_ image: Image) -> AnyPublisher func processImageData() -> AnyPublisher { loadWebResource("dataprofile.txt").flatMap { dataRes in loadWebResource("imagedata.dat").flatMap { imageRes in decodeImage(dataRes, imageRes).flatMap { imageTmp in dewarpAndCleanup(imageTmp) } } } }

Slide 82

Slide 82 text

Monad Comprehensions Composition meets imperative syntax

Slide 83

Slide 83 text

Composition func loadWebResource(_ path: String) async throws -> Resource func decodeImage(_ r1: Resource, _ r2: Resource) async throws -> Image func dewarpAndCleanup(_ image: Image) async throws -> Image func processImageData() async throws -> Image { let dataRes = await try loadWebResource("dataprofile.txt") let imageRes = await try loadWebResource("imagedata.dat") let imageTmp = await try decodeImage(dataRes, imageRes) return await try dewarpAndCleanup(imageTmp) }

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

Composition func processImageData() -> IO { let dataRes = IO.var() let imageRes = IO.var() let imageTmp = IO.var() let image = IO.var() return binding { dataRes <- loadWebResource("dataProfile.txt") imageRes <- loadWebResource("imagedata.dat") imageTmp <- decode(dataRes.get, imageRes.get) image <- dewarpAndCleanup(imageTmp.get) } yield: { image.get }^ } bow-swift.io

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

awesome Swift is for Functional Programming

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

Why choose? Take what’s best from both

Slide 90

Slide 90 text

Demystifying Tomás Ruiz-López @tomasruizlopez Functional Programming Swifters December 2020