Things that made me go hmmmm…
Over the last couple of months I’ve been working on a Scala project. There is nothing quite like immersing yourself in a language to increase your knowledge. Along the way I figure that I must have made every possible syntax mistake. This post is about three gotchas – detail of the language has tripped me up.
Method invocation with parentheses
As a general rule, it’s not necessary to use parentheses when invoking Scala methods that have either none or one argument. There are two cases where this does not apply however:
1. Default arguments:
Soon after making the switch over from Scala 2.7.7 to 2.8.0 we made our first attempt at using a default parameter. It went something like this:
object MyObject {
def something(someArg: String = "Fred"){ println(someArg) }
}
class Foo() {
MyObject something
}
This results in this error:
error: missing arguments for method something in object MyObject;
follow this method with `_' if you want to treat it as a partially applied function
MyObject something
As pointed out here “If a method definition includes parentheses, when you call it you must also use parentheses. Presumably this still holds if all the arguments to the method have default values.”
2. Using println:
scala> println 10
<console>:1: error: ';' expected but integer literal found.
println 10
^
scala> println(10)
10
According to Programming In Scala: “Note that (the no parentheses) syntax only works if you explicitly specify the receiver of the method call. You cannot write ‘println 10′, but you can write ‘Console println 10′.”
Case?
In my opinion one of the best things about Scala is the first-order functions that are defined on the TraversibleOnce trait. I’d use a couple of them on any given day. I’ve often needed to perform an operation on each key-value pair in a Scala Map. On more than one occasion I’ve started off writing something like this:
val myMap = Map("hay" -> "stack", "killer" -> "whale")
myMap.filter((key, value) => key == "killer").map((key, value) => value)
Which results in this error:
error: wrong number of parameters; expected = 1
Doing this another way using a for expression gives a better result however:
scala> for((key,value) <- myMap if (key == "killer")) yield(value) res19: scala.collection.immutable.Iterable[java.lang.String] = List(whale)
Programming In Scala explains that the Scala compiler translates all for expressions into combinations of higher order methods:
“A for expression of the form:
for (x <- expr1 if expr2) yield expr3
is translated to:
for (x expr2)) yield expr3“
and: “A for expression of the form:
for((x1, ..., xn) <- expr1) yield expr2
translates to:
expr.map{case (x1,...,xn) => expr2}"
So if I want to use higher order methods as I attempted to do first, I need:
scala> myMap.filter({case(key, value) => key == "killer"}).map({case(key, value) => value})
res29: scala.collection.immutable.Iterable[java.lang.String] = List(whale)
Or the equivalent shorter version:
scala> myMap.filter(_._1 == "killer").map(_._2) res30: scala.collection.immutable.Iterable[java.lang.String] = List(whale)
Booleans
A java.lang.Boolean does not implicitly convert to a Scala Boolean. You need to convert the java.lang.Boolean into a java boolean type using the booleanValue method in order for the conversion to be possible:
scala> val javaBoolean: java.lang.Boolean = true
javaBoolean: java.lang.Boolean = true
scala> val scalaBoolean: Boolean = javaBoolean
<console>:13: error: type mismatch;
found : java.lang.Boolean
required: scala.Boolean
val scalaBoolean: Boolean = javaBoolean
^
scala> val scalaBoolean: Boolean = javaBoolean.booleanValue
scalaBoolean: Boolean = true