✂️ Scala

Small information nuggets and recipies about Scala

(most recent on top)

Run a single ScalaTest spec

sbt 'testOnly *TestNameSpec -- -z "The full name of the test to run"'

Manually request from Play’s Guice an injected class instance

import play.api.inject.NewInstanceInjector
val obj = NewInstanceInjector.instanceOf[AllowedHostsFilter]

Handle spaces correctly in test files paths

- new File(getClass.getResource("/testTheme.zip").getFile)
+ new File(getClass.getResource("/testTheme.zip").toURI.getPath)

Match AnyVal class instances with Mockito

case class Wrapper(value: String) extends AnyVal

… set/check expectations

when(mock.method(KubeName(anyString)) thenReturn "example"

… capture arguments

val wrapper: ArgumentCaptor[String] = forClass(classOf[String])
wrapper.getValue shouldBe "example"

Encode domain knowledge with value classes

case class Id(id: String) extends AnyVal
case class Email(email: String) extends AnyVal
case class Timestamp(ts: Long) extends AnyVal

def f(user: Id, sender: Email, when: Timestamp) = ???

Algebraic Data Types (ADT)

… sum = A + B (meaning A or B; also called “co-product”)

sealed trait Sum
case object A extends Sum
case object B extends Sum

… product = A * B (meaning A and B)

case class Product(foo: A, bar: B)

… example: Something + AnotherThing * SomethingElse

sealed trait Example
case object Something extends Example
case class Other(a: AnotherThing, e: SomethingElse) extends Example

Pre and post conditions in Scala:

  • Scala Standard Library 2.12.2 - scala.Predef

  • Scala preconditions (assert, assume, require, ensuring) | M@X on DEV

    • Useful for dynamically checking invariants, documentation, static code analysis
    • The real difference is only in the intent expressed
  • assert ~> AssertionError
    • predicate which needs to be proven and a static checker can prove at compile time
    • if the condition fails, then a logical error was made within the code
    • elidable via -Xdisable-assertions
  • assume ~> AssertionError
    • an axiom that a static checker can rely upon
    • elidable via -Xdisable-assertions
  • require ~> IllegalArgumentException
    • a formal pre-condition
    • if the condition fails, then the caller of the function is to blame
  • ensuring ~> AssertionError
    • a formal post-condition
    • a guarantee the function is provide with regards to its return value

Dangers of catching Throwable in Scala

… focus only on non fatal exceptions

import scala.util.control.NonFatal
try { f(x) }
catch { case NonFatal(t) => }

… if you really need it, at least propagate Scala’s usage of Throwable for control flow

import scala.util.control.ControlThrowable
try { codeThatMayThrowThrowable() }
catch {
  case e: ControlThrowable => throw e
  case e => handleThrowable(e)

Using execution contexts: Scala vs Play

… use this, which uses an ActorSystem


… don’t use the default, which is a special ForkJoinPool (uses the blocking method to spawn new threads in the pool for potentially blocking code)


Useful compiler options

scalacOptions ++= Seq(
  "-encoding", "UTF-8",

Mockito verify says “no interactions” but debug steps just fine through the production code

  • Check if you’re returning a Future and trying to verify before the Future completes
  • Try to verify only after an Await, or inside whenReady or eventually

ScalaTest pesky parenthesis

ScalaTest with ScalaCheck: Disable shrinking for type

implicit def noShrink[T]: Shrink[T] = Shrink.shrinkAny

Convention to call methods or functions without parameters

reply       // method has no side-effects (pure function)
reply()     // ordinary method call which might also have side-effects

Desugar Scala’s syntax

scala -print file.scala
# list of phases to use
scalac -Xshow-phases

# use `typer` phase to remove syntactic sugar
scala -Xprint:typer -e "for (i <- 0 to 100) yield i"

for comprehension desugared

<- desugars to flatMAp, e.g.

for {
   r1 <- result1
   r2 <- result2
   r3 <- result3
} yield (r1+r2+r3)

… converts into

result1.flatMap(r1 => 
    result2.flatMap(r2 => 
        result3.map(r3 => r1 + r2 + r3)))

Parallel vs sequential computation with Futures

… parallel computation before aggregation

val result1 = future(...)
val result2 = future(...)
val res = for {
   r1 <- result1
   r2 <- result2
} yield (r1+r2)

… sequential computation and aggregation

val res = for {
   r1 <- future(...)
   r2 <- future(...)
} yield (r1+r2)