December 4, 2008

Pattern Guards fooled by null

A sadness:

Pattern guards don't handle nulls well, and, also, introducing a pattern guard appears to make a default _ not match on null values:

scala> def guard_fails(s: String) {
| s match {
| case s: String if s == null => println("null")
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails: (String)Unit

scala> guard_fails("test")
s=test

scala> guard_fails(null)
scala.MatchError
at .guard_fails(:5)
at .(:6)
at .()
at RequestResult$.(:3)
at RequestResult$.()
at RequestResult$result()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMeth...
scala>

scala> def guard_fails2(s: String) {
| s match {
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails2: (String)Unit

scala> guard_fails2("test")
s=test

scala> guard_fails2(null)
scala.MatchError
at .guard_fails2(:5)
at .(:6)
at .()
at RequestResult$.(:3)
at RequestResult$.()
at RequestResult$result()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMet...
scala>

To code this match defensively, you have to define a null pattern explicitly:

scala> def ok(s: String) {
| s match {
| case s: String if true => println("s=" + s)
| case null => println("null")
| case _ => println("default")
| }
| }
ok: (String)Unit

scala> ok("test")
s=test

scala> ok(null)
null

scala>

October 24, 2008

Scala RichString is not comparable to String

A sadness:
val r: scala.runtime.RichString = "1"
val s: String = "1"
if (r == s) {
println("ok1")
} else {
println("sad1")
}

if (s == r) {
println("ok2")
} else {
println("sad2")
}

if (s.equals(r)) {
println("ok3")
} else {
println("sad3")
}

if (r.equals(s)) {
println("ok4")
} else {
println("sad4")
}

Will print:
sad1
sad2
sad3
sad4

This isn't as sad as:
sad1
ok2
sad3
ok4
which would precipitate Robey's "ultimate sadness".

I got into this state by adding a .drop to a String, and chaos resulted. The details are tangentally described in: http://www.nabble.com/String-and-RichString-equality-td14888607.html

Still, this is another thing to keep in your head when coding along. I suppose it's best to coerce RichStrings back to Strings immediately, so as not to allow RichStrings to propagate too far.

October 13, 2008

NumberFormatException

scala> "true".toBoolean
res1: Boolean = true


That's pretty cool. Does it work for "false" too?

scala> "false".toBoolean
res1: Boolean = false


Yeah! Awesome! How does it handle other values, I wonder?

scala> "1".toBoolean
java.lang.NumberFormatException: For input string: "1"


... Err, what?

August 20, 2008

Handling nulls

There's been a bunch of posts about handling nulls in Scala (and even Java), so let's throw one more into the mix.

This morning, I've been writing some code to read test data from the filesystem, using resources. Following David's lead on showing Java-Scala code, here's a typical Java take on it, note that I need to handle the case of reading the file in IntelliJ differently than I do in the build (IntelliJ now places resources in a subdirectory per module):


def dataFile(path: String): FilePath = {
val resource = getResource(path)
if (resource == null) {
val intellijResource = getResource("furnace/" + path)
if (intellijResource == null) {
error("No resource found at '" + path + "' or 'furnace/" + path + "'")
} else {
intellijResource
}
} else {
resource
}
}

def getResource(path: String) = Foo.getClass.getResource(path)



Now, here's the code rewritten using Option for handling a null returned from getResource (apologies for formatting). I'm using Scalaz's OptionW, as it provides a nice (implicit) conversion from null to an Option (onull) and for throwing an error on None (err). As an aside, these would make a fine addition to the standard API.


def dataFile(path: String): FilePath = dataFileFromAntBuild(path)

def dataFileFromAntBuild(path: String) = getResource(path).getOrElse(dataFileFromIntelliJ(path))

def dataFileFromIntelliJ(path: String) = {
val intellijPath = "furnace" + path
onull(getResource(intellijPath)).err("No resource found at '" + path + "' or '" + intellijPath + "'")
}

def getResource(path: String) = Foo.getClass.getResource(path)



This code is nicer than the first as it explicitly shows that you're sequencing through the nullness and is much more concise (we could make it more concise too if we wish).

For those still stuck in Javaland, there's always Functional Java's Option to achieve a similar thing (though with more verbosity).

Accumulation

It's a pretty common operation to take a list of stuff and accumulate the items. For example, if I have a collection of word frequencies and I want to aggregate the counts by word.

The "Java" imperative way of doing this is to create a HashMap of stuff and populating the HashMap. For example (this is Java-style Scala code):


def accumulate(in: Seq[(String, Int)]): Seq[(String, Int)] = {
val map = new HashMap[String, Int]

for ((str, cnt) <- in) {
if (map.isDefinedAt(str)) map(str) = map(str) + cnt
else map(str) = cnt
}

map.toList
}


What are we doing? We're creating some state and iteratively updating the state.

My preferred way of doing this is thinking about the issue as "transformative". We're transforming the input to the output, without mutating anything.


def acc(in: Seq[(String, Int)]): Seq[(String, Int)] =
in.foldLeft[Map[String, Int]](Map.empty){
case (map, (str, cnt)) => map + (str -> (map.getOrElse(str, 0) + cnt))
}.toList


The transformative approach is shorter and I believe, sweeter.

August 18, 2008

Invoking Java varargs methods from Scala

A quick little example of invoking a Java varags method from Scala. Suppose you've code like the following:


object MyApplication {
private lazy val javaApi = new JavaApi

def main(args: Array[String]) {
val numbers = Array(1, 2, 3)
javaApi.methodWithVarArgs(numbers)
}
}


This will generate a nice error message telling you exactly what to do:


.../MyApplication.scala:6: warning: I'm seeing an array passed into a Java vararg.
I assume that the elements of this array should be passed as individual arguments to the vararg.
Therefore I wrap the array in a `: _*', to mark it as a vararg argument.
If that's not what you want, compile this file with option -Xno-varargs-conversion.
javaApi.methodWithVarArgs(numbers)
^
one warning found
Compile suceeded with 1 warning; see the compiler output for details.


What this is telling you is that you need to help the inferencer by type annotating the argument:


object MyApplication {
private lazy val javaApi = new JavaApi

def main(args: Array[String]) {
val numbers = Array(1, 2, 3)
javaApi.methodWithVarArgs(numbers: _*)
}
}


Nice one! Type annotating is something you'll get used to doing with Scala...

August 17, 2008

importing collections

I'd like to propose the following convention when importing classes from scala's collection library:

If you're using more than one class from the collections library, or might, import the packages themselves, like so:

import scala.collection.immutable
import scala.collection.mutable

They can then be unambiguously used throughout:

val cache = new mutable.HashMap[String, String]

What say ye?