Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Reactive Processing

Gideon de Kok
October 02, 2013
130

Functional Reactive Processing

Primer into functional and actor based programming

Gideon de Kok

October 02, 2013
Tweet

Transcript

  1. 1970 1975 1980 1985 1990 1995 2000 2005 2010 2015

    Transistors Frequency Cores Wednesday 2 October 13
  2. The real-time Web constant exchange of data, both in large

    and small packages Wednesday 2 October 13
  3. Callback hell anyone? fs.readdir(source, function(err, files) { if (err) {

    console.log('Error finding files: ' + err) } else { files.forEach(function(filename, fileIndex) { console.log(filename) gm(source + filename).size(function(err, values) { if (err) { console.log('Error identifying file size: ' + err) } else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function(width, widthIndex) { height = Math.round(width / aspect) console.log('resizing ' + filename + 'to ' + height + 'x' + height) this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err) }) }.bind(this)) } }) }) } }) Wednesday 2 October 13
  4. a system capable of reacting to events – load –

    failure – users Wednesday 2 October 13
  5. Non determinism is the root of all evil in both

    concurrent as distributed environments Wednesday 2 October 13
  6. scala> val a = 7 a: Int = 7 scala>

    a = 9 <console>:8: error: reassignment to val Wednesday 2 October 13
  7. val res = async { a + 2 } +

    async { val d = a; d - 2 } Wednesday 2 October 13
  8. Multiple arguments val addAndDivide = (a: Int, b: Int) =>

    (b + a) / a addAndDivide(10,4) res0: Int = 3 Wednesday 2 October 13
  9. Curried val addAndDivide = (a: Int) => (b: Int) =>

    (b + a) / a addAndDivide(10)(4) res0: Int = 3 Wednesday 2 October 13
  10. More sane example val isInRangeF = (min: Int, max: Int)

    㱺 (x: Int) 㱺 x > min && x < max Wednesday 2 October 13
  11. Usage val newFunction = isInRangeF(20,30) newFunction: Int 㱺 Boolean =

    <function1> newFunction(22) res0: Boolean = true isInRangeF(40,45)(50) res1: Boolean = false Wednesday 2 October 13
  12. In the wild (Ruby) a = [ "a", "b", "c",

    "d" ] b = a.map {|x| x + "!" } b #=> [ "a!", "b!", "c!", "d!" ] Wednesday 2 October 13
  13. In the wild (Scala) List(List(1,2), List(2), List(4,5)).map(_.sum) res0: List(3, 2,

    9) List(2,3,4,5,6,7).foldLeft(0)(_+_) res1: Int = 27 List("Hello", "I", "want", "to", "be", "a", "sentence").reduceLeft(_ + " " + _) res2: String = Hello I want to be a sentence Wednesday 2 October 13
  14. Map val l = List(1,2,3,4,5,6) l.map(x => x + 2)

    res0: List[Int] = List(3,4,5,6,7,8) Wednesday 2 October 13
  15. val a: Option[Int] = Some(2) a.map(x => x + 4)

    res0: Some(6) Option Wednesday 2 October 13
  16. Null? def fetchUserFromDatabase(email: String): User val user = fetchUserFromDatabase("[email protected]") var

    firstName = "" if (user != null) { firstName = user.firstName } else { throw Exception("User doesn't exist") } Wednesday 2 October 13
  17. Future def fetchFromSite(url: String): Future[HttpResponse] val json: Future[JsObject] = fetchFromSite("http://google.com/

    search.json?q=cats").map(x => toJson(x.toString)) val value: JsObject = Await.result(json, 2 seconds) Wednesday 2 October 13
  18. But what if a function returns a context within a

    context.. Wednesday 2 October 13
  19. within a context within a context within a context within

    a context within a context? Wednesday 2 October 13
  20. Future: Monad val userFut: Future[User] = db.user.fetch("[email protected]") val orgFut: Future[Organisation]

    = userFut.flatMap(x 㱺 db.organisation.fetch(x.organisationId)) val org: Organisation = Await.result(orgFut, 1 second) Wednesday 2 October 13
  21. Option: Monad val email: Option[String] = request.headers.get(“email”) val domain: Option[String]

    = email.flatMap(x => x.split("@").tail.headOption) val domainLength: Option[Int] = domain.map(_.length) Wednesday 2 October 13
  22. Multiple Monads db.user.fetch("[email protected]").flatMap { u => db.organisation.fetch(u.organizationId).flatMap { o 㱺

    db.city.fetch(o.cityId) flatMap { c => db.country.fetch(c.countryId) } } } Wednesday 2 October 13
  23. Multiple Monads for { user <- db.user.fetch("[email protected]") org <- db.organisation.fetch(user.organizationId)

    city <- db.city.fetch(org.cityId) country <- db.coutry.fetch(city.countryId) } yield country Wednesday 2 October 13
  24. But what should we use to enable stateful programming in

    concurrent and distributed environments? Wednesday 2 October 13
  25. Cognitive Concurrency proof data structures are difficult to design Understanding

    in-code bottlenecks is painful Bugfixing and error hunting sometimes mere impossible Wednesday 2 October 13
  26. class User(var firstName: String, var lastName: String) { ! def

    setFirstName(name: String) = firstName = name ! def setLastName(name: String) = lastName = name ! def getFullName = firstName + " " + lastName } val user = User("John", "Doe") val a = Async { ! user.setFirstName("Johnny") ! user.setLastName("Walker") } val b = Async { ! user.getFullName } b == "Johnny Doe" Wednesday 2 October 13
  27. class User(var firstName: String, var lastName: String) { ! val

    lock = new ReentrantLock ! def setFirstName(name: String) = { ! ! lock.lock ! ! firstName = name ! ! lock.unlock ! } ! def setLastName(name: String) = { ! ! lock.lock ! ! lastName = name ! ! lock.unlock ! } ! def getFullName = { ! ! lock.lock ! ! firstName + " " + lastName ! ! lock.unlock ! } } // Thread 1 user.setFirstName("Johnny") // Thread 2 user.getFullName // Thread 1 user.setLastName("Walker") Wednesday 2 October 13
  28. What would it look like if we didn’t create the

    problem in the first place? Wednesday 2 October 13
  29. m oney coffee prepare order retrieve order em pty cup

    filled cup Wednesday 2 October 13
  30. m oney coffee prepare order retrieve order em pty cup

    filled cup Wednesday 2 October 13
  31. val printer = actor(new Act { ! become { !

    ! case x: String ⇒ println(x) ! } }) printer ! "Print this!" Wednesday 2 October 13
  32. val a = actor(new Act { ! become { !

    ! case "hello" ⇒ sender ! "hi" !} }) val result: Future[String] = (a ? "hello").mapTo[String] Wednesday 2 October 13
  33. object CoffeeShop { ! case class Money ! case class

    Coffee ! case class Cup(contents: Option[Coffee]) ! case class OrderCoffee(cashier: ActorRef) ! implicit val system = ActorSystem("coffee-shop") ! class Customer extends Actor { ! var money = Some(Money()) ! var cup = None ! def receive = { ! ! case OrderCoffee(cashier) => ! ! ! val m = money ! ! ! money = None ! ! ! cashier ! m ! ! case c: Cup => ! ! ! cup = c ! ! ! println("Whooohooo! Coffee :-)") ! } } } Wednesday 2 October 13
  34. class Cassier(barista: ActorRef) extends Actor { ! var orders =

    Map[String, ActorRef] ! def receive = { ! ! case Money => ! ! ! val customer = sender ! ! ! val newOrder = Order(java.util.UUID.randomUUID.toString) ! ! ! orders = orders ++ (newOrder.id -> customer) ! ! ! val cup = Cup(contents = None) ! ! ! barista ! (order, cup) ! ! case x: (Order, Cup) => val customer = orders(x._1.id) ! ! customer ! x._2 ! } } Wednesday 2 October 13
  35. class Barista extends Actor { ! def receive = {

    ! ! case x: (Order, Cup) => ! ! ! sender ! (x._1 -> prepareCoffee(x._2))) ! } ! def prepareCoffee(cup: Cup) = { ! ! thread.sleep(120) ! ! cup.copy(contents = Some(Coffee())) ! } } Wednesday 2 October 13
  36. val customer = system.actorOf(Props[Customer]) val barista = system.actorOf(Props[Barista]) val cassier

    = system.actorOf(Props(new Cassier(barista))) customer ! OrderCoffee(cassier) Wednesday 2 October 13
  37. “Actors do one thing at a time, they do it

    very well, and then they quickly move on.” Wednesday 2 October 13
  38. val baristas = system.actorOf(Props[Barista]).withRouter(RoundRobinRouter(nrOfInstances = 5))) val cassiers = system.actorOf(Props(new

    Cassier(barista))).withRouter(RoundRobinRouter(nrOfInstances = 3))) customer ! OrderCoffee(cassiers) customerTwo ! OrderCoffee(cassiers) customerThree ! OrderCoffee(cassiers) Wednesday 2 October 13
  39. class CoffeeShopOwner extends Actor { ! var baristas: List[Barista] !

    override val supervisorStrategy = ! OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { ! case _: BurnedHandException ⇒ Resume ! case _: PassedOut ! ! ⇒ Restart ! case _: Died ! ! ! ! ⇒ Stop ! case _: Exception ⇒ Escalate ! } } Wednesday 2 October 13
  40. akka { ! actor { ! ! deployment { !

    ! ! /path/to/service { ! ! ! ! router = "round-robin" ! ! ! ! nr-of-instances = 2 ! ! ! ! remote { ! ! ! ! ! nodes = ["node1:2552", "node2:2552"] ! ! ! ! } ! ! ! } ! ! } ! } } Wednesday 2 October 13