introduction to functional reactive programming

Post on 09-Jan-2017

75 Views

Category:

Mobile

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

About me

Eliasz SawickiBlog: www.eliaszsawicki.comTwitter: @EliSawic

@EliSawic

Introduction to Functional Reactive

Programming

@EliSawic

Agenda• What is Functional Reactive Programming?

• Let's take a look at ReactiveCocoa

• Working with streams

• Real life example

• Conclusion

@EliSawic

Functional Reactive Programming

@EliSawic

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

Reactive Programming

@EliSawic

Asynchronous Dataflow

@EliSawic

Reacting to state changes

@EliSawic

Functional Programming

@EliSawic

Immutable

@EliSawic

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

@EliSawic

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

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

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

Stateless

@EliSawic

Statefulvar value = 0

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

@EliSawic

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

@EliSawic

Functional Reactive Programming

@EliSawic

Imperative vs

Declarative

@EliSawic

Imperative

@EliSawic

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

@EliSawic

Declarative

@EliSawic

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

@EliSawic

ReactiveCocoa

@EliSawic

Event streams

@EliSawic

Event Stream

@EliSawic

Event

@EliSawic

Non-Terminating• Next

@EliSawic

Terminating

• Completed

• Failed

• Interrupted (Reactive Cocoa)

@EliSawic

Signal

@EliSawic

Events over time

@EliSawic

No side effects

@EliSawic

No random access to events

@EliSawic

Must be observed in order to access it's

events

@EliSawic

Hot

@EliSawic

Signal's lifetime• Passes any number of Next events

• "Dies" when terminating event

• Any new observer will receive Interrupted event

@EliSawic

Observinglet someWork = Signal<Int, NoError> { observer in observer.sendNext(1) observer.sendNext(2) .... observer.sendNext(1000000) observer.sendCompleted()}

signal.observe { (event) in print(event)}signal.observeNext { (value) in print(value)}

signal.observeCompleted { print("Completed")}

@EliSawic

Pipe

@EliSawic

Pipelet (signal, observer) = Signal<String, NoError>.pipe()

signal.observeNext({ text in print(text)})

signal.observeCompleted({ print("Test completed")})

observer.sendNext("It's a test") // It's a testobserver.sendCompleted() // Test completed

@EliSawic

SignalProducer

@EliSawic

Represents tasks

@EliSawic

Creates signal

@EliSawic

Possible side effects

@EliSawic

Does not start it's work if not started

@EliSawic

Cold

@EliSawic

Using Signal Producerlet producer = SignalProducer<String, NSError> { (observer, composite) in observer.sendNext("In Progress...") // ...... observer.sendCompleted()}

producer.startWithSignal { (signal, _) in signal.observeNext({ (text) in print(text) // In Progress... }) signal.observeCompleted({ print("Test completed") // Test completed })}

@EliSawic

Cold vs Hot

@EliSawic

Properties

@EliSawic

MutablePropertylet name = MutableProperty("Bob")

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

name.value = "Lisa"

@EliSawic

Bindings

@EliSawic

Basic bindinglet property = MutableProperty<String>("")let (signal, _) = Signal<String, NoError>.pipe()property <~ signal

@EliSawic

Schedulers

@EliSawic

Memory Management

@EliSawic

Disposables

@EliSawic

Manipulating signals

@EliSawic

Map

@EliSawic

Filter

@EliSawic

Aggregating

@EliSawic

Skip repeats

@EliSawic

Manipulating multiple signals

@EliSawic

Combine latest

@EliSawic

Zip

@EliSawic

Merge

@EliSawic

Chaining operators

@EliSawic

Chain them all!let newSignalX = signalX.skipRepeats() .filter { x > 2 } .map { x * 10 }

let newSignalY = signalY.filter { x > 10 }

let combined = combineLatest(newSignalX, newSignalY)

combined.observeNext { xValue, yValue in print("Update: \(xValue) : \(yValue)")}

@EliSawic

Example

@EliSawic

@EliSawic

@EliSawic

@EliSawic

@EliSawic

@EliSawic

How does it work?

@EliSawic

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

@EliSawic

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

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

@EliSawic

Is form valid?disposables += isFormValid <~ formData.map { (isValidName, isValidSurname, isValidMail) -> Bool in return isValidMail && isValidSurname && isValidMail}

@EliSawic

Button statelet producer = isFormValid.producer.skipRepeats()disposables += producer.startWithNext {[unowned self] (isValid) in self.updateAcceptButtonWithState(isValid)}

@EliSawic

Conclusion

@EliSawic

Thank you for your attention!

@EliSawic

top related