Scala for Programmers

Scala runs using Java bytecode, but it's not terribly Java-like, except for using Value/Reference types. It's very into implicit types, and silly shortcuts. It has Lisp-like singly-liked lists.

Fluff, misc

Reference/Value types

Like Java, classes and so on, can only be created as reference types - never on the stack. Technically everything is a reference. It uses the trick where value type references act like value types, the same as Python.

Strangely, in classes == and != are pairwise value compares. You need to overload your equals function to make it work. eq and ne perform reference compares (only on reference types).

Math

Declarations

Syntax is val or var, the name, then an optional colon with the type. val is a constant (or a constant pointer to non-const data). It prefers to guess the types whenever it can:

var n : Int = 0  // variable. Initial value is required
var n=7 // using inferred type shortcut
val N : Array[Int] = Array(2,4,6,8) // val is constant (to non-const data, if a pointer
    N(2)=7 // fine
    N = Array(1,2,3,4,5) // error - attempting the change const pointer

var ch : Char = 'e' // C-style char syntax
var w : String = "catfish"

Template types use square brackets:

var stuff : Tuple3[Int, Int, String] =  (3,5,"cow") // guess type of (3,5,"cow")
var L1 : List{String] = _ // underscore here is null (default value for the type)

def findIn[T1] (L : List[T1]) ... // findIn is a template function, for any type of list

String combining

The basic way works, plus some older shortcuts:

w = "We saw " + num + " cats"  // implicit cast to string
  
// script style interpolation if you add "s" before the string. Use $var or ${expression}:
w = s"We saw $num cats with ${num*4} total paws"
w = s"Cats cost $$$catCost" //  Cats cost $6 - $$ is an "escaped" $

Similar to the s"cat" format is f"" (can add formatting like 2.3f) and raw"" (no escapes). Also "saw %i cats".format(num).

Tuples

The usual tuples. Immutable. Use underscore-index to look inside. But, oddly, the first item is 1 (not 0):

var t2 = ("cats",7) // infers type
vat t2b : Tuple2[String,Int] = t2 // the formal type
t2._1 = "dogs" // ERROR - tuples are immutable (as usual)
var animal = t2._1 // _1 is the first item

Scala has the multiple assign shortcut. It does this in even more ways, which is calls pattern matching: var (aType, aAmt) = t2 declares and assigns to both variables.

Scala has more lightweight class with some shortcuts, know as a case class. These are more common than tuples.

If's

Standard C/Java style except they are value-returning. A scala if/else is also a ?:. Plus has extra compares:

You're encouraged to use match, which is like s switch, as a value-returning block. If has many special rules for how to match. Not having a return value is a run-time error:

 val n1 = numOfCats match { 
       case 3 => "herd" // technically 3 is a pattern, matching number 3
       case 5 | 8 => "frog" // regular-expression style
       case _ => "" // underscore is default.
       // Actually, any identifier is a default, but _ is a good "don't care"
    }

The pattern can't do range-checking, but you can add if's afterwards:

 val n2 = testScore match { 
       case n if n<60 => "F" // works like a function: n is placeholder for the input
       case n if n<70 => "D" // can use same n, or different
       case n => "" // default (using _ here is more common)
 }

Loops

Scala has standard C/Java while and do-while loops. But the for loops are only python-style, over a range you create, or a list. The <- is part of the syntax:

for (i <- 1 to 10) { do stuff with i }
for (i <- 1 to 10 by 2)
for( n <- 0 until len) // shortcut for: 0 to n-1, which is 0.to(n-1)

for(w <- listOfAnimalNames) // standard foreach loop

1 to 10 is using the calling shortcut. It's actually 1.to(10), which is a built-in int function returning a range. Likewise the second is 1.to(10).by(2), which could be simply 1.to(10,2). Fun fact, ranges can be examined with r1(0), as an index.

Scala has a very silly nested loop shortcut. You write both loops in a single for. The second one is the inner loop:

for(x <- 1 to 10, y <- 1 to 8) //  Runs 80 times, x=1, y is 1 to 8 ...
for(a <- Animals, n <- 1 to 3) // handles each animal with 1,2,3

There's also a shortcut to put an if inside the loop, which acts as a continue command. This examines every animal except dogs:

for(x <- Animals if(x!="dog")) { // every item in Animals, except dog's
  val cuteness = match x {
    case "cat" => 10
     ...
  }
}

We can combine these. The inner if's are allow to use the outer variables. This examines some odd diagonal slide of a grid:

for( x <- 0 until 10 if(x!=3), // skipping 3's just to show we can
       y <- 0 until 10 if(x+y>3 && x<y<12)) // we're using both look vars in the test

Blocks

Scala allows, and encourages, value-returning blocks. The last processed item is the value. Of course, anything declared inside of these blocks are local:

val someMath = { val n=z1*z2; n*2 - z3n }
// uses n as a local aux var, then returns the final equation

w = { val petSum=cats+dogs; val w2=if(petSum>2) "lots" else "some"; s"I have $w2" }
// aux var petSome. w2 is a partial string. Ends by adding "I have " in front and returning

Collection types:

Scala encourages immutable types. The single-linked immutable Lisp-style List works pretty well for this. Scala prefers not to use doubly-linked lists. Most types have a creation shortcut: TypeName(v1,v2,v3). All use A.Length (with optional ()'s). L(3) is the indexing operation (round instead of square).

Arrays are technically not resizable. The operations which change the size are a bit slow:

A1 : Array[Int] = Array(2,4,6,8)
A1 = A1 :+ 5 // add to end
A3 = A1 ++ A2 // ++ concatonates two arrays

You'e suppose to create an ArrayBuffer (along with a ListBuffer and StringBuilder). Play with those crazy, unsafe direct-access types, then convert the final result into an Array.

L1 = List(2,4,6,8,10)
L2 = L1.tail // all but 1st
L3 = 0 :: L1 // adding 0 to front (best place to add)
     L2
       \
L1 -> 2 4 6 8 10
     /
L3->0

To look nice, ++ or triple ::: concat two Lists.

Other fun facts, Scala's Queue class isn't implemented as a doubly-linked list, as you'd think. It uses 2 singly-linked lists, Add to the front of the "end" list, and flip it when the "front" list runs out of elements.

Scala has a pile of list-traversing operations:

Functions

A basic declare looks like this. Note the return type, then the = just before the body:

def addTwo(x : Int, y : Int) : Int = {
  return x+y
}

The void type is replaced by Unit. Unit counts as a real type, and even as a value. Parameters are constant, unless you add "var" in front of them (in other words, they're implicitly declared as "val").

There are a pile of Shortcuts

Instead of having pre-conditions, or returning -1 for bad inputs, Scala has a formal partial function. You predefine inputs where it won't run. You can write real functions isDefiinedAt and apply (recall apply is the () operator), or you can write case statements (it automatically add the match).

val ff = new PartialFunction[String,Int] {
  def isDefinedAt(w:String) = w!="cat"  // ff.isDefinedAt("cow") runs this
  def apply(w:String) = w.length*2  // the real function
}

// this counts as a "match" statement against the input.
// isDefinedAt is implicitly created by the match fall-through:
val ff2 :  PartialFunction[Int,Int] = { // why is this declared differently? Beats me
  case n if(n>5) => n*2
}

ff.isDefinedAt("frog") // true
ff("cat") // 6. 
ff2.isDefinedAt(3) // false

The advantage of having a formal isDefinedAt is we can use it to chain them:

(ff2 orElse ff3)(7) // runs ff2 on 7, unless ff2.isDefinedAt(7) is false

First class functions

var f : Int => Int defines f as a function variable. We can also use C# style Function1[Int,String]. The number is how many parms, with the last being the return type.

Anonymous functions use Python-style syntax:

f = (x:Int) => if(x>3) 10; else 5 // using value-returning if and no-return shorcut

If we can infer the input types, underscore stands for each, in order: (_ * _) is short for (x:Int, y:Int) => x+y.

Duck-type functions (Structural types)

We're allowed to replace the type of an input class with the operations it supports - formal support for Duck typing. You can also specify member variables (since they are really paren-less functions):

This defines type HasX as anything with an X member variable, using the special type keyword. Then the real duck function takes any HasXint - meaning anything with member x:

type HasXint = { var x : Int } // defines a Duck-type "anything with: var x : Int"

// this takes any class with a dot-x:
def sd1(cc : HasXint) cc.x+5 // using a Duck type to specify input type

 sd1(point1) // Point class has an x
 sd1(dog1) // Pretend the Dog class also has an x

This one specifies any class with a yell function, taking an Int and returning nothing:

// canYell is any class with yell(int)
type canYell = { def yell(times:Int):Unit } // defines a Duck type

def yell7(yy : canYell) = yy.yell(7) // runs their yell function with a 7

For fun, here's how it looks without pre-defining the Duck-type:

def sd1[HasX <: { var x : Int }] (cc : HasX) = { cc.x + cc.x }

Imports, namespaces

import scala.collection._ grabs everything in there.

Classes

Class definitions are mostly Java-like. Except the default accessibility is public. It also uses the trick where assignments to member vars are moved into all constructors. Member vars must have a starting value:

class A extends B {
  var x : Int = 0
  var y = 7
}

There's a shortcut for creating a constructor and member vars - known as the Primary Constructor. You're encouraged to always use it. Add assignments to variables in parens after the class name. "Naked" code lines in your class are part of this constructor. Here, point has fields x and y, and also z, and Point(2,4) as a constructor:

class Point(var x:Int=0, var y:Int=0) { // <-declares x and y, AND is a constructor
  println("constr") // <- also part of primary constructor
  
  var z : Int = 0 // point has x and y (above) and also z
  
  def this(zz:Int) {  // <- an additional normal constructor
    this(6,10); // <- required to call primary one first, if it exists
    z=zz
   }
}

All variables must be initialized, either in the primary constructor, or the field declaration. Underscore is a shortcut for the default value:

  class Point { // <- no primary constructor, which is fine
    var x:Int=0
    var y:Int=_ // same as 0
    var z = 0 // inferred type Int
  }
  

Style-wise, underscore means "will initialize in a line below".

Fun fact: a var declared with no initial value is actually declaring an abstract interface. Sub-classes must either declare it, or create it using a get/set pair.

get/sets use the fake variable for the get, and the name followed by an underscore for the set:

class OneNum {
  private var xx:Int=0 // backing var
  
  // getter/setter pair for x:
  
  def x = xx // the getter, using several shortcuts
  
  def x_=(xIn : Int) { // the setter
      var xOut=xIn; // NOTE: inputs to setters are constant
      if(xOut>10) xOut=10;
      xx=xOut
   }
}

Scala actually makes every field into a getter/setter if you don't make one. The idea is that a class only has functions, so all interfaces are clean.

Interfaces

Scala's name for a virtual base class or a Java-style interface is a trait. As with Java, you can inherit from 1 real class and as many interfaces as you want.

A new, crazy thing in scala is having virtual variables. Declaring a variable with no starting value magically makes it virtual. A concrete subclass must either declare it, or declare a getter/setter:

trait Mood {
  var howMuch : Int // abstract field, must be created in subclass
  def isGood() : Boolean // abstract virtual function
}

class DogMood extends Mood {
  var x : Int = 0 // a normal declaration, fulfilling our obligation
  def isGood() : Boolean = howMuch>10
}

Scala also allows "traits" to write bodies for the functions - you're allowed to use the virtual variables:

trait Mood {
  var howMuch : Int // still virtual, but will be an int
  def isGood() : Boolean = howMuch>=10 // implemented
}

class DogMood extends Mood { var x : Int = 0 } // all we need for a concrete subclass

Misc weirdness

Type projection is nothing special. But it points to other weirdness. Cat_t#Claw_t is the boring way to find the Claw_t nested class of Cat_t. Nothing special, and in many languages the pound-sign would be silly -- just use a dot.

But in scala, like Java, instances of objects of a nested class are associated with the parent that made them. If we have two cats, then c1.Claw_t and c2.Claw_t are different classes. They have different concrete parents. If a member function of c1 takes a Claw_t as input, it expects a claw which it made - implicitly a c1.Claw_t. Giving it a c2.Claw_t is a type mismatch. That system is called Path Dependent Types.

Type projection, Cat_t#Claw_t, is for the rarer cases where you don't want to specify any particular parent instance. It matches anyCatInstance.Claw_t. That's an appropriately awkward syntax, for something you wouldn't want to do that often.

Covariance / Contravarience: These are rules about when a template class can accept subtypes of its template argument, and in which direction. For example, a function taking a List[Duck] can probably also take a List[Mallard]. Many languages have automatic rules allowing this -- but in Scala you have to explicitly define the behavior. [+T] means normal subclasses are allowed, just [T] means it has to be that exact class, and [-T] means superclasses are allowed. For example, scala lists are defined using List[+T], which means they work the way we expect when we write list-using functions.

This can't make things legal that weren't. It just gives nicer error messages. If scala had List[-T] then your function doStuff(D : List[Duck]) would accept a List[WaterFowl] as input but would fail when you tried to use Duck-specific member functions.

Within a template type with multiple types, often a nested one, [S <: T] says the second type, which will be named S, can be T or any subtype. If a function takes List[T] and List[S <: T], the second list can be the same type as the first, or any subtypeT. [S >: T] is the opposite -- T or any supertype.

This all makes sense, and has reasons you'd use any of those three. But you can also either ignore it, or use the [+T] option. The standard classes have everything set the correct way.

Mixins: these are nothing. A few languages, scala included, say that inheriting from a class and also from some interfaces (traits, in scala) is a mix-in. Scala also allows interfaces to inherit from abstract classes, which is fun, but nothing special.

Higher-order functions: also nothing special. It's scala's term for passing or returning a function, which every other language can do without needing a special name. But it's cool how scala uses a the real math term for this.