SCALA BASICS with refs to Odersky Book, 1st ed. CHAPTERS 1-3 ____________________________________ Can use Scala like a scripting language: $ scala > ... > :quit $ scala -i fn.scala (-i optional) Core commands in "script": print 9 println 9 assert(...) assignment (decodes to an update method --- see p74) val x: T = ... // type T optional var x: T = ... def f(x: T) : T' = ... // types optional class(fi: Ti) // fi's are fieldnames, Ti's their typings REQUIRED? if (..) .. else .. while (..) { .. } for (x <- sequence) ... Core data structures: val r = Array("a","b","c") val r = new Array[String](3) r(0) = "a" for ( i <- 0 to 2) println(r(i)) //See p. 74 val l = List(1,2,3) // :: is Cons, see p 78 for list ops val pair = ("a", 9) Sets and Maps are pp 81-85, e.g: val m = Map("a" -> 9, "b" -> 0) RECOMMENDED GENERAL STYLE: program functionally; use vars for object fields and while-loop counters. CHAPTER 4 _______________________________________ Classes can look like Java: class ChecksumAccumulator { private var sum = 0 def add(b: Byte): Unit = { sum += b } def checksum(): Int = { return ~(sum & 0xFF) + 1 } } val c = new ChecksumAccumulator c.add(9) object ChecksumAccumulator { // "singleton", module-like, p 100 ... } Scala-compiled app based on object construction: // In file Summer.scala import ChecksumAccumulator.calculate object Summer { def main(args: Array[String]) { for (arg val r = new Rational(2,3) r: Rational = 2/3 scala> 2 * r res16: Rational = 4/3 CHAPTER 7 ____________________________________________________ control structures. Note comment at bottom page 155, which states you should use foreach method of a data structure rather than the general for loop. Note loop "filters" p 156. Page 158 documents a clunky variant of a list-comprehension operation, yield. You should read the examples in Chapter 23 if you want to work with yield. Pages 163-164 show the match-cases structure (which evolves into ML-style pattern matching later) CHAPTER 8 _____________________________________________ explains that functions/methods are closures like in Python and ML. Some small examples, showing syntax tricks: scala> var increase = (x: Int) => x + 1 increase: (Int) => Int = scala> increase(10) res0: Int = 11 scala> val someNumbers = List(11,10,5,0, 5, 10) someNumbers: List[Int] = List(11,10,5,0, 5, 10) scala> someNumbers.foreach((x: Int) => println(x)) 11 10 . . . scala> someNumbers.filter(x => x > 0) res7: List[Int] = List(5, 10) scala> someNumbers.filter(_ > 0) res9: List[Int] = List(5, 10) scala> def echo(args: String*) = for (arg echo() scala> echo("one") one scala> echo("hello", "world!") hello world! CHAPTER 9 _________________________________________ shows how to use higher-typed params to define "control structures". This chapter should be studied if you want to define a (bottom-up) DSL "within" Scala. Page 202: scala> def twice(op: Double => Double, x: Double) = op(op(x)) twice: ((Double) => Double,Double)Double scala> twice(_ + 1, 5) res9: Double = 7.0 The syntax can be awkward --- see the example p 198 with the _ Instead of "quoting" command text that is passed as an arg to a defined control structure, there is an odd declaration syntax for a "call by name" argument (p 206): def byNameAssert(predicate: => Boolean) = if (assertionsEnabled && !predicate) throw new AssertionError byNameAssert(5 > 3) // 5 > 3 is wrapped in a closure by the compiler CHAPTER 10 ____________________________________________ shows tricks for classes abstract class Element { // "abstract" means the same as in Java def contents: Array[String] // these are methods that take zero args and can be called like fields: def height: Int = contents.length def width: Int = if (height == 0) 0 else contents(0).length } class ArrayElement(conts: Array[String]) extends Element { def contents: Array[String] = conts } scala> val ae = new ArrayElement(Array("hello", "world")) ae: ArrayElement = ArrayElement@d94e60 scala> ae.width res1: Int = 5 Note that Scala's compiler enforces the usual confusing compile-time enforcement of typing. Page 218, an attempt to merge ML data types with Java classes: class Cat { val dangerous = false } class Tiger( override val dangerous: Boolean, private var age: Int ) extends Cat Tiger’s definition is a shorthand for the following alternate class definition with an overriding member dangerous and a private member age: class Tiger(param1: Boolean, param2: Int) extends Cat { override val dangerous = param1 private var age = param2 } IMPORTANT: methods, defined with def, can be overriden using the override keyword. Fields defined with val cannot be overriden, but fields defined with var can! (DOUBLE CHECK) CHAPTER 12 _________________________________________ traits are "interfaces that can hold code" or "abstract classes that are marked as interfaces". They are a form of *mix-in*. Because they are interface-like, there is no problem with multiple inheritance. It is best to read the entire chapter before using these. Also read Chapter 20, which gives detailed examples. The Figure on Page 261 summarizes how subclasses and traits can be combined and still form a linear method-lookup hierarchy. CHAPTER 15 _______________________________________________________ an ML data type is defined like this: 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 scala> val op = BinOp("+", Number(1), v) op: BinOp = BinOp(+,Number(1.0),Var(x)) scala> println(op) BinOp(+,Number(1.0),Var(x)) scala> op.right == Var("x") res3: Boolean = true 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 } All this "macro expands" to normal classes; see pp 293-297. Page 298: expr match { case BinOp(_, _, _) => println(expr +"is a binary operation") case _ => println("It's something else") } The match constrol structure has built-in code for Lists, tuples, etc. Pages 310-311: "sealed" modifier Page 314: Python-style assignment patterns for structures CHAPTERS 16 AND 17 show that lists, arrays, maps work as you would expect. CHAPTER 18 _________________________________________ reminds us that classes usually hold mutable fields. A "field" can be defined implicitly by "get" and "set" methods (p 391): class Time { private[this] var h = 12 private[this] var m = 12 def hour: Int = h def hour_= (x: Int) { require(0 <= x && x < 24) h = x } def minute = m def minute_= (x: Int) { require(0 <= x && x < 60) m = x } }