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?

August 13, 2008

Function Pointers

Scala treats functions declared without a parameter list differently from those with a parameter list. It seems that
def f : Int = 1

isn't a first-class function, but
def fn(): Int = 2

is a first-class function. You cannot assign f to a value or pass it as a parameter. You can pass fn as a parameter trivially, but you need a bit of syntax to assign fn to a value.


class F {
def f: Int = 1
def fn(): Int = 2
}

class G {
def g(func: () => Int) : Int = func()
def yup: Int = g((new F).fn)
def yay: Int = {
val fn = () => (new F).fn()
g(fn)
}
//def nope1: Int = g((new F).f)
//def nope2: Int = {
// val fn: () => Int = (new F).fn()
// g(fn)
//}
//def nope3: Int = {
// val fn = (new F).fn()
// g(fn)
//}
}


None of the nope functions will compile, as fn() is taken as an immediate that returns an Int, not as a function object. Even if you try to "cast" it to () => Int, as in nope2, the compiler still takes fn as an immediate. The yup function works without any fuss which, to this naif, appears at odds with nope2. Both seem to be "casts" of the same ilk. Mmm.

Robey came up with the syntax in the function yay this afternoon. I arrived at the same place via a different path, by emulating how anonymous functions can be assigned to values. In polite company, I might say this is "idiomatic."

So, to assign a previously defined parameterless function to a value, you must declare it as:

def fn(): Int = {...}


and assign it thus:

val function = () => fn()

August 5, 2008

Introduction to High-Level Programming With Scala

Tony Moris gave a nice introduction to Scala talk at the local Java User's Group last week:

In this presentation, Tony introduces some of the essential tenets of functional programming and why they are important. He draws on the existing Java knowledge of the audience to relate foreign concepts. Tony then goes on to introduce the Scala programming language to the audience.


Full details are available: Introduction to High-Level Programming With Scala (duration 106 mins). Oh, and that's me who keeps interrupting in the background...

Disclaimer: Tony and I work together at Workingmouse.

August 3, 2008

The Seductions of Scala, Part I

A nice introductory Scala post on Scala by Dean Wampler:


However, I decided to learn Scala first, because it is a JVM language that combines object-oriented and functional programming in one language. At ~13 years of age, Java is a bit dated. Scala has the potential of replacing Java as the principle language of the JVM, an extraordinary piece of engineering that is arguably now more valuable than the language itself. (Note: there is also a .NET version of Scala under development.)

Here are some of my observations, divided over three blog posts.


Source: The Seductions of Scala, Part I