Small information nuggets and recipies about Scala
✂️ Scala
Table of Contents
- Run a single ScalaTest spec
- Manually request from Play’s Guice an injected class instance
- Handle spaces correctly in test files paths
- Match AnyVal class instances with Mockito
- Encode domain knowledge with value classes
- Algebraic Data Types (ADT)
- Pre and post conditions in Scala:
- Dangers of catching Throwable in Scala
- Using execution contexts: Scala vs Play
- Useful compiler options
- Mockito verify says “no interactions” but debug steps just fine through the production code
- ScalaTest pesky parenthesis
- ScalaTest with ScalaCheck: Disable shrinking for type
- Convention to call methods or functions without parameters
- Desugar Scala’s syntax
- Parallel vs sequential computation with Futures
- The Scala Language Specification
- Scala Style Guide - Scala Documentation
- Scalacheat - Scala Documentation
(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"
verify(mock).method(KubeName(anyString))
… capture arguments
val wrapper: ArgumentCaptor[String] = forClass(classOf[String])
verify(mock).method(wrapper.capture().asInstanceOf[Wrapper])
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 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
- aka “Pokemon exceptions” (Gotta Catch ‘Em All)
- Scala: Silently catch all exceptions - Stack Overflow
- Catching Throwable in Scala | spiros.blog()
… 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
play.api.libs.concurrent.Execution.Implicits.defaultContext
… 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)
scala.concurrent.ExecutionContext.Implicits.global
Useful compiler options
scalacOptions ++= Seq(
"-target:jvm-1.8",
"-encoding", "UTF-8",
"-unchecked",
"-deprecation",
"-Xfuture",
"-Yno-adapted-args",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen",
"-Ywarn-value-discard",
"-Ywarn-unused")
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 theFuture
completes - Try to verify only after an
Await
, or insidewhenReady
oreventually
ScalaTest pesky parenthesis
ScalaTest with ScalaCheck: Disable shrinking for type
- Fun with scalatests PropertyChecks
- Add support for not shrinking values in GeneratorDrivenPropertyChecks · Issue #584 · scalatest/scalatest
- Solutions to the ScalaCheck problem that shrinking failing values may generate invalid values
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
- e.g. for comprehension
- What’s in a Scala For Comprehension? | Craig Tataryn’s .plan
- syntactic sugar - Getting the desugared part of a Scala for/comprehension expression? - Stack Overflow
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)