Scala for Java programmers

I highly recommend a careful reading of "Programming in Scala" by Martin Ordesky, Lex Spoon, and Bill Venners. Read it through once to get familiar with the syntax and features, and then read it again to see how it all fits together.

I find it worth emphasizing how some features should change the way a Java programmer designs and builds programs.

I will assume functional features are not a novelty at this point. We have had Scheme ( http://mitpress.mit.edu/sicp/full-text/book/book.html ) and Haskell ( http://www.realworldhaskell.org/ ) for a long time to show us how functional programming is done. Scala is not a pure functional language, and that should be accepted as a feature, not as a compromise or defect. Object-oriented encapsulation of state has proven useful a long time, and we know how to scale large projects with this model. We can see how to move our projects and programmers from Java to Scala.

I'll also assume that you've had time to get accustomed to some of the syntactic sugar of scala. Conciseness does not necessarily change how you program, though it can when boilerplate was previously overwhelming.

§    Thunking

At runtime, you can assemble a chuck of code, often called a "thunk," and pass it along to be evaluated when and if necessary.

Scala supports strict evaluation of method arguments: any expressions are evaluated before they are passed. Nevertheless, you can easily prevent evaluation by passing a function that contains the expressions you want evaluated.

A good example is an assert, where you only want to evaluate an expression when asserts are enabled. Java found it necessary to introduce a new keyword to support this feature. Scala does not.

This function always evaluates the argument, even when disabled:
val assertionsEnabled = false
def dumbAssert(test: Boolean) =
   if (assertionsEnabled && !test) throw new AssertionError

dumbAssert(1>2)

Notice that you can assign a function literal to a function value:
scala> val calculateTest = () => {println("calculating..."); 1 > 2}
calculateTest: () => Boolean = <function0>

scala> calculateTest()
calculating...
res0: Boolean = false

scala> dumbAssert(calculateTest())
calculating

This function retains an expression, then evaluates that expression when the function is called, even when assertions are not enabled.

(Note this function literal is actually an anonymous object that extends the trait Function0. An apply method invokes the function.)

Instead you can define an assert that takes a function.
def betterAssert(test: () => Boolean) =
   if (assertionsEnabled && !test()) throw new AssertionError

betterAssert(() => 2>1)
betterAssert(calculateTest)

The function is called only when assertions are enabled.

Passing a function is still a burden. If you try a simple expression evaluating to a boolean, you get a error because a function is expected.
scala> betterAssert(2>1)
<console>:8: error: type mismatch;
 found   : Boolean(true)
 required: () => Boolean
       betterAssert(2>1)

If you omit the () from the declaration, then you are passing the argument by name, and delaying its evaluation until first used.
def bestAssert(test: => Boolean) =
   if (assertionsEnabled && !test) throw new AssertionError

bestAssert(2>1)
bestAssert(calculateTest())

You no longer see the calculation as a side-effect, when the expression does not need to be evaluated.

Notice how similar this syntax is to forming and passing a function.

§    Currying for control

Ever find yourself copying boilerplate that you seem unable to reuse? Let's pretend the following try/catch/finally block is useful.
def mathTest(test: => Boolean): Unit = {
  try {
    if (!test) throw new AssertionError("Test failed");
  } catch {
    case ae: ArithmeticException => throw new AssertionError("Bad Math");
  } finally {
    println("Finally")
  }
}

It would behave like this:
scala> mathTest (3>2)        
Finally

scala> mathTest (2>3)
Finally
java.lang.AssertionError: Test failed
...

scala> mathTest (3/0>2)
Finally
java.lang.AssertionError: Bad Math
...

Let's pretend you want to change the error words, and the contents of the finally.
def mathTest2(finalAction: => Unit, errorWords: String, test: => Boolean): Unit = {
  try {
    if (!test) throw new AssertionError(errorWords)
  } catch {
    case ae: ArithmeticException => throw new AssertionError("Bad math")
  } finally {
    finalAction
  }
}

You could use it like this, awkwardly:
scala> mathTest2(println("Pretend I'm closing a file"),"normal math", 3>2)
Pretend I'm closing a file

scala> mathTest2(println("Pretend I'm closing a file"),"crazy math", 1/0>2) 
Pretend I'm closing a file
java.lang.AssertionError: Bad math
...

Instead, you can declare a function that returns a function that returns a function. (And the first argument is a function.)
def mathTest3(finalAction: ()=>Unit)(errorWords: String)(test: => Boolean): Unit = {
  try {
    if (!test) throw new AssertionError(errorWords)
  } catch {
    case ae: ArithmeticException => throw new AssertionError("Bad math")
  } finally {
    finalAction()
  }
}
mathTest3: (finalAction: () => Unit)(errorWords: String)(test: => Boolean)Unit

You can then save a new "curried" function like this:
val mathTest4 = mathTest3 {
  () => println("Never again forgetting to close that file.")
} _

The new function mathTest4 is a function that returns a function.

Why did I not pass this first argument by name, instead of as a function? Because that argument is evaluated to construct the function that is returned.

You can apply this as you would a control structure:
mathTest4 ("Normal math?") {
  val two = 2
  val three = 3
  println ("Finished with expensive calculations.");
  three > two
}
Finished with expensive calculations.
Never again forgetting to close that file.

Or perhaps not so good math:
mathTest4 ("Abnormal math?") {
  val one = 1
  val infinity = 1/0
  println ("Did I get away with dividing by zero?");
  infinity > one
}
Never again forgetting to close that file.
java.lang.AssertionError: Bad math
...

§    Whole lot of nothing

In Java, we have null and void. In Scala we have a few more alternatives.

In Scala null is actually an instance of the Null class. The Null class is a subclass of any class that derives from AnyRef. So you can assign a null instance to any reference type:
scala> val x = null
x: Null = null

scala> var y = "foo"
y: java.lang.String = foo

scala> y = x
y: java.lang.String = null

scala> null == y
res4: Boolean = true

The == is a final method defined in Any

Unit is like a void, returned from a method that is called only for side-effects. However Unit is an actual type, with only one allowed value, written as ().
scala> Unit
res3: Unit.type = object scala.Unit

scala> val unit = ()
unit: Unit = ()

scala> def foo() = println("bar")
foo: ()Unit

scala> assert (foo() == ())
bar

Nil is a singleton object that corresponds to the empty list:
scala> Nil 
res2: scala.collection.immutable.Nil.type = List()

scala> val list = List("a")
list: List[java.lang.String] = List(a)

scala> list.tail     
res29: List[java.lang.String] = List()

scala> assert (list.tail == Nil)

Nothing is the value returned by a method that does not return. You can use such a method where another value is expected.
scala> def f() = {throw new Exception("No return.")} 
f: () Nothing

scala> var g = () => {"Returns string"} 
g: () => java.lang.String = <function0>

scala> g = f 
g: () => java.lang.String = <function0>

scala> g() 
java.lang.Exception: No return.  

None is one of two possible values for the Option type. The other possible value is Some(x), where x is a useful value.

§    Algebraic types

Pattern-matching in scala, is actually an object-oriented implementation of algebraic types. Option is an example.

For example, we can define a type like this
abstract class Color
case class Black() extends Color
case class White() extends Color
case class Gray(shade: Int) extends Color
case class RGB(r: Int, g: Int, b: Int) extends Color

This declares an abstract base type, and four specific derived types. These are types, not instances, like an enum.

Each case class automatically gets some functionality:

  1. Each class has a factory method with the name of the class to construct an instance.
    scala> val b = Black()
    b: Black = Black()
    
    scala> val c: Color = Black()
    c: Color = Black()
    
    scala> val g = Gray(25) 
    g: Gray = Gray(25)
    
  2. All arguments in the parameter list are maintained as val fields.
    scala> val g = Gray(25)
    g: Gray = Gray(25)
    
    scala> g.shade
    res0: Int = 25
    
    scala> g.shade = 32
    <console>:9: error: reassignment to val
           g.shade = 32
    
  3. You get deep recursive implementations of equals, hashcode, and toString
    scala> g
    res1: Gray = Gray(25)
    
    scala> g == new Gray(25)
    res2: Boolean = true
    

In combination, this gives great power to a match expression, which we here put into a function that returns a String.
def describe(c: Color) = c match {
   case Gray(25)         => "favorite color" 
   case Gray(s) if s>255 => throw new IllegalArgumentException
   case Gray(s)          => "shade of "+s
   case rgb @ RGB(0,_,_) => "No red in "+rgb
   case other            => "Something else: "+other
}

This is better than a overloaded function, because you can make cases for specific values of arguments.
scala> describe (Gray(25))
res5: java.lang.String = favorite color

scala> describe(Gray(16))
res6: java.lang.String = shade of 16

scala> describe (RGB(0,1,1))
res7: java.lang.String = No red in RGB(0,1,1)

scala> describe (RGB(1,1,1))
res8: java.lang.String = Something else: RGB(1,1,1)

scala> describe (Black())
res9: java.lang.String = Something else: Black()

scala> describe (Gray(256))
java.lang.IllegalArgumentException
...

You can leave off the match because a case expression is a function literal:
val describe: Color => String = 
{
   case Gray(25)         => "favorite color" 
   case Gray(s) if s>255 =>  throw new IllegalArgumentException
   case Gray(s)          => "shade of "+s
   case rgb @ RGB(0,_,_) => "No red in "+rgb
   case other            => "Something else: "+other
}

Option[A] and List[A] are parametric versions of case classes.

In this way you can create nested cases.
scala> val test = Option(Gray(25))
test: Option[Gray] = Some(Gray(25))

scala> val list = List(Some(Gray(25)), None, Some(Gray(1)))
list: List[Option[Gray]] = List(Some(Gray(25)), None, Some(Gray(1)))

Since the outer List is a sequence, we can iterate over it with pattern matching:
scala> for (Some(c) <- list) {println(c)}
Gray(25)
Gray(1)

This is actually equivalent to
list filter { 
  case Some(c) => true 
  case _ => false 
} foreach { 
  case Some(c) => println(c) 
}

Or better, iterate with our describe function:
scala> for (Some(c) <- list) yield describe(c)
res6: List[String] = List(favorite color, shade of 1)

Which is equivalent to
list filter { 
  case Some(c) => true 
  case _ => false 
} map { 
  case Some(c) => describe(c)
}

Even better, let's do it the recursive way.
def print(list: List[Option[Color]]): Unit = 
  list match {
    case Some(c) :: rest => println (describe(c)) ; print(rest)
    case None :: rest    => print(rest)
    case Nil             => ()
  }

This works because :: (pronounced "cons") and Nil are the two case classes for a List.
scala> print (list)
favorite color
shade of 1

Here's the shorter version using a function literal:
val print: List[Option[Color]] => Unit = 
  {
    case Some(c) :: rest => println (describe(c)) ; print(rest)
    case None :: rest    => print(rest)
    case Nil             => ()
  }


Return to parent directory.