Scala

Table of Contents

Syntax

NotImplemented

In Scala you write ??? to show that a method is not yet implemented.

sealed trait

A sealed trait is simply a trait which can only be extended from within the file of which it is declared / created.

case

case class differs from regular class in that they get:

  • Pattern matching support. See this section for examples.
  • Default implementations of equals and hashCode
  • Default implementation of Serializable
  • A prettier implementation of toString
  • he small amount of functionality that they get from automatically inheriting from scala.Product

For case object equals and hashCode don't matter much for singletons (unless you do something really degenerate), so you're pretty much just getting pattern matching, serialization, a nice toString , and some methods you probably won't ever use.

Pattern matching

See here for some examples. Especially the section about "Constructor patterns". It's pretty neato!

Functions

When you're writing a "matching function", you do the following:

def someMatch(a: Int) = a match {
  case 42 => println("But what is the question?")
  case _ => println("WRONG!")
}

someMatch(42)

What you don't do is this:

def someMatch(a: Int) = {
  a match {
    // ...
  }
}

Bad programmer!

The @

It enables one to bind a matched pattern to a variable. Consider the following, for instance:

val o: Option[Int] = Some(2)
You can easily extract the content:

o match {
case Some(x) => println(x)
case None =>
}

But what if you wanted not the content of Some, but the option itself? That would be accomplished with this:

o match {
case x @ Some(_) => println(x)
case None =>
}

Note that @ can be used at any level, not just at the top level of the matching.

ClassTag

See the docs for a thourough description.

On the JVM in general we have something called type erasure.

A ClassTag[T] stores the erased class of a given type T, accessible via the runtimeClass field. This is particularly useful for instantiating Arrays whose element types are unknown at compile time.

scala> import scala.reflect._
import scala.reflect._

scala> // Asterix here is simply the same as in Python => multiple arguments
scala> def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*)
mkArray: [T](elems: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]

scala> mkArray(42, 13)
res0: Array[Int] = Array(42, 13)

scala> mkArray("Japan","Brazil","Germany")
res1: Array[String] = Array(Japan, Brazil, Germany)

While the below code would not work, even if we were allowed to compile this. The reason is that the type-information for T would be erased during compilation, and thus the implicit conversion can not be performed during runtime.

def mkArray[T](elems: T*) = Array[T](elems:_*)
mkArray(42, 13)  // <= JVM has no idea how to do this since no information regarding the type is available

Also, it might be interesting to know that making use of the ClassTag by including it in the context bound, is actually just syntactical sugar. It will be compiled into the following:

def mkArray[T](elems: T*)(implicit tag: ClassTag[T]) = Array[T](elems: _*)

So what is actually happening is that we get an additional parameter for the method, tag, which is implicit, i.e. this function assumes that ClassTag is available for class T when this method is called.

lazy

  • Denotes a field that will only be calculated once it is acesssed for the first time and then it's stored for future reference

@transient

  • Denote a field that shall not be serialized
  • Writing [email protected] lazy val log = … = thus means:
    • Do not serialize this object
    • Evaluate it when needed
    • In the case of say a @transient lazy val log = LogManager.getLogger("loggerName") the context of a Spark program will not send the log to each node, but instead have each node calculate and store when needed.

Concurrency

@volatile

  • Field annotated with @volatile are basically wrapped in a Mutex
  • Thus, this field can only be accessed (read or write) by ONE thread at the time

this.synchronize

  • Ensures single thread ownership when being accessed
  • All objects have this method by default
  • Except null-values, unlike @volatile which also works on null-values
class Person(var name: String) {
  def set(changedName: String) {
    this.synchronized {
      name = changedName  // Only accessed by one thread at the time
    }
  }
}

Concepts

Implicits

Implicit parameters

implicit means that the value will be taken from the context in which the function is called. If there is no implicit value of the right type in the scope, it will not compile.

class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

The above code is saying that the addPrefix method will only be valid as long as some variable named p of type Prefixer is to be found in the scope.

My example
case class Observation[T](val value: T)
def toObservation[T](a: T)(implicit factory: T => Observation[T]) : Observation[T] = factory(a)

// Then we create the "factory" method for whatever type we need
implicit def toIntObservation(a: Int) = Observation(a + 1)

// And voilá!
toObservation(10)

Why is this different from creating a "factory method" for each type T that we're interested in?

  • We can do the same with trait, and thus having one "interface" trait which needs to be implemented to fully convert from one type to the other.
  • I suppose it is just a convenience, so we don't have to call these factory methods by name, but instead have them passed to the method which need it automagically.

Implicity conversions

When the compiler finds an expression of the wrong type for the context, it will look for an implicit Function value of a type that will allow it to typecheck. So if an A is required and it finds a B, it will look for an implicit value of type B => A in scope.

implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0

// works the same as

def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)

In this example we can see that when the compiler finds a Double but expects an Int , it checks for a Function to do the conversion, finds it, and does the conversion, leading to the functions above being the same (if you had written them in two different shells/context of course..not in the same).

Type erasure

In programming languages, type erasure refers to the compile-time process by which explicit type annotation are removed from a program, before it is executed at run-time.

Packages

Akka

Syntax

Messages are sent to an Actor through one of the following methods.

  • ! means “fire-and-forget”, e.g. send a message asynchronously and return immediately. Also known as tell .
  • ? sends a message asynchronously and returns a Future representing a possible reply. Also known as ask .

Actors

It is a good idea to provide factory methods on the companion object of each Actor which help keeping the creation of suitable Props as close to the actor definition as possible. This also avoids the pitfalls associated with using the Props.apply(...) method which takes a by-name argument, since within a companion object the given code block will not retain a reference to its enclosing scope:

object DemoActor {
  /**
    *  Create Props for an actor of this type.
    * 
    *  @param magicNumber The magic number to be passed to this actor’s constructor.
    *  @return a Props for creating this actor, which can then be further configured
    *          (e.g. calling `.withDispatcher()` on it)
    */
  def props(magicNumber: Int): Props = Props(new DemoActor(magicNumber))
}

class DemoActor(magicNumber: Int) extends Actor {
  def receive = {
    case x: Int => sender() ! (x + magicNumber)
  }
}

class SomeOtherActor extends Actor {
  // Props(new DemoActor(42)) would not be safe
  context.actorOf(DemoActor.props(42), "demo")
  // ...
}

Props

  • Configuartion class to specify options for the creation of actors
  • Think of it as an immutable and thus freely shareable recipe for creating an actor including associated deployment information

Actor Systems

ActorSystem is simply the top-most supervisor, which every other Actor is in some form "controlled" by.

The way to create an actor at this top-level is the following:

val system = ActorSystem("Name")
val actor = system.actorOf(Props[SomeActorClass])

Resources

sbt

Assembly

Without tests

sbt 'set test in assembly := {}' assembly

Ensime

Troubleshooting

Missing requirements error for libraries which is actually part of the Scala standard lib. using Maven

See this issue on Github. Unfortunately, I haven't even located the scala class path yet o.O except for in the .ensime-cache directory in projects using sbt as a build-tool.