Scala for the busy Java programmer:
Type Aliasing
In Akka, you'll see this in the Actor object:
type Receive = PartialFunction[Any, Unit]
Scala Hierarchy
Absolutely everything extends Any.
The Scala equivalents of primitives in Java extends AnyVal. All other types extend AnyRef.
Null is the base class for all AnyRef.
Nothing is the base class for Null and all AnyVal.
Terminology
"Anonymous functions in source code are called function literals" [1].
Type Aliasing
In Akka, you'll see this in the Actor object:
type Receive = PartialFunction[Any, Unit]
What this means is that where ever we see Receive we mean PartialFunction[Any, Unit]. This is type-aliasing much like in C where, for example, you see:
typedef int jint;
This is from the OpenJDK's jni_x86.h where jint is defined as an int in Linux.
Obviously, running in the JVM, Scala does not need to define the type of something like jint according to architecture. But it's more than a convenience. Scala has something called type members whose effect is a little like parameterized types but promises not to bloat as quickly as generics can.
"In Scala, the inner class is addressed using the expression Outer#Inner instead of Java's Outer.Inner. The '.' syntax is reserved for objects" [1]
Let's use a slightly modified version of Odersky's example [1] to illustrate this:
abstract class Food
class Grass extends Food {
override def toString() = "grass"
}
class DogFood extends Food {
override def toString() = "dog food"
}
abstract class Animal {
type SuitableFood
def eat(food : SuitableFood) = {
println(this.toString + " eats " + food.toString)
}
}
class Dog extends Animal {
type SuitableFood = DogFood
override def toString() = "dog"
}
class Cow extends Animal {
type SuitableFood = Grass
override def toString() = "cow"
}
object Animals {
def main(args : Array[String]) {
val dog = new Dog
val cow = new Cow
val dogFood = new DogFood
val grass = new Grass
dog.eat(dogFood)
cow.eat(grass)
}
def doEat(animal : Animal, food : Animal#SuitableFood) {
/* This fails to compile with:
found : food.type (with underlying type com.phenry.scala.Animal#SuitableFood)
required: animal.SuitableFood
*/
animal.eat(food) // <-- does not compile!
}
}
Let's use a slightly modified version of Odersky's example [1] to illustrate this:
abstract class Food
class Grass extends Food {
override def toString() = "grass"
}
class DogFood extends Food {
override def toString() = "dog food"
}
abstract class Animal {
type SuitableFood
def eat(food : SuitableFood) = {
println(this.toString + " eats " + food.toString)
}
}
class Dog extends Animal {
type SuitableFood = DogFood
override def toString() = "dog"
}
class Cow extends Animal {
type SuitableFood = Grass
override def toString() = "cow"
}
object Animals {
def main(args : Array[String]) {
val dog = new Dog
val cow = new Cow
val dogFood = new DogFood
val grass = new Grass
dog.eat(dogFood)
cow.eat(grass)
}
def doEat(animal : Animal, food : Animal#SuitableFood) {
/* This fails to compile with:
found : food.type (with underlying type com.phenry.scala.Animal#SuitableFood)
required: animal.SuitableFood
*/
animal.eat(food) // <-- does not compile!
}
}
The same effect can be achieved with Java generics but that can be subverted via erasure. This appears more solid. More information can be found here.
Emptiness
There are a few ways to represent nothingness in Scala and a good description lives here and here. The main points are that null is just like Java, Nil is an empty List and Unit is just like Java's void.
This last one is interesting. There is only one way to have an instance of Unit and it's represented as (). It's an actual reference (unlike java.lang.Void which cannot be instantiated due to its private constructor) and can be used like this:
def takesVoidToString(arg: () => String) : Unit = {
println("called with " + arg())
}
def main(arg : Array[String]) = {
def fnTakesVoidReturnsString() = "fnTakesVoidReturnsString"
takesVoidToString(fnTakesVoidReturnsString)
.
.
def takesVoidToString(arg: () => String) : Unit = {
println("called with " + arg())
}
def main(arg : Array[String]) = {
def fnTakesVoidReturnsString() = "fnTakesVoidReturnsString"
takesVoidToString(fnTakesVoidReturnsString)
.
.
which in this case prints out:
called with fnTakesVoidReturnsString
Here our function takesVoidToString takes another function that in turn takes no arguments and just returns a String. By calling arg() we implicitly call fnTakesVoidReturnsString's apply method. Without the (), we'd see arg's toString method called and see that it is a Function0.
A note on syntax can be found here.
Finally, Nothing can be used when a function does not terminate properly. For example:
scala> def fnThrowsException = throw new UnsupportedOperationException
fnThrowsException: Nothing
A note on syntax can be found here.
Finally, Nothing can be used when a function does not terminate properly. For example:
scala> def fnThrowsException = throw new UnsupportedOperationException
fnThrowsException: Nothing
That is, the REPL is telling us the return type of fnThrowsException is Nothing.
Partial Application of Functions in Scala
The syntax for a function that can be partially applied looks like this:
def canBePartiallyApplied(count : Int)(f : Int => Int) { // this compiles but we'd like syntactic sugar: (f : Function1[Int, Int])
for (i <- 1 to count) {
println(f(i))
}
}
And we call it thus:
def g(x : Int) = x + 1
def partiallyApplied = app.canBePartiallyApplied(3)(_)
partiallyApplied(g)
Note the underscore to indicate that we'll be filling that in later. This is similar but different to currying. The idea of currying takes one argument and returns a function. Partial application takes two arguments (but also returns a function). See here for more information.
Scala Hierarchy
Absolutely everything extends Any.
The Scala equivalents of primitives in Java extends AnyVal. All other types extend AnyRef.
Null is the base class for all AnyRef.
Nothing is the base class for Null and all AnyVal.
Terminology
"Anonymous functions in source code are called function literals" [1].
No comments:
Post a Comment