Wednesday, May 26, 2010

A common misunderstanding is that a code block (without parameters) is a function. That is not the case. A code block is a sequence of statements that are executed and result the last statement is returned. That sounds like a Function0, however, if the block is passed to a method/function only the last statement will be returned to the function/method. If that method/function expects a function as the parameter the last statement maybe returned as a function not a value, this means that the block itself is not a function.

scala>var count = 0

count: Int = 0

// the last statement is returned as a function so count

// is incremented only one during the creation of the function

scala> List(1,2,3,4).map{count += 1;_ + 1}

res9: List[Int] = List(2, 3, 4, 5)

scala> count

res10: Int = 1

// now the count increment is within the function

scala> List(1,2,3,4).map{i => count += 1;i + 1}

res11: List[Int] = List(2, 3, 4, 5)

scala> count

res12: Int = 5

The previous example demonstrates a Gotcha if I ever saw one. Map expects a function so the block essentially constructs a function. The last statement being the function. The first line count += 1 executed only once because it is part of creating the function not part of the resulting function. This is equivalent to:

scala>val x = {count += 1 ; i:Int => i +1}

x: (Int) => Int = < function1>

scala> List(1,2,3,4).map(x)

res15: List[Int] = List(2, 3, 4, 5)

Beginning a block with the parameter list signals that the entire block is a function.

Rule of thumb: Functions with placeholder parameters should be a single statement.

Thursday, May 20, 2010

A second "gotcha" that one might get tripped up when dealing with abstract types is the signature of the concrete class contains type information about the abstract type. So if you are not explicit when assigning a variable or defining a function you can get unexpected compiler errors.

scala>trait S {

| type x

| def get : x

| }

defined trait S

scala>var sample = new S{

| type x = Int

| def get = 3

| }

sample: java.lang.Object with S{type x = Int} = $anon$1@397af435

scala> sample = new S {

| type x = Double

| def get = 3.0

| }

< console>:7: error: type mismatch;

found : java.lang.Object with S

required: java.lang.Object with S{type x = Int}

sample = new S {

In this example sample uses type inference so the actual type is S with underlying type Int. The consequence is that sample can only be assigned with instances of S with type x = Int. The fix is to explicitly declare the variable type:

scala>var sample2 : S = new S{

| type x = Int

| def get = 3

| }

sample2: S = $anon$1@31602bbc

scala> sample2 = new S {

| type x = Double

| def get = 3.0

| }

sample2: S = $anon$1@4de5ed7b

The same thing happens when declaring functions and allows type inference for function definition

scala>class Fac {

| def newS = new S {

| type x = Int

| def get = 3

| }

| }

defined class Fac

scala>class SubFac extends Fac{

| overridedef newS = new S {

| type x = Double

| def get = 3.0

| }

| }

< console>:8: error: type mismatch;

found : java.lang.Object with S

required: java.lang.Object with S{type x = Int}

overridedef newS = new S {

The fix for this example is to be explicit in the definition of the function in the superclass

Monday, May 17, 2010

In a previous post about abstract types I showed one of the benefits of using abstract types over parameterized types. Abstract Types vs Parameter. The next several posts will feature potential problems you may encounter when using Abstract Types.

I should point out that abstract types are not inherently difficult to understand but they are rather different from anything you will see when you come from the Java world so if you are new to them I would use them with caution at first.

In the abstract types example you will notice that the abstract type 'I' in Foreach is not within the trait Source rather it is outside in the Foreach trait. At first one might consider putting the type in Source rather than Foreach. The naive change can get you in trouble (but there is a couple easy fixes)

So what is the problem? The problem is simple but subtle. Notice that source is defined as a def. So calling source 2 times may return 2 different instances of Source. A simple change can fix this. Either change def source : Source to val source : Source. Or change the method foreach to assign the result from source to a val.

Monday, May 3, 2010

This topic (and the next) are intended to discuss abstract types. A class/trait with an abstract type is quite similar to a class/trait type parameter. For example:

trait C[A] {

def get : A

def doit(a:A):A

}

trait C2 {

type A

def get : A

def doit(a:A):A

}

Both implementations have similar properties. However they are NOT the same. At first I thought that I could used them inter-changeably. However, consider the following examples:

//compiles

def p(c:C[Int]) = c.doit(c.get)

// doesn't compile

def p2(c:C2) = c.doit(c.get)

So why doesn't p2 compile? Because it returns A. From the signature of p2 it is impossible to know what p2 returns. There are several ways to fix this problem. One make the method return Unit:

// compiles because the internals of C2 does not leak out

def p(c:C2):Unit = c.doit(c.get)

Another fix would be to change doit to return Unit or an explicit return value like Int

trait C2 {

type A

def get : A

def doit(a:A):Int

}

// compiles correctly

def p(c:C2) = c.doit(c.get)

A second difference between parameterized types and types with abstract type values is illustrated below:

trait C2 {

type A

def get : A

}

scala>var c : C2 = new C2 {

| type A = Int

| def get = 3

| }

c: C2 = $anon$1@11a40fff

// what is the type of result if at compile time the

// value of c is not known

scala>var result = c.get

result: C2#A = 3

scala> c = new C2 {

| type A = String

| def get = "hi"

| }

c: C2 = $anon$1@5f154718

// crazy eh :) the variable can be anything but does not

// have type Any so you cannot assign arbitrary values

scala> result = c.get

result: C2#A = hi

scala> result.isInstanceOf[String]

res0: Boolean = true

// while the dynamic type of result is a string the

// static type is not so you cannot assign a string to result

scala> result = "4"

< console> :8: error: type mismatch;

found : java.lang.String("4")

required: C2#A

result = "4"

^

The obvious question is what use are abstract types. I don't claim to know them all but the main point is that they do not expose the internal implementation details to the world. The famous cake pattern is one such example usage of abstract types.

I read the following as well (wish I could remember where):

Abstract types are good when extending and there will be concrete subclasses. Param type good for when a type is useful without extension but can handle several types.

A simpler example is examined here. It is loosely based on a real world usecase.
The example below is contrived so that it is smaller than the actual usecase, so consider the design and not the fact that the example could be easier done with other examples. In the real scenario this design reduced the lines of duplicated code from around 500 to 10.

The example below shows how a Traversable like object can be created from InputStreams and Readers. The important aspect is that the type signature of Foreach does not leak information about the implementation. Users of a Foreach object don't care whether it is backed onto an InputStream or Reader. They just care about the type of object contained.

I am leaving this already long post here. The next post will investigate different ways you can get in trouble trying to implement using abstract types.

Search This Blog

About Me

Jesse EicharI am a senior software developer at Camptocamp SA (Swiss office) and specialize in open-source geospatial Java projects. I am a member of the uDig steering committee and a contributor to Geotools and Mapfish. In addition I regularly work with Geoserver and Geonetwork.

In my free time I am a Scala enthusiast. I am working on the Scala IO incubator project and WebSpecs a Specs2 based testing framework for webapplications.