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
© Copyright 2025 Paperzz