82
Introduction to Functional Reactive Programming @EliSawic

Introduction To Functional Reactive Programming Poznan

Embed Size (px)

Citation preview

Page 1: Introduction To Functional Reactive Programming Poznan

Introduction to Functional Reactive

Programming

@EliSawic

Page 2: Introduction To Functional Reactive Programming Poznan

About me

Eliasz SawickiBlog: www.eliaszsawicki.comTwitter: @EliSawic

@EliSawic

Page 3: Introduction To Functional Reactive Programming Poznan

Agenda• What is functional reactive programming?

• Working with streams

• ReactiveCocoa - Thinking in signals

• Example

@EliSawic

Page 4: Introduction To Functional Reactive Programming Poznan

Functional Reactive Programming

@EliSawic

Page 5: Introduction To Functional Reactive Programming Poznan

WikipediaFunctional reactive programming (FRP) is a programming paradigm for reactive programming (asynchronous dataflow programming) using the building blocks of functional programming (e.g. map, reduce, filter).

@EliSawic

Page 6: Introduction To Functional Reactive Programming Poznan

Reactive Programming

@EliSawic

Page 7: Introduction To Functional Reactive Programming Poznan

Asynchronous Dataflow

@EliSawic

Page 8: Introduction To Functional Reactive Programming Poznan

Reacting to state changes

@EliSawic

Page 9: Introduction To Functional Reactive Programming Poznan

Functional Programming

@EliSawic

Page 10: Introduction To Functional Reactive Programming Poznan

Immutable

@EliSawic

Page 11: Introduction To Functional Reactive Programming Poznan

assert(f(x) == f(x))

@EliSawic

Page 12: Introduction To Functional Reactive Programming Poznan

A personclass Person { let name: String let phoneNumber: String init(name: String, phoneNumber: String) { self.name = name self.phoneNumber = phoneNumber }}

class MobilePhone { func call(person: Person) -> Bool { // implementation }}

@EliSawic

Page 13: Introduction To Functional Reactive Programming Poznan

Mutablelet mobilePhone = MobilePhone()let john = Person(name: "John", phoneNumber: "123456789")

func makeAPhoneCall(device: Phone, person: Person, countryCode: String) -> Bool { person.phoneNumber = countryCode + person.phoneNumber let success = device.call(person) return success}

makeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // truemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // falsemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // false

@EliSawic

Page 14: Introduction To Functional Reactive Programming Poznan

Immutablelet mobilePhone = MobilePhone()let john = Person(name: "John", phoneNumber: "123456789")

func makeAPhoneCall(device: Phone, person: Person, countryCode: String) -> Bool { let prefixedPhoneNumber = countryCode + person.phoneNumber let newPerson = Person(name: person.name, phoneNumber: prefixedPhoneNumber) let success = device.call(newPerson) return success}

makeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // truemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // truemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // true

@EliSawic

Page 15: Introduction To Functional Reactive Programming Poznan

Stateless

@EliSawic

Page 16: Introduction To Functional Reactive Programming Poznan

Statefulvar value = 0

func increment() -> Int { value += 1 return value}

@EliSawic

Page 17: Introduction To Functional Reactive Programming Poznan

Statelessfunc increment(value: Int) -> Int { return value + 1}

@EliSawic

Page 18: Introduction To Functional Reactive Programming Poznan

Imperative vs

Declarative

@EliSawic

Page 19: Introduction To Functional Reactive Programming Poznan

Imperative

@EliSawic

Page 20: Introduction To Functional Reactive Programming Poznan

Imperativelet array = [0, 1, 2, 3, 4, 5]var evenNumbers = [Int]()for element in array { if element % 2 == 0 { evenNumbers.append(element) }}

@EliSawic

Page 21: Introduction To Functional Reactive Programming Poznan

Declarative

@EliSawic

Page 22: Introduction To Functional Reactive Programming Poznan

Declarativelet array = [0, 1, 2, 3, 4, 5]let evenNumbers = array.filter { $0 % 2 == 0 }

@EliSawic

Page 23: Introduction To Functional Reactive Programming Poznan

Working with streams

@EliSawic

Page 24: Introduction To Functional Reactive Programming Poznan

Stream

@EliSawic

Page 25: Introduction To Functional Reactive Programming Poznan

Manipulating streams

@EliSawic

Page 26: Introduction To Functional Reactive Programming Poznan

Map

@EliSawic

Page 27: Introduction To Functional Reactive Programming Poznan

Filter

@EliSawic

Page 28: Introduction To Functional Reactive Programming Poznan

Aggregating

@EliSawic

Page 29: Introduction To Functional Reactive Programming Poznan

Skip repeats

@EliSawic

Page 30: Introduction To Functional Reactive Programming Poznan

Manipulating multiple streams

@EliSawic

Page 31: Introduction To Functional Reactive Programming Poznan

Combine latest

@EliSawic

Page 32: Introduction To Functional Reactive Programming Poznan

Zip

@EliSawic

Page 33: Introduction To Functional Reactive Programming Poznan

Merge

@EliSawic

Page 34: Introduction To Functional Reactive Programming Poznan

Chaining streams

@EliSawic

Page 35: Introduction To Functional Reactive Programming Poznan

www.rxmarbles.com

@EliSawic

Page 36: Introduction To Functional Reactive Programming Poznan

ReactiveCocoa

@EliSawic

Page 37: Introduction To Functional Reactive Programming Poznan

Thinking in Signals

@EliSawic

Page 38: Introduction To Functional Reactive Programming Poznan

What is a signal?

@EliSawic

Page 39: Introduction To Functional Reactive Programming Poznan

This presentation is a signal

@EliSawic

Page 40: Introduction To Functional Reactive Programming Poznan

Represents events over time

@EliSawic

Page 41: Introduction To Functional Reactive Programming Poznan

No random access to events

@EliSawic

Page 42: Introduction To Functional Reactive Programming Poznan

Observe and react

@EliSawic

Page 43: Introduction To Functional Reactive Programming Poznan

If you don't listen, it's gone

@EliSawic

Page 44: Introduction To Functional Reactive Programming Poznan

Observing does not trigger side effects

@EliSawic

Page 45: Introduction To Functional Reactive Programming Poznan

What is event?

@EliSawic

Page 46: Introduction To Functional Reactive Programming Poznan

Eventenum Quality { case Great case Average case Worst}

struct Idea {

var content: String var quality: Quality

}

@EliSawic

Page 47: Introduction To Functional Reactive Programming Poznan

Non-Terminating• Next

@EliSawic

Page 48: Introduction To Functional Reactive Programming Poznan

Terminating

• Completed

• Failed

• Interrupted (Reactive Cocoa)

@EliSawic

Page 49: Introduction To Functional Reactive Programming Poznan

Presentationlet (presentation, presentationObserver) = Signal<Idea, NoError>.pipe()

let content = "This presentation is a signal"let idea = Idea(content: content, quality: .Great)

presentationObserver.send(value: idea)

@EliSawic

Page 50: Introduction To Functional Reactive Programming Poznan

Observingpresentation.observeValues { idea in remember(idea: idea)}

presentation.observeCompleted { print("Finally...")}

presentationObserver.send(value: idea)presentationObserver.sendCompleted()

@EliSawic

Page 51: Introduction To Functional Reactive Programming Poznan

Only great ideaslet greatIdeas = presentation.filter { $0.quality == .Great }greatIdeas.observeValues { (greatIdea) in remember(idea: greatIdea)}

presentationObserver.send(value: idea)presentationObserver.sendCompleted()

@EliSawic

Page 52: Introduction To Functional Reactive Programming Poznan

Positive listenerlet greatPresentation = presentation.map { idea -> Idea in var greatIdea = idea greatIdea.quality = .Great return greatIdea}

@EliSawic

Page 53: Introduction To Functional Reactive Programming Poznan

Count worst ideaslet worstIdeas = greatPresentation.filter { $0.quality == .Worst }

let numberOfWorstIdeas = worstIdeas.reduce(0) { (sum, idea) -> Int in return sum + 1}

numberOfWorstIdeas.observeValues { (numberOfWorstIdeas) in print("Number of worst ideas: \(numberOfWorstIdeas)")}

presentationObserver.send(value: idea)presentationObserver.sendCompleted()

@EliSawic

Page 54: Introduction To Functional Reactive Programming Poznan

Signal's lifetime• Passes any number of Next events

• "Dies" when terminating event

• Any new observer will receive Interrupted event

@EliSawic

Page 55: Introduction To Functional Reactive Programming Poznan

Signal producer

@EliSawic

Page 56: Introduction To Functional Reactive Programming Poznan

Represents a tasks

@EliSawic

Page 57: Introduction To Functional Reactive Programming Poznan

Creates a signal

@EliSawic

Page 58: Introduction To Functional Reactive Programming Poznan

Possible side effects

@EliSawic

Page 59: Introduction To Functional Reactive Programming Poznan

Does not start it's work if not asked

@EliSawic

Page 60: Introduction To Functional Reactive Programming Poznan

Run presentationfunc runPresentation() -> SignalProducer<Idea, NoError> { return SignalProducer { observer, _ in observer.send(value: idea1) observer.send(value: idea2) ... observer.sendCompleted() }}

@EliSawic

Page 61: Introduction To Functional Reactive Programming Poznan

Work with presentationrunPresentation().startWithSignal { (signal, _) in signal.observeValues({ idea in print(idea) })

signal.observeCompleted { print("Finally...") }}

@EliSawic

Page 62: Introduction To Functional Reactive Programming Poznan

Cold vs Hot

@EliSawic

Page 63: Introduction To Functional Reactive Programming Poznan

Properties

@EliSawic

Page 64: Introduction To Functional Reactive Programming Poznan

Mutable Propertylet firstSlide = Slide(number: 1)let slide = MutableProperty<Slide>(firstSlide)

slide.producer.startWithNext { (text) in print(text)}

slide.value = Slide(number: 2)

@EliSawic

Page 65: Introduction To Functional Reactive Programming Poznan

Bindings

@EliSawic

Page 66: Introduction To Functional Reactive Programming Poznan

Binding examplelet slideNumber = MutableProperty<Int>(0)let (signal, _) = Signal<Slide, NoError>.pipe()slideNumber <~ signal.map { return $0.number }

label.reactive.text <~ signal.map { return "Slide number \($0.number)" }

@EliSawic

Page 67: Introduction To Functional Reactive Programming Poznan

Schedulers

@EliSawic

Page 68: Introduction To Functional Reactive Programming Poznan

Know where you aresignal.observe(on: QueueScheduler.main).observeValues { idea in print("Performing UI updates")}

producer.start(on: backgroundQueue).startWithValues { values in print("Starting task")}

@EliSawic

Page 69: Introduction To Functional Reactive Programming Poznan

Memory Management

@EliSawic

Page 70: Introduction To Functional Reactive Programming Poznan

Disposables

@EliSawic

Page 71: Introduction To Functional Reactive Programming Poznan

Free your memorylet disposablesBag = CompositeDisposable()disposablesBag += signal.observeValues { value in ...}

disposablesBag += producePresentation().startWithValues { (value) in ...}

disposablesBag.dispose()

@EliSawic

Page 72: Introduction To Functional Reactive Programming Poznan

Example

@EliSawic

Page 73: Introduction To Functional Reactive Programming Poznan

@EliSawic

Page 74: Introduction To Functional Reactive Programming Poznan

How does it work?

@EliSawic

Page 75: Introduction To Functional Reactive Programming Poznan

Is name valid?let isValidName = nameSignal.map { (name) -> Bool in return input.characters.count > 2}

@EliSawic

Page 76: Introduction To Functional Reactive Programming Poznan

Is surname valid?let isValidSurname = nameSignal.map { (name) -> Bool in return input.characters.count > 2}

@EliSawic

Page 77: Introduction To Functional Reactive Programming Poznan

Is mail valid?let isValidMail = mailSignal.map { (mail) -> Bool in let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx) return emailTest.evaluateWithObject(mail)}

@EliSawic

Page 78: Introduction To Functional Reactive Programming Poznan

Combine Latestlet formData = combineLatest(isValidName, isValidSurname, isValidMail)

@EliSawic

Page 79: Introduction To Functional Reactive Programming Poznan

Is form valid?let isValidForm = MutableProperty<Bool>(false)

isValidForm <~ formData.map { (isValidName, isValidSurname, isValidMail) -> Bool in return isValidMail && isValidSurname && isValidMail}

@EliSawic

Page 80: Introduction To Functional Reactive Programming Poznan

Button statelet producer = isValidForm.producer.skipRepeats()

producer.startWithNext { isValid in updateButtonWith(state: isValid)}

@EliSawic

Page 81: Introduction To Functional Reactive Programming Poznan

Conclusion

@EliSawic

Page 82: Introduction To Functional Reactive Programming Poznan

Thank you for your attention!sendCompleted()

@EliSawic