functional and reactive u is gwt.create 2015
TRANSCRIPT
Functional and Reactive UIs
Henri Muurimaa SVP of Engineering, Vaadin
[email protected] @henrimuurimaa
Recursion
Lazy evaluation
Lambda expressionsType theory
MonadsReferential transparency
Currying
Entscheidungsproblem
Pattern matching
Tuples
val square = (number: Int) => number * number
Functional programming 101
val tenSquared = square(10)
val nums = List(1,2,3) val squares = nums map square // List(1, 4, 9)
val evenNums = nums filter { _ % 2 == 0 } // List(2)
val letters = List("a", "b", "c") val numbersWithLetters = squares zip letters // List((1,a), (4,b), (9,c))
val presentNumber: Option[Int] = Some(10)
Dealing with absent values on the type level
val absentNumber: Option[Int] = None
val wrappedNull: Option[Int] = Option(null) // None
Working with Option
case class User(name: String, id: Int)
trait UserRepository { def findById(i: Int): Option[User] }
val user = userRepository.findById(1) // Option[User]
if(user.isDefined) { println("User name: " + user.get.name) }
user foreach { u => println("User name: " + u.name) }
val nameOpt = user map { _.name } // Option[String]
val name = user map { _.name } getOrElse "No user found"
case class Cell(x: Int, y: Int) { def +(other: Cell) = Cell(x + other.x, y + other.y) }
class Dungeon { var floors: Set[Cell] = Set() var entities: Map[Entity, Cell] = Map() def visibleIlluminatedCells = ??? }
Dungeon model
class Dungeon { var floors: Set[Cell] = Set() var entities: Map[Entity, Cell] = Map()
def playerOpt = entities.keys collect { case p: Player => p } headOption def playerCellOpt = playerOpt map entities def playerSightRange = playerOpt map { _.sightRange } getOrElse 0
def inPlayerSightRange(cell: Cell) = playerCellOpt map { playerPos => MapLogic.distance(playerPos, cell) <= playerSightRange } getOrElse false
def visibleIlluminatedCells = { val lightSources = entities map { case (entity, cell) => (cell, entity.illuminationRadius) }
val visibles = lightSources flatMap MapLogic.area filter inPlayerSightRange
floors intersect visibles.toSet } }
Functional summary
Option lets you get rid of NPEs forever
Use small functions with clear responsibilities
Chaining functional operations is practically a superpower
Vaadin
ComboBox cities = new ComboBox(); Label selectedCity = new Label(); selectedCity.setPropertyDataSource(cities);
Tessell
void onInit() { employee.name.set(name); binder.bind(employee.name).to(textOf(view.employeeName())); } void someBusinessLogic() { // only have to set the name employee.name.set(newName); }
val numbers = Observable.from(List(1, 2, 3)) numbers subscribe { n => println(n) }
Observables to the rescue
val squares = numbers map { num => num * num }
val letters = Observable.from(List("a", "b", "c")) val numbersWithLetters = squares zip letters
val movesObserver = Vector( up.clickEvents map { e => tryMove(Cell(0, -1)) }, down.clickEvents map { e => tryMove(Cell(0, 1)) }, left.clickEvents map { e => tryMove(Cell(-1, 0)) }, right.clickEvents map { e => tryMove(Cell(1, 0)) } )
def tryMove(delta: Cell) = board.dungeon.playerPosition map { cell => cell + delta } filter board.dungeon.canMoveTo
Observing player moves
// Emit a Option[Cell] every time player tries to move val moveObserver = Observable from movesObserver flatten
// Map a legal destination cell to the set of visible cells after the move val visibleCellsObserver = moveObserver collect { case Some(cell) => board.dungeon.playerOpt foreach { board.dungeon.put(_, cell) } board.dungeon.visibleIlluminatedCells }
// Subscribe the game board instance to the stream of legal moves. // This will call board.onNext() with a set of visible cells whenever // player performs a legal move. visibleCellsObserver subscribe board
Handling legal moves
// Subscribe to the illegal move stream to show a notification every // time player tries an illegal move. moveObserver filter { _.isEmpty } subscribe { none => Notification.show("That direction is blocked", Notification.Type.Tray) }
Handling illegal moves
Summary
These techniques are very powerful and they can be learned gradually
Both functional and reactive techniques are great with UIs
Functional puts FUN back in programming!