Scala essentials for essentially everything

Cheat sheet: The complete guide to Scala

Markus Hauck
Scala
© Shutterstock / Pinkomelet

Scala fans rejoice! Markus Hauck has created an exhaustive cheat sheet for Scala developers. This masterwork has everything. So dive right in, the water’s fine!

Scala has emerged from the field as a serious contender. Designed to address the common criticisms of Java, Scala is functional, concise, and has a powerful type system. What fascinates me about Scala is the connection between functional programming on the JVM and a very expressive type system. Another bonus is the possible interoperability with Java, especially if you want to migrate from a grown Java codebase to Scala incrementally.

I’ve collected over twenty essential tips and tricks for Scala. If you just need a reference sheet for your own personal use, it’s all covered here. So let’s get started!

If you’d like to see it in its original form, check it out here. If a true cheat sheet is what you want, you won’t be disappointed.

 

Variables & methods

var x = "mutable"
val y = "immutable"

// Initialized once on first access
lazy val z = "lazy"

// Pattern matching on left side
val (one, two) = ("one", 2)

// Simple method
def add(n: Int, m: Int): Int = n + m

// Curried version (one argument per list)
def add(n: Int)(m: Int): Int = n + m

// By-name parameters, evaluates ‘a‘ twice
def twice[A](a: => A) = { a; a }

// Repeated Parameters (Varargs)
def many(ns: Int*): Seq[Int] = ns

// Calling a varargs method with a ‘Seq‘
> many(Seq(1, 2) :_*) 

Strings

val answer: Int = 42
// add ‘s‘ prefix for interpolation
s"The answer is: $answer"

// use ${} for more complex expressions
s"The answer is: ${21 * 2}"

// """ enclose a multiline string
"""
Inside triple quotes there
is no need to escape: \
"""

Regular Expressions

val time = """(\d{1,2}):(\d{2})""".r

> "16:03" match { case time(_*) => "matched!" }

res: String = matched!

// extract matched groups
> "16:12" match { case time(h,m) =>
  s"Hours: $h, minutes: $m"
}

res: String = Hours: 16, minutes: 12 

Classes

// Implicit default constructor
// ‘AnyRef‘ plays the role of ‘Object‘
class Foo extends AnyRef {
  val bar: Int = 42
  def foobar: Boolean = true
}

// Parameterized constructor
class Foo(msg: String) { ... }

// Additional constructor
class Foo(msg: String) {
  def this(n: Int) = this(n.toString)
}

// Can inherit from exactly one class
class Bar extends Foo("foo") 

Objects

Objects hold “static” members. When used as a companion object, it is relevant during implicit search. Objects are singletons.

object Foo {
  val hello: String = "Hello"
  def world: String = "World"
}

// Companion object:
// class and object share name and source file
class Bar
object Bar { ... } 

Traits

The sealed modifier forbids extension of the trait from a different source file than the one it is defined in. This allows exhaustiveness checks while pattern matching.

trait Foo {
  // can have abstract members
  def foo: String

  // can have implementations
  def bar: Unit = println("bar")
}

// Multiple traits can be mixed-in
trait Bar
class Foobar extends Foo with Bar {
  override def foo = "foo"
} 

Case classes

// class definition prefixed with ‘case‘
case class Person(name: String, age: Int) 

What case does:
1. constructor parameters are promoted to fields
2. generates companion object with apply and unapply
3. generates the copy method
4. generates equals, hashCode and toString

For-loop and For-comprehension

For-loops: iterate for side-effects only. For-comprehensions: chain effectful computations.

// for-loop
for (i <- 1 to 10) println(i)

// nested for-loop
for (i <- 1 to 10; j <- 1 to 10) println((i, j))

// for-comprehension
for (i <- 1 to 3; j <- 1 to i) yield i * y

// guards in for-loops and for-comprehensions
for (i <- 1 to 5 if i > 4) yield i

// curly braces for multiline expressions
for {
  i <- 1 to 5
  j <- 1 to i
  if j % 2 == 1
} yield i * j 

Pattern-matching

arg match {
  // Variable Patterns
  case x =>
  // Typed Patterns
  case x: String =>
  // Literal Patterns
  case 42 =>
  // Stable Identifier Patterns
  case ‘foo‘ =>
  // Constructor Patterns
  case Foo(x,y) =>
  // Tuple Patterns
  case (x,y,z) =>
  // Extractor Patterns
  // (See ’Custom Extractors’)
  case NumString(x) =>
  // Pattern Sequences
  case x1 +: x2 +: xs =>
  // Pattern Alternatives
  case true | 42 | "str" =>
  // Pattern Binders
  case pair@(x,y) =>
}	

Custom extractors

// Return Boolean from unapply
object Even {
  def unapply(n: Int): Boolean =
    n % 2 == 0
}
> 41 match { case Even() => ’!’ }
scala.MatchError: 41 ...
> 42 match { case Even() => "even!" }
res: String = "even!"
// Return Option from unapply
object NumString {
  def unapply(s: String): Option[Int] =
    Try(s.toInt).toOption
}
> "42" match { case NumString(n) => n }
res: Int = 42
> "scala" match { case NumString(n) => n }
scala.MatchError: scala
// Alternatively define unapplySeq
object Words {
  def unapplySeq(s: String): Option[Seq[String]] 
    Some(s.split("\\s+").to[Seq])
}
> "foo bar baz" match { case Words(ws) => ws }
res: Seq[String] = Vector("foo", "bar", "baz")
> "test" match { case Words("foo" +: _) => 1 }
scala.MatchError: test 

Type parameters

// Two type parameters A and B
def foo[A, B](a: A, b: B) = ???

// Upper Bound, A has to be a subtype
def foo[A <: String] // Lower Bound, A has to be a supertype def foo[A :> String]

// Context Bound
def foo[A: Ordering](x: A, y: A): Boolean = {
  import Ordering.Implicits._
  x < y
}
// Context Bounds desugar to implicit params
def foo[A](x: A, y: A)(
  implicit evidence$1: Ordering[A]): Boolean 

Variance

Scala

Implicits

There are two categories of places where Scala searches for implicits: identifiers accessible without prefix at the call-site; and implicit scope, all companion objects of classes associated with the implicit’s type.

// implicit values
implicit val n: Int = 42

// implicit conversions
implicit def f(n: Int): String = n.toString

// implicit classes
implicit class Wrapper[A](val a: A) {
  def printMe: Unit = println(a)
}

// implicit parameters
def foo(implicit ec: ExecutionContext) = ??? 

Types

Scala

Option

Replaces null. There is only one obvious reason for a missing value.

Scala

val some: Option[Int] = Some(1)
val none: Option[Int] = None

// getOrElse
> some.getOrElse(42)
res: Int = 1
> none.getOrElse(42)
res: Int = 42

// fold
> some.fold("")(_.toString)
res: String = "1"
> none.fold("")(_.toString)
res: String = ""

// orElse
> some.orElse(none)
res: Option[Int] = Some(1)
> none.orElse(Some(42))
res: Option[Int] = Some(42)

Either

Domain errors that have to be handled, there are multiple reasons for an error.

Scala

val right: Either[String, Int] = Right(1)
val left: Either[String, Int] = Left("oops")

// getOrElse
> right.getOrElse(42)
res: Int = 1
> left.getOrElse(42)
res: Int = 42

// map on left and right side
> right.map(_ + 1)
res: Either[String, Int] = Right(2)
> right.left.map(_.length)
res: Either[Int, Int] = Right(1)
> left.left.map(_.length)
res: Either[Int, Int] = Left(4) 

Try

Interact with Java / Legacy Code where exceptions are thrown, a means of last resort.

Scala

> import scala.util.Try

> Try { "hello".toInt }
res: Try[Int] =
  Failure(java.lang.NumberFormatException)

> Try { "42".toInt }
res: Try[Int] = Success(42)

Collections

Prefer immutable collections, falling back to a var first and use mutable collections as a last resort only. Also, prefer Vector over List.

Warning: Seq by default allows mutable implementations, import scala.collection.immutable.Seq instead.

// Creating a collection via apply:
> List(1, 2, 3)
res: List[Int] = List(1, 2, 3)
> Array(’a’, ’b’)
res: Array[Char] = Array(a, b)
> Map((’a’, 1), (’b’, 2))
res: Map[Char, Int] = Map(a -> 1, b -> 2)

// Importing mutable collections
> import scala.collection.mutable
> mutable.Buffer(1, 2, 3)
res: Buffer[Int] = ArrayBuffer(1, 2, 3) 

Important methods

collect filter then map in one
collectFirst find with pattern matching
count count elements with predicate
exists check predicate satisfied >= 1
find find element with predicate
filter filter elements with predicate
flatMap map a function producing a collection
foldLeft recursive traversal
foldRight for right associative ops
forall check predicate holds for all elements
map transform each element
slice select an interval of elements
take, drop  remove elements from front/back
to[Col]  convert to collection

Futures

Don’t blindly import Scala’s default ExecutionContext, it is optimized for CPU-bound tasks!

> import scala.concurrent._, duration._
> import ExecutionContext.Implicits.global

// Creating an asynchronous computation
> Future { 5 * 2 }
res1: Future[Int] = Future(<not completed>)

// Modify result with a pure function
> res1.map((n: Int) => n + 11)
res2: Future[Int] = Future(<not completed>)

// Use flatMap to chain Futures
> res2.flatMap((n: Int) => Future { n * 2 })
res3: Future[Int] = Future(<not completed>)

// Register callbacks
> res3.onComplete {
  case Success(r) => println(s"Success: $r")
  case Failure(e) => println(s"Failure: $e")
}

// Block thread for result (anti-pattern)
> Await.result(res3, 1.second)
res4: Int = 42 

Duration DSL

> import scala.concurrent.duration._
> 5.seconds
res: FiniteDuration = 5 seconds
> 2.hours
res: FiniteDuration = 2 hours 

Scala Plugin for IntelliJ IDEA

Scala

 

To see more cheat sheets, download the latest issue of JAX Magazine:

Cheat sheets are a godsend when you don’t know something – and that will happen no matter how well-prepared you are. Programming is not a “who does better” contest and it’s just as hard as learning a new language: it takes time until you get the hang of it and master all its facets (syntax, semantics and pragmatics). It might get intimidating at times, which is why the following cheat sheets might come in handy: Java, JavaScript, Scala, Kotlin, and Dart.

And since we’re here to learn more about programming languages, why not dive deeper into the JVM universe? It will be fun, I promise.

Author

Markus Hauck

Markus Hauck is an IT consultant and Scala trainer for codecentric in Germany. Scala developer by day, functional programmer using scary languages by night. Find him on Twitter @markus1189.


Comments
comments powered by Disqus