Scala Programming Language

Scala Programming
Language
Tomáš Bureš
DISTRIBUTED SYSTEMS RESEARCH GROUP
http://nenya.ms.mff.cuni.cz
CHARLES UNIVERSITY PRAGUE
Faculty of Mathematics and Physics
What is Scala
• Programming language
 class-based
 imperative with a lot of functional constructs
 statically typed
• a bit richer type-system
• type inferences
 support for XML
 compiled to Java-bytecode
• quite seamless interoperability with Java
 basically supported in Eclipse, Netbeans and
IntelliJ Idea
Some basic notes
• Primitive types same as in Java
Objects and classes
// In file Summer.scala
// In file ChecksumAccumulator.scala
import ChecksumAccumulator.calculate
class ChecksumAccumulator {
private var sum = 0
object Summer {
def add(b: Byte) { sum += b }
def main(args: Array[String]) {
def checksum(): Int = ~(sum & 0xFF)+1
for (arg <- args)
println(arg +": "
}
+ calculate(arg))
}
object ChecksumAccumulator {
}
def calculate(s: String): Int =
val acc = new ChecksumAccumulator
• main method
• singleton objects
• classes
• vals & vars
• type parameterization
• type inference
• semicolon inference
for (c <- s) acc.add(c.toByte)
acc.checksum()
}
}
Rational Numbers Example
class Rational(n: Int, d: Int) {
def * (that: Rational): Rational =
new Rational(numer * that.numer,
require(d != 0)
private val g = gcd(n.abs, d.abs)
denom * that.denom)
def * (i: Int): Rational =
new Rational(numer * i, denom)
val numer = n / g
val denom = d / g
override def toString =
numer +"/"+ denom
def this(n: Int) = this(n, 1)
private def gcd(a: Int, b: Int):Int =
def + (that: Rational): Rational =
if (b == 0) a else gcd(b, a % b)
new Rational(numer * that.denom
+ that.numer * denom,
denom * that.denom)
def + (i: Int): Rational =
new Rational(numer + i *
denom, denom)
}
• constructors
• if-else yields value
• infix operators
Implicit Conversions
implicit def intToRational(x: Int) = new Rational(x)
scala> val r = new Rational(2,3)
r: Rational = 2/3
scala> 2 * r
res16: Rational = 4/3
For-loops
val filesHere = (new java.io.File(".")).listFiles
for (
file <- filesHere
if file.isFile;
if file.getName.endsWith(".scala")
) println(file)
• for-loops mapped to calling methods map,
flatMap, foreach, and filter
• typically implemented by Iterable trait
For-loops
val filesHere = (new java.io.File(".")).listFiles
def fileLines(file: java.io.File) =
scala.io.Source.fromFile(file).getLines.toList
val forLineLengths =
for {
file <- filesHere
if file.getName.endsWith(".scala")
line <- fileLines(file)
trimmed = line.trim
if trimmed.matches(".*for.*")
} yield trimmed.length
• nested iterations
• for may yields a value
No break and continue
int i = 0;
// This is Java def searchFrom(i: Int): Int =
boolean foundIt = false;
if (i >= args.length) -1
while (i < args.length) {
else if (args(i).startsWith("-"))
if (args[i].startsWith("-")) {
i = i + 1;
continue;
}
searchFrom(i + 1)
else if (args(i).endsWith(".scala"))
i
else searchFrom(i + 1)
if (args[i].endsWith(".scala")) {
foundIt = true;
break;
val i = searchFrom(0)
}
i = i + 1;
}
• there is no break and continue
• a way around is to use if-else
• tail recursion
First class functions
scala> increase = (x: Int) => x + 9999
increase: (Int) => Int = <function>
scala> increase(10)
res2: Int = 10009
• functions are first-class objects
• (...) on an object leads to calling
method apply(...)
scala> someNumbers.filter(x => x > 0)
res8: List[Int] = List(5, 10)
• types inferred from context
scala> someNumbers.filter(_ > 0)
res9: List[Int] = List(5, 10)
• placeholder syntax
First class functions
def sort(xs: Array[Int]): Array[Int] = {
if (xs.length <= 1) xs
else {
val pivot = xs(xs.length / 2)
Array.concat(
sort(xs filter (pivot >)),
xs filter (pivot ==),
sort(xs filter (pivot <)))
}
}
• partially applied functions
• >, ==, < are methods of class Int
New “control structures”
def withPrintWriter(file: File)(op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
• currying
• curly braces may be used instead of parentheses
when just one parameter is supplied
val file = new File("date.txt")
withPrintWriter(file) {
writer => writer.println(new java.util.Date)
}
By-name parameters
def byNameAssert(predicate: => Boolean) =
if (assertionsEnabled && !predicate) throw new AssertionError
byNameAssert(5 > 3)
• syntactic sugar for writing “() => 5>3”
Traits
trait Ordered[T] {
def compare(that: T): Int
def <(that: T): Boolean = (this compare that) < 0
def >(that: T): Boolean = (this compare that) > 0
def <=(that: T): Boolean = (this compare that) <= 0
def >=(that: T): Boolean = (this compare that) >= 0
}
class Rational(n: Int, d: Int) extends Ordered[Rational] {
// ...
def compare(that: Rational) =
(this.numer * that.denom) - (that.numer * this.denom)
}
• trait is something as Java interface but method definitions
and attributes are allowed
• trait is basically a mixin
• abstract classes and single inheritance rule are still present
Trait as mixins
class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends HasLegs
class Cat extends Animal with Furry with FourLegged
• which method to call? – linearization
• diamond inheritance – all ancestors are in fact “virtual”
Matching
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
• matching against a value
• matching against a type
• it binds parameters of the case
def generalSize(x: Any) = x match {
case s: String => s.length
case m: Map[_, _] => m.size
case _ => -1
}
// match only positive integers
case n: Int if 0 < n => ...
• matching guards
// match only strings starting with the letter `a'
case s: String if s(0) == 'a' => ...
Matching constructors
sealed abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr
def simplifyTop(expr: Expr): Expr = expr match {
case UnOp("-", UnOp("-", e))
=> e
// Double negation
case BinOp("+", e, Number(0)) => e
// Adding zero
case BinOp("*", e, Number(1)) => e
// Multiplying by one
case _ => expr
}
• case classes
• sealed modifier to denote complete enumeration of options
expr match {
case List(0, _*) => println("found it")
case _ =>
}
• possible with any class
using extractors
Patterns everywhere
scala> val myTuple = (123, "abc")
myTuple: (Int, java.lang.String) = (123,abc)
scala> val (number, string) = myTuple
number: Int = 123
• patterns in assignment
string: java.lang.String = abc
scala> val exp = new BinOp("*", Number(5), Number(1))
exp: BinOp = BinOp(*,Number(5.0),Number(1.0))
scala> val BinOp(op, left, right) = exp
op: String = *
left: Expr = Number(5.0)
right: Expr = Number(1.0)
val second: List[Int] => Int = {
case x :: y :: _ => y
}
• cases in fact define a function
with multiple entry points
Exceptions
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val f = new FileReader("input.txt")
// Use and close file
• catch-block uses the match
expressions syntax
} catch {
case ex: FileNotFoundException => // Handle missing file
case ex: IOException => // Handle other I/O error
}
Variance annotations
class Queue[+T] private (val list: List[T]) {
def head: T = list.head
def tail: Queue[T] = {
new Queue(list.tail)
}
def append[U >: T](x: U) = new Queue[U]((x :: list.reverse).reverse)
override def toString = list.toString
}
object Queue {
• class Queue is covariant in
its type parameter
def empty[T] = new Queue[T](Nil)
}
scala> val q = Queue.empty[String].append("Test")
q: Queue[String] = List(Test)
scala> val r : Queue[Any] = q
r: Queue[Any] = List(Test)
Abstract types
class Food
abstract class Animal {
type SuitableFood <: Food
def eat(food: SuitableFood)
}
class Grass extends Food
class Cow extends Animal {
type SuitableFood = Grass
override def eat(food: Grass) {}
}
scala> val bessy: Animal = new Cow
class Fish extends Food
bessy: Animal = Cow@674bf6
scala> bessy eat (new Fish)
<console>:10: error: type mismatch;
• method eat in class Cow accepts only
instances of Grass or of its subclasses
found
: Fish
required: bessy.SuitableFood
bessy eat (new Fish)
Support for XML
scala> val joe = <employee name="Joe" rank="code monkey" serial="123"/>
joe: scala.xml.Elem = <employee rank="code monkey" name="Joe"
serial="123"></employee>
scala>
joe \ "@name"
res15: scala.xml.NodeSeq = Joe
scala>
• parser can recognize XML and create
instances of scala.xml.Node
joe \ "@serial"
res16: scala.xml.NodeSeq = 123
def proc(node: scala.xml.Node): String =
node match {
case <a>{contents}</a> => "It's an a: "+ contents
case <b>{contents}</b> => "It's a b: "+ contents
case _ => "It's something else."
}
• XML in matches
• braces used for escaping
Actors
val echoActor = actor {
while (true) {
receive {
case msg =>
println("received message: "+ msg)
}
}
}
scala> echoActor ! "hi there"
received message: hi there
• actor is a thread
• actors may exchange messages
• synchronized keyword and data
sharing is possible but discouraged
Reusing threads
object NameResolver extends Actor {
import java.net.{InetAddress, UnknownHostException}
def act() {
react {
case (name: String, actor: Actor) => actor ! getIp(name); act()
case "EXIT" => println("Name resolver exiting.") // quit
case msg => println("Unhandled message: "+ msg); act()
}
}
def getIp(name: String): Option[InetAddress] = {
try {
Some(InetAddress.getByName(name))
} catch {
case _:UnknownHostException => None
}
}
}
• react method never returns
• thread is reused for other actors
• possible to have a large number of actors
Futures
val ft = a !! Msg // send message, ft is a future
...
val res = ft() // await future ft
val ft1 = a !! Msg
val ft2 = b !! Msg
val ft3 = c !! Msg
...
val results = awaitAll(500, ft1, ft2, ft3)
// returns a List[Option[Any]] holding the results
val res = awaitEither(ft1, ft2)
Other not covered nifty features
•
•
•
•
imports
access modifiers
tuples, lists
and many others ...
Conclusion
• Quite a good language (better than Java)
• Actors are a powerful concept for dealing
with concurrency and asynchrony
• The IDE support is falling behind
• But the best of all ... it's Java bytecode !!!
 So integration with Java and it's libraries is
straightforward