Functional and reactive u is gwt.create 2015

28
Functional and Reactive UIs Henri Muurimaa SVP of Engineering, Vaadin [email protected] @henrimuurimaa

Transcript of Functional and reactive u is gwt.create 2015

Functional and Reactive UIs

Henri Muurimaa SVP of Engineering, Vaadin

[email protected] @henrimuurimaa

Number of metal bands per capita

Functional programming

Reactive programming

Functional and reactive UI programming

A style of programming that expresses computation as the evaluation of mathematical functions

Recursion

Lazy evaluation

Lambda expressionsType theory

MonadsReferential transparency

Currying

Entscheidungsproblem

Pattern matching

Tuples

Something practical?

Side effects?

State?

No

Bad

Okay…

What instead of how

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))

Null checks are a code smell

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"

PlayerEnemy

Illuminated areaPlayer

sight range

github.com/hezamu/DungeonGame

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

What is Reactive Programming?

int a = b + 1; =B1+1

Spot the difference?

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); }

Listening to events is a code smell

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

RxVaadinwww.vaadin.com/blog/-/blogs/reactive-functional-ui-development-with-vaadin

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!

Thank You!

github.com/hezamu @henrimuurimaa

[email protected]