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
andhashCode
- 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 =@transient 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 thelog
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 } } }
- Except null-values, unlike
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 astell
.?
sends a message asynchronously and returns a Future representing a possible reply. Also known asask
.
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])
Learning resources
- This is a great one: The Neophytes Guide To Scala, Part 14: Actor approach to concurreny.
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.