Why Scala?
Marc Saegesser (@marcsaegesser)
June 3, 2018
What is Scala?
- Scala is a JVM language
- Compiles to Java byte code
- Runs anywhere there's JVM
- Interoperability between Java and Scala
- Multi-paradigm language
- Object oriented programming
- Functional programming
- Open Source
- Standard Libraries
- Lightbend Libraries
- Compiler
Functional Programming?
- Functions are 'first class' entities
- Functions can be assigned to variables
- Passed as arguments to functions
- Returned from functions
- Defined within other functions
- Pure functions
- Like mathetmatical functions
- Given the same arguments always return the same result
- No side effects
- Referential transparency
- Functions provide things that are built in to other languages
- Control flow
- Error handling
def factorial(x: BigInt): BigInt =
if(x == 0)
1
else
x * factorial(x-1)
Functions
// In Java
import java.util.BigInteger
BigInteger factorial(BigIteger x) {
if(x == BigInteger.ZERO) {
return BigInteger.ONE;
} else {
return x.multiply(factorial(x.subtract(BigInteger.ONE));
}
}The equivalent in Java
Programming in Scala (Chapter 2) http://a.co/c1HfcNR
Growing a Language

Traits, Classes and Objects
Traits, Classes and Objects

package org.saegesser.puzzle
sealed trait Tile {
def label: String
def edges: Edges
def withConstraint(constraint: Constraint): Vector[FixedTile]
def show: String = s"$label. $edges"
def matchesConstraint(es: Edges, c: Constraint): Boolean = // ...
}
Traits, Classes and Objects
package org.saegesser.puzzle
sealed trait Tile {
def label: String
def edges: Edges
def withConstraint(constraint: Constraint): Vector[FixedTile]
def show: String = s"$label. $edges"
def matchesConstraint(es: Edges, c: Constraint): Boolean = // ...
}
case class FreeTile(label: String, edges: Edges) extends Tile {
val rotations = edges.rotations
def withConstraint(c: Constraint): Vector[FixedTile] = {
rotations
.filter { r => matchesConstraint(r, c) }
.map { es => FixedTile(label, es) }
}
}
Traits, Classes and Objects
package org.saegesser.puzzle
sealed trait Tile {
def label: String
def edges: Edges
def withConstraint(constraint: Constraint): Vector[FixedTile]
def show: String = s"$label. $edges"
def matchesConstraint(es: Edges, c: Constraint): Boolean = // ...
}
case class FreeTile(label: String, edges: Edges) extends Tile {
val rotations = edges.rotations
def withConstraint(c: Constraint): Vector[FixedTile] = {
rotations
.filter { r => matchesConstraint(r, c) }
.map { es => FixedTile(label, es) }
}
}
case class FixedTile(label: String, edges: Edges) extends Tile {
def withConstraint(c: Constraint): Vector[FixedTile] = {
if(matchesConstraint(edges, c)) Vector(this)
else Vector()
}
}
Traits, Classes and Objects
package org.saegesser.puzzle
sealed trait Tile {
def label: String
def edges: Edges
def withConstraint(constraint: Constraint): Vector[FixedTile]
def show: String = s"$label. $edges"
def matchesConstraint(es: Edges, c: Constraint): Boolean = // ...
}
case class FreeTile(label: String, edges: Edges) extends Tile {
val rotations = edges.rotations
def withConstraint(c: Constraint): Vector[FixedTile] = {
rotations
.filter { r => matchesConstraint(r, c) }
.map { es => FixedTile(label, es) }
}
}
case class FixedTile(label: String, edges: Edges) extends Tile {
def withConstraint(c: Constraint): Vector[FixedTile] = {
if(matchesConstraint(edges, c)) Vector(this)
else Vector()
}
}
object Tile {
def apply(label: String, edges: Array[String]): Tile = FreeTile(label, Edges(edges))
}Traits, Classes and Objects
class Board(val tiles: Vector[Tile]) {
// ...
def requiredEdgeAt(idx: Int, side: EdgeSide): Option[EdgeValue] =
tiles(idx) match {
case FixedTile(_, edges) => Some(matchingEdgeValue(edges.side(side)))
case FreeTile(_, _) => None
}
def constraintsFor(idx: Int): Constraint = {
adjacencies(idx) match { case (t, r, b, l) =>
Constraint(
t.flatMap(e => requiredEdgeAt(e.idx, e.side)),
r.flatMap(e => requiredEdgeAt(e.idx, e.side)),
b.flatMap(e => requiredEdgeAt(e.idx, e.side)),
l.flatMap(e => requiredEdgeAt(e.idx, e.side))
)
}
}
}
Pattern Matching and Destructuring
class Board(val tiles: Vector[Tile]) {
// collect all the Free tiles witih their board positions.
val freeTiles = tiles.zipWithIndex.collect { case (t: FreeTile, i: Int) => (t, i) }
// ...
}
def boardsFrom(board: Board): Vector[Board] = {
@inline
def updateTiles(ts: Vector[Tile], free: Tile, fixed: Tile,
currIdx: Int, destIdx: Int): Vector[Tile] = {
if(currIdx != destIdx) ts.updated(destIdx, free).updated(currIdx, fixed)
else ts.updated(currIdx, fixed)
}
board.freeTiles.headOption.map { case (free, point) => // The first free tile and its index
val c = board.constraintsFor(point) // Constraints for point
board.freeTiles
.map { case (t, i) => (t.withConstraint(c), i) } // Constraint search for point
.filterNot { case (ts, _) =>
ts.isEmpty || // Ignore tiles with no matches
(point == 0 && ts.head.label >= "7") || // Symmetry constraints
((point == 2 || point == 6 || point == 8) && (ts.head.label < board.tiles(0).label))
}.flatMap { case (ts, i) =>
ts.map(t => new Board(updateTiles(board.tiles, free, t, point, i)))
}
}.getOrElse(Vector())
}
An Example
sealed trait Option[A] { // ... }
case class Some(a: A) extends Option[A] { // ... }
case object None extends Option[A] { // ... }
Option and Either and Try, oh my!
They're way less scary than lions and tigers and bears
sealed trait Option[A] { // ... }
case class Some(a: A) extends Option[A] { // ... }
case object None extends Option[A] { // ... }
val aValue = Some(42)
val negValue = Some(-42)
val noValue = None
Option and Either and Try, oh my!
They're way less scary than lions and tigers and bears
sealed trait Option[A] { // ... }
case class Some(a: A) extends Option[A] { // ... }
case object None extends Option[A] { // ... }
val aValue = Some(42)
val negValue = Some(-42)
val noValue = None
aValue.map(_.toString) // Some("42")
noValue.map(_.toString) // None
Option and Either and Try, oh my!
They're way less scary than lions and tigers and bears
sealed trait Option[A] { // ... }
case class Some(a: A) extends Option[A] { // ... }
case object None extends Option[A] { // ... }
val aValue = Some(42)
val negValue = Some(-42)
val noValue = None
aValue.map ( _.toString ) // Some("42")
noValue.map ( _.toString ) // None
aValue.flatMap { x => if(x > 0) Some(x.toString) else None } // Some("42")
negValue.flatMap { x => if(x > 0) Some(x.toString) else None } // None
noValue.flatMap { x => if(x > 0) Some(x.toString) else None } // NoneOption and Either and Try, oh my!
They're way less scary than lions and tigers and bears
sealed trait Either[A, B]
case class Left[A, B](a: A) extends Either[A, B]
case class Right[A, B](b: B) extends Either[A, B]
Option and Either and Try, oh my!
sealed trait Either[A, B]
case class Left[A, B](a: A) extends Either[A, B]
case class Right[A, B](b: B) extends Either[A, B]
val aRight: Either[String, Int] = Right(42)
val aLeft: Either[String, Int] = Left("Oops!")
Option and Either and Try, oh my!
sealed trait Either[A, B]
case class Left[A, B](a: A) extends Either[A, B]
case class Right[A, B](b: B) extends Either[A, B]
val aRight: Either[String, Int] = Right(42)
val aLeft: Either[String, Int] = Left("Oops!")
aRight.map(_*2) // Right(84)
aLeft.map(_*2) // Left("Oops")
aRight.flatMap { x => if(x >= 0) Right(x*2) else Left("Negative") } // Right(84)
Option and Either and Try, oh my!
sealed trait Try[A]
case class Success[A](a: A) extends Try[A]
case class Failure[A](t: Throwable) extends Try[A]
val success = Try { 2 / 1 } // Success(2)
val fail = Try { 2 / 0 } // Failure(java.lang.ArithmeticException: / by zero)
success.map(_*2) // Success(4)
fail.map(_*2) // Failure(java.lang.ArithmeticException: / by zero)
Option and Either and Try, oh my!
def getDBConnection(): Connection
def getUserIdForName(conn: Connection, name: UserName): Int
def updateUserAddress(conn: Connection, user: Int, address: Address): ConfirmationCode
val conn = getDBConnection()
if (conn != null) {
val user = getUserIdForName(conn, name)
if (user != -1) {
val conf = updateUserAddress(conn, user, address)
if (conf != null) {
// Report success
} else {
// Report address change failure
}
} else {
// Report get user failure
}
} else {
// Report get connection failure
}
Option and Either and Try, oh why!
An ugly example
def getDBConnection(): Either[Error, Connection]
def getUserIdForName(conn: Connection, name: UserName): Either[Error, UserId]
def updateUserAddress(conn: Connection, user: UserId,
address: Address): Either[Error, ConfirmationCode]
def updateAddress(name: UserName, newAddress: Address): Either[Error, ConfirmationCode] = ???
Option and Either and Try, oh why!
def getDBConnection(): Either[Error, Connection]
def getUserIdForName(conn: Connection, name: UserName): Either[Error, UserId]
def updateUserAddress(conn: Connection, user: UserId,
address: Address): Either[Error, ConfirmationCode]
def updateAddress(name: UserName, newAddress: Address): Either[Error, ConfirmationCode] =
for {
c <- getDBConnection()
u <- getUserIdForName(c, name)
r <- updateUserAddress(c, u, address)
} yield r
Option and Either and Try, oh why!
def getDBConnection(): Try[Connection]
def getUserIdForName(conn: Connection, name: UserName): Try[UserId]
def updateUserAddress(conn: Connection, user: UserId, address: Address): Try[ConfirmationCode]
def updateAddress(name: UserName, newAddress: Address): Try[ConfirmationCode] =
for {
c <- getDBConnection()
u <- getUserIdForName(c, name)
r <- updateUserAddress(c, u, address)
} yield r
A Digression on Types of Types
Option, Either, Try are examples of Sum types
AKA: Union types or Disjoint Union types or Variants
Tuples, classes, etc. are examples of Product types
Together Product and Sum types are called
Algebraic Data Types (ADTs)
An Example
Implementing a network client
- A network socket
- A heartbeat timer
- A connect timer
- A reconnect timer
- A reconnect count
Requirements
object Connection {
sealed trait ConnectionState
case object Disconnected extends ConnectionState
case class Connecting(s: Socket, connectTimer: TimerTask) extends ConnectionState
case class Connected(s: Socket, heartBeatTimer: TimerTask) extends ConnectionState
case class Reconnecting(s: Socket, attempts: Int, connectTimer: TimerTask) extends ConnectionState
case class ReconnectWait(attempts: Int, reconnectTimer: TimerTask) extends ConnectionState
}
Make Illegal State Unrepresentable
object Connection {
sealed trait ConnectionState
case object Disconnected extends ConnectionState
case class Connecting(s: Socket, connectTimer: TimerTask) extends ConnectionState
case class Connected(s: Socket, heartBeatTimer: TimerTask) extends ConnectionState
case class Reconnecting(s: Socket, attempts: Int, connectTimer: TimerTask) extends ConnectionState
case class ReconnectWait(attempts: Int, reconnectTimer: TimerTask) extends ConnectionState
}
class Connection {
import Connection._
var currentState: ConnectionState = Disconnected
def connect(host: String): Try[Unit] = {
currentState match {
case Disconnected => ???
case Connecting(s, t) => ???
case Connected(s, hbt) => ???
case Reconnecting(s, a, ct) => ???
case ReconnectWait(a, rct) => ???
}
}
// ...
}
Make Illegal State Unrepresentable
Make Illegal State Unrepresentable

Video: Effective ML by Yaron Minsky
Important Libraries
- ScalaTest - Unit test framework
- ScalaCheck - Property based testing
- ScalaZ, Cats - Advanced FP for Scala
- STM - Software Transactional Memory
- Argonaut, Circe - JSON parsing
- Apache Spark - Big data
Conclusion
- Reasoning about and understanding code
- Domain modelling
- Design and implementation
- Testing and validation
- Maintainability
- Error handling
- Code re-use
Functions and Types improve ...
Postlude
Our situation is analogous to that of someone who has learned the rules for how the pieces move in chess but knows nothing of typical openings, tactics, or strategy. Like the novice chess player, we don’t yet know the common patterns of usage in the domain. We lack the knowledge of which moves are worth making ... We lack the experience to predict the consequences of making a move ...
Structure and Interpretation of Computer Programs (sec 1.2)
References
- Programming in Scala by Odersky, et. al. - http://a.co/c1HfcNR
- Growing a Language by Guy Steele - https://youtu.be/_ahvzDzKdB0
-
Effective ML by Yaron Minsky - https://youtu.be/-J8YyfrSwTk
-
Structure and Interpretation of Computer Programs by Ableson, Sussman and Sussman - http://mitpress.mit.edu/sites/default/files/sicp/index.html
-
Functional and Reactive Domain Modeling by Debasish Ghosh - https://www.manning.com/books/functional-and-reactive-domain-modeling
-
An Outsiders Guide to Statically Typed Functional Programming by Brian Marick - https://leanpub.com/outsidefp
-
The puzzle solver - https://github.com/marcsaegesser/ArrowPuzzleSolver
Why Scala?
By Marc Saegesser
Why Scala?
SDC - Naperville June 3, 2018
- 517