Marc Saegesser (@marcsaegesser)
June 3, 2018
def factorial(x: BigInt): BigInt =
if(x == 0)
1
else
x * factorial(x-1)
// 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
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 = // ...
}
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) }
}
}
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()
}
}
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))
}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))
)
}
}
}
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())
}
sealed trait Option[A] { // ... }
case class Some(a: A) extends Option[A] { // ... }
case object None extends Option[A] { // ... }
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
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
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 } // NoneThey'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]
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!")
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)
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)
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
}
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] = ???
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
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
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)
Implementing a network client
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
}
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) => ???
}
}
// ...
}
Video: Effective ML by Yaron Minsky
Functions and Types improve ...
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)
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