12. 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
13. 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"
15. 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
16. 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
}
}
17. Functional summary
Option lets you get rid of NPEs forever
Use small functions with clear
responsibilities
Chaining functional operations is
practically a superpower
20. 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);
}
22. 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
24. 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
25. // 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
26. // 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
27. 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!