55
How to use Functional Reactive Programming without Black Magic

Functional Reactive Programming without Black Magic (UIKonf 2015)

Embed Size (px)

Citation preview

Page 1: Functional Reactive Programming without Black Magic (UIKonf 2015)

How to use Functional Reactive Programming without Black Magic

Page 2: Functional Reactive Programming without Black Magic (UIKonf 2015)

let me = Person(name: "Jens Ravens")

@JensRavens GitHub: JensRavens nerdgeschoss.de

swift.berlin

Page 3: Functional Reactive Programming without Black Magic (UIKonf 2015)

A short introduction to functional programming, the universe and everything.

Page 4: Functional Reactive Programming without Black Magic (UIKonf 2015)

In the beginning McIlroy created the unix pipe. And he

saw it was good.

ls | grep *.jpg | sort

Page 5: Functional Reactive Programming without Black Magic (UIKonf 2015)

Application Architecture

Page 6: Functional Reactive Programming without Black Magic (UIKonf 2015)
Page 7: Functional Reactive Programming without Black Magic (UIKonf 2015)

starting from a blank slate

Page 8: Functional Reactive Programming without Black Magic (UIKonf 2015)

implement to understand

Page 9: Functional Reactive Programming without Black Magic (UIKonf 2015)

And I promise not to use the scary M-word.

me, 12 weeks ago.

Page 10: Functional Reactive Programming without Black Magic (UIKonf 2015)

How to train your monad.

Page 11: Functional Reactive Programming without Black Magic (UIKonf 2015)

–Saunders Mac Lane

All told, a monad is just a monoid in the category of endofunctors.“

Page 12: Functional Reactive Programming without Black Magic (UIKonf 2015)

Monads are just Applicative Functors“

Page 13: Functional Reactive Programming without Black Magic (UIKonf 2015)

ls | grep *.jpg | sort

Page 14: Functional Reactive Programming without Black Magic (UIKonf 2015)

Buy it, use it, break it, fix it,

Trash it, change it, mail - upgrade it.

– Daft Punk, Technologic

Page 15: Functional Reactive Programming without Black Magic (UIKonf 2015)

Buy it; if error { //TODO: Handle me! } else { use it; if error { //TODO: Handle me! } else { break it; if error { //TODO: Handle me!

Page 16: Functional Reactive Programming without Black Magic (UIKonf 2015)

ls | grep *.jpg | sort

Page 17: Functional Reactive Programming without Black Magic (UIKonf 2015)

Monadsomething that defines map and bind

Page 18: Functional Reactive Programming without Black Magic (UIKonf 2015)

The optional Monadlet string: String? = "World" let greeting = string.map{"Hello \($0)”} //Hello World

extension Optional { func bind<U> (f: T->U?) -> U? { if let result = self.map({f($0)}) { return result } else { return nil } } }

Page 19: Functional Reactive Programming without Black Magic (UIKonf 2015)

The optional Monadlet string: String? = "World" let greeting = string.map{"Hello \($0)”} //Hello World

func greetOrNil(name: String)->String? { if name == "World" { return "Hello World" } else { return nil } }

let greeting2 = string.bind(greetOrNil) //Hello World

Page 20: Functional Reactive Programming without Black Magic (UIKonf 2015)

The optional Monadextension Optional { func bind<U> (f: T->U?) -> U? { if let result = self.map({f($0)}) { return result } else { return nil } } }

extension Optional { func bind<U> (f: T->Optional<U>) -> Optional<U> { switch self { case let .Some(value): return f(value) case .None: return nil } } }

Page 21: Functional Reactive Programming without Black Magic (UIKonf 2015)

The result Monad

public enum Result<T> { case Success(Box<T>) case Error(NSError) }

Page 22: Functional Reactive Programming without Black Magic (UIKonf 2015)

The result Monadpublic enum Result<T> { …

public func map<U>(f: T -> U) -> Result<U> { switch self { case let .Success(v):

return .Success(Box(f(v.value))) case let .Error(error): return .Error(error) } } …

}

Page 23: Functional Reactive Programming without Black Magic (UIKonf 2015)

The result Monadpublic enum Result<T> { …

public func bind<U>(f: T -> Result<U>) -> Result<U> { switch self { case let .Success(v): return f(v.value) case let .Error(error): return .Error(error) } } …

}

Page 24: Functional Reactive Programming without Black Magic (UIKonf 2015)

ls | grep *.jpg | sort

Monad

Transform

Page 25: Functional Reactive Programming without Black Magic (UIKonf 2015)

func parseString(data: NSData) -> Result<String>

func parseJson(data: NSData) -> Result<[String: AnyObject]>

func asyncGreeting(name: String, completion: Result<String>->Void)

Page 26: Functional Reactive Programming without Black Magic (UIKonf 2015)

public func bind<U>(f:(T, (Result<U>->Void))->Void) -> (Result<U>->Void)->Void { return { g in switch self { case let .Success(v): f(v.value, g) case let .Error(error): g(.Error(error)) } } }

Page 27: Functional Reactive Programming without Black Magic (UIKonf 2015)

Interstellar

Page 28: Functional Reactive Programming without Black Magic (UIKonf 2015)

ls | grep *.jpg | sort

Page 29: Functional Reactive Programming without Black Magic (UIKonf 2015)

public final class Signal<T> { private var value: Result<T>? private var callbacks: [Result<T> -> Void] = [] public func subscribe(f: Result<T> -> Void) { if let value = value { f(value) } callbacks.append(f) }

public func update(value: Result<T>) { self.value = value self.callbacks.map{$0(value)} }

}

Page 30: Functional Reactive Programming without Black Magic (UIKonf 2015)

public func map<U>(f: T -> U) -> Signal<U> { let signal = Signal<U>() subscribe { result in signal.update(result.map(f)) } return signal } public func bind<U>(f: T -> Result<U>) -> Signal<U> { let signal = Signal<U>() subscribe { result in signal.update(result.bind(f)) } return signal }

Page 31: Functional Reactive Programming without Black Magic (UIKonf 2015)

public func bind<U>(f: (T, (Result<U>->Void))->Void) -> Signal<U> { let signal = Signal<U>() subscribe { value in value.bind(f)(signal.update) } return signal }

Page 32: Functional Reactive Programming without Black Magic (UIKonf 2015)

pushing instead of pulling

Page 33: Functional Reactive Programming without Black Magic (UIKonf 2015)

the rise of custom operators

Page 34: Functional Reactive Programming without Black Magic (UIKonf 2015)

infix operator >>> { associativity left precedence 160 }

public func >>> <A,B> (left: Signal<A>, right: A->Result<B>) -> Signal<B> { return left.bind(right) }

public func >>> <A,B>(left: Signal<A>, right: (A, (Result<B>->Void))->Void) -> Signal<B>{ return left.bind(right) }

public func >>> <A,B> (left: Signal<A>, right: A->B) -> Signal<B> { return left.map(right) }

Page 35: Functional Reactive Programming without Black Magic (UIKonf 2015)

ls | grep *.jpg | sort

Page 36: Functional Reactive Programming without Black Magic (UIKonf 2015)

ls | grep *.jpg | sort

ls >>> grep("*.jpg") >>> sort

Page 37: Functional Reactive Programming without Black Magic (UIKonf 2015)

But what about Threads?

Page 38: Functional Reactive Programming without Black Magic (UIKonf 2015)

public final class Thread { public static func main<T>(a: T, completion: T->Void) { dispatch_async(dispatch_get_main_queue()) { completion(a) } } public static func background<T>(queue: dispatch_queue_t)(_ a: T, _ completion: T->Void) { dispatch_async(queue){ completion(a) } } }

ls >>> Thread.background(queue) >>> grep("*.jpg") >>> sort >>> Thread.main

Page 39: Functional Reactive Programming without Black Magic (UIKonf 2015)

Extending UIKit to support Signals.

Page 40: Functional Reactive Programming without Black Magic (UIKonf 2015)

var SignalHandle: UInt8 = 0 extension UITextField { public var textSignal: Signal<String> { let signal: Signal<String> if let handle = objc_getAssociatedObject(self, &SignalHandle) as? Signal<String> { signal = handle } else { signal = Signal("") NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("textChanged:"), name: UITextFieldTextDidChangeNotification, object: self) objc_setAssociatedObject(self, &SignalHandle, signal, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) } return signal } public func textChanged(notification: NSNotification) { textSignal.update(.Success(Box(self.text))) } }

Page 41: Functional Reactive Programming without Black Magic (UIKonf 2015)

If it’s variable, it qualifies as a Signal.

Page 42: Functional Reactive Programming without Black Magic (UIKonf 2015)

ReactiveKittenIt’s about gifs. And cats.

And gifs of cats.

Page 43: Functional Reactive Programming without Black Magic (UIKonf 2015)

userTyping >>> createURL >>> loadRequest >>> parseData >>> mainThread >>>

displayCats

The transform, the cat and you.

Page 44: Functional Reactive Programming without Black Magic (UIKonf 2015)

import Interstellar

private func request(path: String, completion: Result<NSData>->Void) { let url = NSURL(string: baseURL.stringByAppendingString(path))! let request = NSURLRequest(URL: url) session.dataTaskWithRequest(request){ data, response, error in if error != nil { completion(.Error(error)) } else if let response = response as? NSHTTPURLResponse { if response.statusCode >= 200 && response.statusCode<300 { completion(.Success(Box(data))) } else { completion(.Error(NSError(domain: "Networking", code: response.statusCode, userInfo: nil))) } } else { completion(.Error(NSError(domain: "Networking", code: 500, userInfo: nil))) } }.resume() }

Page 45: Functional Reactive Programming without Black Magic (UIKonf 2015)

private func parseJSON(data: NSData) ->Result<[String: AnyObject]> { var error: NSError? if let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? [String: AnyObject] { return .Success(Box(json)) } else { return .Error(error!) } }

Page 46: Functional Reactive Programming without Black Magic (UIKonf 2015)

let imageSignal = gifSignal >>> getURL >>> Thread.background >>> loadFromCache >>> retryFromNetwork >>> Thread.main

Page 47: Functional Reactive Programming without Black Magic (UIKonf 2015)

class ViewController: UIViewController { var signal: Signal<[Gif]>! let searchBar = UISearchBar() override func viewDidLoad() { super.viewDidLoad()

navigationItem.titleView = searchBar signal = searchBar.textSignal >>> Network().search() >>> Thread.main

Page 48: Functional Reactive Programming without Black Magic (UIKonf 2015)

github

.com

/jens

rave

ns/re

activek

itten

Page 49: Functional Reactive Programming without Black Magic (UIKonf 2015)

What’s next()?

Page 50: Functional Reactive Programming without Black Magic (UIKonf 2015)

Functors, Applicatives and Monads in Pictures.

http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

Page 51: Functional Reactive Programming without Black Magic (UIKonf 2015)

The Introduction to RP you’ve been missing.

https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

Page 52: Functional Reactive Programming without Black Magic (UIKonf 2015)

ReactiveCocoa 3

Page 53: Functional Reactive Programming without Black Magic (UIKonf 2015)

RxSwift

Page 54: Functional Reactive Programming without Black Magic (UIKonf 2015)

Interstellar

available on Carthage jensravens/interstellar

Page 55: Functional Reactive Programming without Black Magic (UIKonf 2015)

Thank you.

@JensRavensgithub.com/jensravens/interstellar