Consolidated blog first published on
Scala is a new and promising programming language which compiles to JVM byte code and it is touted as a replacement for Java. However, it has a functional and scripting background and there are some things that may surprise you, especially if you come from a Java background...basically, "Here be dragons"...
If you're new to Scala, here's some great links to get you started:
scala> def foo = new { def apply() = "not foo really" }
foo: java.lang.Object{def apply(): java.lang.String}
scala> foo()
res0: java.lang.String = not foo really
Not what you'd expect. Basically a function defined without () must be invoked without (), while a function defined with () can be invoked without (). The side-effects are many, as the example above demonstrates.
Here's more details: http://old.nabble.com/def-xx-and-def-xx-()-not-the-same--to26226554.html#a26226554
Many scala collections and constructs are non-strict, especially Range.
Be very careful with this type of statement, in versions prior to scala 2.8:
object LearnStrict extends Application {
var prefix = "expected-"
val list = for (i <- 0 to 3) yield prefix + i*i
prefix = "UNEXPECTED-1-"
println (list mkString)
prefix = "UNEXPECTED-2-"
println (list mkString)
}
The result is different than what you'd expect...reason being that for is not a loop. for simply delegates to a method named "foreach" or "map" on the subject collection and the implementation for those, on many collections, are not strict - meaning they will evaluate more than once...
The Range has been changed in scala 2.8 - now it's strict, meaning the range (0 to 3) will evaluate only once. However, beware of other collections...
A simple fix is to force the collection toList:
val list = (for (i <- 0 to 3) yield prefix + i*i).toList
Bottom line...remember that for (e <- elements)
it is NOT THE SAME as java's for (e : elements)
. Especially when you're dealing with threads...
IF you want to see some very funny behavior, replace the for with this innocent-looking one:
val list = for (i <- Map ("1"->"a","2"->"b", "3"->"c").values) yield prefix + i
Class parameters are not always members... and it's not easy to figure out if they will be members or just arguments for the primary constructor...
The rules are that if they're marked with "val" or "var" OR the class is a case class, then they ARE class members. If that doesn't apply, then they MAY be class members, if needed later on in the class.
For instance, in this example in section 6.3 of the scala bible, the two must end up as members:
class Rational(n: Int, d: Int) {
override def toString = n +"/"+ d
}
While in this example in section 6.6, they should not...
class Rational(n: Int, d: Int) {
require(d != 0)
val numer: Int = n
val denom: Int = d
override def toString = numer +"/"+ denom
}
What to do? Use VAL as much as possible. If you just can't, tread lightly! See more details at http://old.nabble.com/Constructor-parametes-td16679717.html#a16679717
The best ilustration was given to me by Daniel Sobral: contrast this version:
class LargePerson(name:String, age:Int) = {
override def toString = name + ", aged: " + age
}
with this one:
class SmallPerson(name: String, age: Int) = {
override val toString = name + ", aged: " + age
}
Although there's a debate about fall-back and fall-through, in principle, if you wanted to do the equivalent of java's:
switch (value) {
case 1 :
case 2 :
do1();
break;
...
You can use the OR operator, in Scala:
value match {
case 1 | 2 => do1
...
This generates a compile error:
class NoStatic[T<:AnyRef] {
var lazyValue = null
}
while this works fine:
class NoStatic[T>:Null<:AnyRef] {
var lazyValue = null
}
The trick is that while null is an instance of AnyRef, it is not an instance of T<:AnyRef, which could be Nothing...
...Yeah, I know...
Type parameters overwrite each-other in a class the same way values/variables would. In the example below, the [A] used in the method foreach is not the same [A] as the one used in the rest of the class Cont[A].
class Cont[A] {
def foreach[A](f: A => Unit) {}
}
trait Goo {}
class kuku extends Cont[Int] {
override def foreach[Goo](f: Goo => Unit) {}
}
The correct version is:
def foreach(f: A => Unit) {}