Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Taming Asynchronous Transforms with Interstellar

343 views

Published on

Who's afraid of the monad? Let's have a look at results, signals and why concurrency doesn't have to be evil.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Taming Asynchronous Transforms with Interstellar

  1. 1. Taming Asynchronous Transforms with Interstellar
  2. 2. let me = Person(name: "Jens Ravens", company: "nerdgeschoss") @JensRavens GitHub: JensRavens jensravens.com nerdgeschoss.de
  3. 3. A short introduction to functional programming, the universe and everything.
  4. 4. In the beginning McIlroy created the unix pipe. And he saw it was good. ls | grep *.jpg | sort
  5. 5. Optionals and Results
  6. 6. Optionals are a box containing something.
  7. 7. struct Cat { func pet() -> String { return "purrr" } } let boxContainingCat: Optional<Cat> = Cat() let sound: String? if let cat = boxContainingCat { sound = cat.pet() } else { sound = nil }
  8. 8. struct Cat { func pet() -> String { return "purrr" } } let boxContainingCat: Optional<Cat> = Cat() let sound = boxContainingCat?.pet()
  9. 9. struct Cat { func pet() -> String { return "purrr" } } let boxContainingCat: Optional<Cat> = Cat() let sound = boxContainingCat.map { cat in cat.pet() }
  10. 10. struct Cat { func pet() -> String? { return "purrr" } } let boxContainingCat: Optional<Cat> = Cat() let sound: String?? = boxContainingCat.map { cat in cat.pet() }
  11. 11. struct Cat { func pet() -> String? { return "purrr" } } let boxContainingCat: Optional<Cat> = Cat() let sound: String? = boxContainingCat.flatMap { cat in cat.pet() }
  12. 12. func double(i: Int) -> [Int] { return [i, 2*i] } [1, 2, 3].map(double) // [[1,2], [2,4], [3,6]] [1, 2, 3].flatMap(double) // [[1, 2, 2,4, 3,6]
  13. 13. Error Handling
  14. 14. Buy it, use it, break it, fix it, Trash it, change it, mail - upgrade it. – Daft Punk, Technologic
  15. 15. Buy it; if error { //TODO: Handle me! } else { use it; if error { //TODO: Handle me! } else { break it; if error { //TODO: Handle me!
  16. 16. enum Result<T> { case Success(T) case Error(ErrorType) func map<U>(f: T -> U) -> Result<U> func flatMap<U>(f: T -> Result<U>) -> Result<U> }
  17. 17. func ls()-> Result<[String]> func grep(pattern: String)(values: [String]) -> [String] func sort(values: [String]) -> [String] { return [] } let sorted = ls().map(grep("*.jpg")).map(sort) ls | grep *.jpg | sort
  18. 18. Interstellar
  19. 19. ls | grep *.jpg | sort
  20. 20. class Signal<T> { func subscribe(f: Result<T> -> Void) -> Signal<T> func next(g: T -> Void) -> Signal<T> func error(g: ErrorType -> Void) -> Signal<T> func update(result: Result<T>) func update(value: T) func update(error: ErrorType) } let signal = Signal<String>() signal.next { string in print(string) } signal.update("Hello World")
  21. 21. pushing instead of pulling
  22. 22. But what about Threads?
  23. 23. let threadSignal = Signal<String>() func uppercase(string: String) -> String { return string.uppercaseString } threadSignal .ensure(Thread.background) .map(uppercase) .ensure(Thread.main) .next { print($0) }
  24. 24. Extending UIKit to support Signals.
  25. 25. extension UITextField { public var textSignal: Signal<String> } let textField = UITextField() textField.textSignal.next { string in print(string) }
  26. 26. If it’s variable, it qualifies as a Signal.
  27. 27. real world code examples
  28. 28. func executeRequest(request: Request) -> Signal<HTTPResponse> func getURL(response: HTTPResponse) throws -> NSURL func upload(data: NSData)(url: NSURL, completion: Result<String>->Void) func createEntity(path: String, completion: Result<Conversation>->Void) func createConversation(avatar: UIImage) -> Signal<Conversation> { let signal = Signal<Conversation>() let data = UIImageJPEGRepresentation(avatar, 0.8)! api .executeRequest(.GetUploadURL) .flatMap(getURL) .flatMap(api.upload(data)) .flatMap(createEntity) .subscribe(signal.update) return signal }
  29. 29. func poll() -> Signal<[Conversation]> { let signal = Signal<[Conversation]>() let json = api .executeRequest(.GetConversations) .map { $0.json } let conversations = json .flatMap(Sync<Conversation>(context: context).updateObjects) let messages = json .flatMap(getMessages) .flatMap(Sync<Message>(context: context).updateObjects) conversations.merge(messages) .flatMap(context.saveAndPipe) .next { signal.update($0.0) } .error { signal.update($0) } signal.map(countUnread).next(setUnreadCount) return signal }
  30. 30. func poll() -> Signal<[Conversation]> { let signal = Signal<[Conversation]>() let json = api .executeRequest(.GetConversations) .map { $0.json } let conversations = json .flatMap(Sync<Conversation>(context: context).updateObjects) let messages = json .flatMap(getMessages) .flatMap(Sync<Message>(context: context).updateObjects) conversations.merge(messages) .flatMap(context.saveAndPipe) .next { signal.update($0.0) } .error { signal.update($0) } signal.map(countUnread).next(setUnreadCount) return signal } API Request sync conversations sync messages wait for both then save update unread count notify listeners
  31. 31. Warpdrive
  32. 32. • Thread.main / Thread.background • Signal.delay(seconds: NSTimeInterval) • Signal.wait throws • Signal.debounce(seconds: NSTimeInterval) Coming soon: Holodeck UIKit Bindings
  33. 33. Thank you. @JensRavens github.com/jensravens/interstellar

×