On Github stanch / funlx-meetup
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
}
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("Nick", "Stanchenko")
assert(person.name == "Nick Stanchenko")
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 ⇒
...
}
All methods are unicode operators (don’t overdo it)
3.+(3) // same as 3 + 3
class Bird {
def ♫(song: String) = println(song * 3)
def sing(song: String) = ♫(song)
}
val bird = new Bird
bird ♫ "twitt" // same as bird.♫("twitt")
bird sing "twitt" // same as bird.sing("twitt")
Single parameters can go in braces
bird ♫ {
...
}
Multiple parameter groups
def f(x: Int)(y: Int) = x * y
f(3) {
val z = 8 + 9
z - 1
}
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)
Parameters the compiler knows
def foo(bar: String)(implicit config: Config) = ...
// explicit
foo("bar")(new Config("baz"))
// implicit
implicit val c = new Config("baz")
...
foo("bar") // c: Config found and inserted
“Pimp my library”
implicit class PimpedInt(x: Int) {
def times[A](f: Int ⇒ A)
}
5 times { i ⇒
println(i)
}
User writes
myButton.on("click", alert("Hi"))
At compile-time, this expands into
myButton.setOnClickListener(new View.OnClickListener {
def onClick(v: View) = alert("Hi!")
})
You can write code that generates code!
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
Juggle futures with ease
// our futures
val x: Future[Int] = ...
val y: Future[Int] = ...
// no blocking here!
val z: Future[Int] = async {
await(x) + await(y)
}
Entire async workflows
async {
val data = await(getDataFromDatabase(38))
val processed = await(processData(data))
await(prettify(processed))
}
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")
}
}
}
Android DSL
var greeting = slot[TextView]
l[LinearLayout](
w[Button] ~>
text("Greet me!") ~>
On.click {
greeting ~> text("Hello there") ~> show
},
w[TextView] ~> hide ~> wire(greeting)
)
Composition
def caption(cap: String): Tweak[TextView] = text(cap) + TextSize.large
w[TextView] ~> caption("I’m a caption!")
Futures?
val futureCaption: Future[Tweak[TextView]] = myFuture.map(text ⇒ caption(text)) // no problem! w[TextView] ~> futureCaption
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.10.3" 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