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: