On Github jtjeferreira / scalatron-workshop-sinfo
Static and Strong
val x: Int = 4 val y: List[Int] = List(1, 2)
Happily inferred
val x = 4 val y = List(1, 2)
Values can’t change
val x = 4
Variables can
var x = 4 x += 3
Lazy values don’t compute until accessed
lazy val z = launchMissiles() // nothing happens ... z // missiles launched!
Everything is an expression
val p = {
val ab = a + b
val cd = c + d
ab + cd // last line is returned
}
val x = if (2 > 0) "Plausible" else "No way"
def f(a: Int) = a + 1
def biggerFunction(a: Int, b: Int) = {
val z = a + b * 3 - 4 * a * b
z + 9
}
List("a", "b", "c").contains("b")
// is equivalent to
List("a", "b", "c") contains "b"
3 + 4 // is equivalent to 3.+(4)
"string".length() // is equivalent to "string".length
Classes
class Person(firstName: String, lastName: String) {
// properties
val name = firstName + " " + lastName
// methods
def greetMe(greeting: String) = println(s"$greeting, $name")
// everything inside body runs on construction
println("I have been born")
}
val person = new Person("João", "Ferreira")
assert(person.name == "João Ferreira")
Singletons
object World {
val a = 4
val b = 9
}
assert(World.a + World.b == 13)
Mixins
trait Barking {
def bark() = println("bark")
}
trait Meowing {
def meow() = println("meow")
}
class CatDog extends Barking with Meowing
Partial implementations
trait Greetable {
val name: String // needs implementation
val greeting = "Hi"
def greet() = println(s"$greeting, $name")
}
class Person(firstName: String, lastName: String) extends Greeting {
val name = s"$firstName $lastName"
}
Methods
// can have default values and named arguments def m(x: Int, y: String = "default") = x.toString + y m(3) m(x = 9, y = "foo") // can have type parameters def n[A, B](x: A, y: B) = ???
Functions (function values)
// full declaration
val f: Function1[Int, Int] = (x: Int) ⇒ x + 1
val f: Int ⇒ Int = (x: Int) ⇒ x + 1
// with type annotation
val f: Int ⇒ Int = x ⇒ x + 1
val f: Int ⇒ Int = _ + 1
// with argument annotation
val f = (x: Int) ⇒ x + 1
val f = { x: Int ⇒
...
}
Simple patterns
(x, y) match {
case (3, _) ⇒ 9
case (_, p) if p > 8 ⇒ 10
case (p, q) ⇒ p + q
}
val List(x, y, _*) = List(1, 2, 3, 4, 5)
Case classes
case class Person(name: String, age: Int, partner: Option[Person])
p match {
case Person(_, a, _) if a > 18 ⇒ true
case Person(_, _, Some(Person(n, a, _))) if n.length > a ⇒ false
case Person("Bobby", _, None) ⇒ true
case _ ⇒ false
}
val Person(name, age, _) = p
assert(age == 19)
Anonymous function paradise
val x = List(1, 2, 3).map(i ⇒ i + 1).filter(i ⇒ i > 2)
// or
val x = List(1, 2, 3).map(_ + 1).filter(_ > 2)
// or
val x = List(1, 2, 3) map { i ⇒
val z = doSomethingWith(i)
s"$z is a number, but I’m a string"
}
Methods for everything
def digitSum(x: Int) = x % 10 + (x / 10 % 10) val (cool, notCool) = (1 to 100) .sortBy(digitSum) .take(50) .drop(25) .partition(_ % 4 == 0) val reallyCool = cool.find(_ > 500).getOrElse(123)
Predicting the future
// an int will be here... some time...
val x: Future[Int] = Future {
Thread.sleep(3)
4
}
// print it when it comes
x onSuccess println
// or report if an exception comes instead
x onFailure println
Futures are like collections!
// another Future
val y = x.map(_ + 1)
// a future that depends on x and y
val z = (x zip y) map { case (resultX, resultY) ⇒
resultX + resultY
}
// failure from x or y is propagated!
z onFailure println
Actors
class A extends Actor {
def receive = {
case Message(x) ⇒ ...
case OtherMessage(y, z) ⇒ ...
}
}
Ping-ping
case class Ping(x: Int)
class A extends Actor {
context.actorSelection("../b") ! Ping(22)
def receive = {
case Ping(x) ⇒ sender ! Ping(x + 1)
}
}
class B extends Actor {
def receive = {
case Ping(x) ⇒ sender ! Ping(x + 2)
}
}
Routing DSL
startServer(interface = "localhost", port = 8080) {
path("hello") {
get {
complete {
"<h1>Say hello to spray</h1>"
}
}
} ~
path("test" / IntNumber) { number ⇒
post {
complete("OK")
}
}
}
Console
scala> def f(n: Int): Int = if (n > 1) {
| f(n-1) * n
| } else {
| 1
| }
f: (n: Int)Int
scala> f(5)
res0: Int = 120
Online sandboxes
Build definition
name := "needs" organization := "org.needs" version := "1.0.0-RC3" scalaVersion := "2.11.7" libraryDependencies ++= Seq( "com.typesafe.play" %% "play-json" % "2.2.0", "org.needs" %% "play-functional-extras" % "1.0.0", "org.scala-lang.modules" %% "scala-async" % "0.9.0-M4", "org.scalatest" %% "scalatest" % "2.0" % "test" ) ...
Running tasks
sbt > compile ... [success] Total time: 7 s, completed 06.03.2014 0:25:55
Rerunning on file change
sbt > ~test ... [info] All tests passed. [success] Total time: 1 s, completed 06.03.2014 0:32:49 1. Waiting for source changes... (press enter to interrupt)
Plugins for everything
class ControlFunctionFactory {
def create = new ControlFunction().respond _
}
class ControlFunction {
def respond(input: String) = "Status(text=hello)"
}
Welcome( name=string, path=string, apocalypse=int, round=int ) React( generation=int, name=string, time=int, view=string, energy=int, master=int:int ) Goodbye(energy=int)
Move(direction=int:int) // Move(direction=-1:1) moves the entity left(-1 in x) and down(1 in y). Spawn(direction=int:int,name=string,energy=int) Status(text=string) //Multiple commands Move(...)|Spawn(...)
class ControlFunctionFactory {
def create = new ControlFunction().respond _
}
class ControlFunction {
def respond(input: String) = "Status(text=Hello World)"
}
class ControlFunctionFactory {
def create = new ControlFunction().respond _
}
class ControlFunction {
var n = 0
val directions = ???
def respond(input: String) = ???
}
class ControlFunctionFactory {
def create = new ControlFunction().respond _
}
class ControlFunction {
var n = 0
val directions = List("0:1","1:0","0:-1","-1:0")
def respond(input: String) = {
val direction = directions(n%4)
n += 1
"Move(direction="+ direction +")"
}
}
sealed trait Command
case class Welcome(name: String, apocalypse: Int, round: Int) extends Command
case class React(generation: Int, name: String, time: Int, view: View,
energy: Int, master: Option[XY], collision: Option[XY]) extends Command
case class Goodbye(energy: Int) extends Command
object CommandParser {
def apply(input: String): Command = ???
}
object CommandParser {
def apply(input: String): Command = ???
private def extractMappings(command: String): (String, Map[String, String]) = {
val segments = command.split('(')
if( segments.length != 2 )
throw new IllegalStateException("invalid command: " + command)
val params = segments(1).dropRight(1).split(',')
val keyValuePairs = params.map( splitParam ).toMap
(segments(0), keyValuePairs)
}
private def splitParam(param: String): (String, String) = {
val segments = param.split('=')
if( segments.length != 2 )
throw new IllegalStateException("invalid key/value pair: " + param)
(segments(0),segments(1))
}
}
object CommandParser {
def apply(input: String): Command = {
val (opcode, mappings) = extractMappings(input)
opcode match {
case "Welcome" => Welcome(
mappings("name"),
mappings("apocalypse").toInt,
mappings("rounds").toInt
)
case "React" => React(
mappings("generation").toInt,
mappings("name"),
mappings("time").toInt,
View(mappings("view")),
mappings("energy").toInt,
mappings.get("master") map XY.apply,
mappings.get("collision") map XY.apply
)
case "Goodbye" => Goodbye(
mappings("energy").toInt
)
}
}
}
case class XY(x: Int, y: Int) {
def isNonZero: Boolean = ???
def isZero: Boolean = ???
def isNonNegative: Boolean = ???
def updateX(newX: Int): XY = ???
def updateY(newY: Int): XV = ???
def addToX(dx: Int): XY = ???
def addToY(dy: Int): XY = ???
def +(pos: XY): XY = ???
def -(pos: XY): XY = ???
}
object XY {
def apply(s: String) : XY = ???
val Zero = ???
val One = ???
}
case class XY(x: Int, y: Int) {
def isNonZero = x != 0 || y != 0
def isZero = x == 0 && y == 0
def isNonNegative = x >= 0 && y >= 0
def updateX(newX: Int) = XY(newX, y)
def updateY(newY: Int) = XY(x, newY)
def addToX(dx: Int) = XY(x+dx, y)
def addToY(dy: Int) = XY(x, y+dy)
def +(pos: XY) = XY(x+pos.x, y+pos.y)
def -(pos: XY) = XY(x-pos.x, y-pos.y)
def *(factor: Double) = XY((x*factor).intValue, (y*factor).intValue)
def distanceTo(pos: XY) : Double = (this-pos).length
def length : Double = math.sqrt(x*x + y*y)
def signum = XY(x.signum, y.signum)
def negate = XY(-x, -y)
def negateX = XY(-x, y)
def negateY = XY(x, -y)
override def toString = x + ":" + y
}
object XY {
def apply(s: String) : XY = {
val xy = s.split(':').map(_.toInt) // e.g. "-1:1" => Array(-1,1)
XY(xy(0), xy(1))
}
val Zero = XY(0,0)
val One = XY(1,1)
val Right = XY( 1, 0)
val RightUp = XY( 1, -1)
val Up = XY( 0, -1)
val UpLeft = XY(-1, -1)
val Left = XY(-1, 0)
val LeftDown = XY(-1, 1)
val Down = XY( 0, 1)
val DownRight = XY( 1, 1)
}
React(view=WWWWWWWW_____WW_____WW__M__WW_____WW____PWWWWWWWW) WWWWWWW W_____W W_____W W__M__W W_____W W____PW WWWWWWW
case class View(cells: String) {
val size = math.sqrt(cells.length).toInt
val center = XY(size/2, size/2)
def offsetToNearest(c: Char): Option[XY] = ???
def apply(relPos: XY) = cellAtRelPos(relPos)
def indexFromAbsPos(absPos: XY) = absPos.x + absPos.y * size
def absPosFromIndex(index: Int) = XY(index % size, index / size)
def absPosFromRelPos(relPos: XY) = relPos + center
def cellAtAbsPos(absPos: XY) = cells.apply(indexFromAbsPos(absPos))
def indexFromRelPos(relPos: XY) = indexFromAbsPos(absPosFromRelPos(relPos))
def relPosFromAbsPos(absPos: XY) = absPos - center
def relPosFromIndex(index: Int) = relPosFromAbsPos(absPosFromIndex(index))
def cellAtRelPos(relPos: XY) = cells(indexFromRelPos(relPos))
}
case class View(cells: String) {
def offsetToNearest(c: Char) = {
val relativePositions =
cells
.view
.zipWithIndex
.filter(_._1 == c)
.map(p => relPosFromIndex(p._2))
if(relativePositions.isEmpty)
None
else
Some(relativePositions.minBy(_.length))
}
}
class ControlFunction {
def respond(input: String): String = {
val command = CommandParser(input)
command match {
case react: React => ???
case _ => ""
}
}
}
class ControlFunction {
def respond(input: String): String = {
val command = CommandParser(input)
command match {
case react: React =>
react.view.offsetToNearest('P') match {
case Some(offset) => s"Move(direction=$offset)|Status(text=Harvesting)"
case None => "Status(text=No Food Visible)"
}
case _ => ""
}
}
}
Problem
def foo(a: Array[Int], b: Array[Int])
guaranteeing that a and b have the same size
def foo(a: Int, b: Int) def foo(a: (Int, Int), b: (Int, Int)) def foo(a: (Int, Int, Int), b: (Int, Int, Int)) def foo(a: (Int, Int, Int, Int), b: (Int, Int, Int, Int)) ...
Shapeless to the rescue
//a and b have size 3 def foo(a: Sized[Int, nat._3], b: Sized[Int, nat._3]) = ???
Shapeless to the rescue
//a and b have size 3 def foo[N <: Nat] (a: Sized[Int, N], b: Sized[Int, N]) = ??? //we could even say that def foo[N <: Nat, M <: Nat] (a: Sized[Int, N], b: Sized[Int, M]) (implicit ltEq: N <= M) = ???
val t: (Int, String, Long) = (1, "2", 3l)
val c1: Seq[Any] = Seq(1, "2", 3l)
val c2: Seq[String] = Seq("1", "2", "3")
val hl = 1 :: "2" :: 3l :: HNil val first: Int = hl(0) val second: String = hl(0)