Scala Explained!

Scala tips and code snippets.

General syntax

This section explains Scala language basic structures like: values, variables, blocks, loops, pattern matching, exceptions handling, lazy values, etc…

Scala functions and classes are explained in their own sections.

Comments

A comment is a block of text that is not executed:

// inline comment

/* multi-line
comment */

/** Scaladoc
 *
 *  comment
 */

Values & variables

Explicit and implicit typing

Value and variable types can be omitted most of the time because Scala can infer them. This mechanism is called type inference:

// Define values with and without explicit types.

// the value's type `String` is explicitly set
val name: String = "Hanna"

// the value's type `String` is inferred from the content's type
val nickname = "Nana"

It is recommended to explicitly write the type when the lack of type hurts the usability of a the code. Example: a library public API.

Statements and blocks

if-else conditional block

A conditional execution statement is written using a if-else block:

// Check if math still works...

val x =
  if (2 > 1)
    "math works"
  else
    "math is broken"

println(x)
// --> math works

while and do-while loops

Both loops run a statement while a condition is satisfied.

Most of the time, collection methods: map, flatMap, foreach, etc… can be used instead of loops.

for comprehensions

You can use for comprehensions to iterate other one or multiple collections.

A for comprehension can also simplify the use of a monad: an advanced concept of functional programming. Learn more here (external link)

Pattern matching with match and case

Pattern matching is one of Scala’s most interesting features. It allows you to check if a value matches one of the defined patterns and runs the associated block of code.

Pattern matching looks like an advanced version of C language’s switch-case, or an alternative to long lists of if-else statements.

Match values

Match types

It is possible to match a value according to its type:

// Print comments about books.

sealed abstract class Book
case class Poem() extends Book
case class Dictionary() extends Book {
  def nbWords = 300000
}

def getComments(book: Book): String = book match {
  // matching `Book` instances according to their actual type
  case dict: Dictionary => s"a ${ dict.nbWords } words dictionary"
  case poem: Poem => s"just a book"
  // `case _` default pattern can be ignored thanks to the `sealed` modifier
}

println(getComments(Poem()))
// --> just a book

println(getComments(Dictionary()))
// --> a 300000 words dictionary

Match and unpack a case class

A case class is a class that has attributes. These attributes can be accessed through pattern matching:

// Display information about music artists.

abstract class Artist
case class Producer(realName: String, style: String) extends Artist
case class Singer(artistName: String, playInstrument: Boolean) extends Artist
case class GuitarPlayer(artistName: String, guitarType: String) extends Artist

def introduce(artist: Artist): String = artist match {
  // match `Producer` type and unpack all its attributes
  case Producer(realName, style) => s"The famous $style producer $realName"

  // match `Singer` type and unpack `artistName` attribute only
  case Singer(artistName, _) => s"The super star $artistName"

  // match `GuitarPlayer` only, no attribute is unpacked
  case GuitarPlayer(_, _) => "A great guitar player"
}

val producer = Producer("Jack Back", style = "Jazz")
val singer = Singer("Tororo", false)
val player = GuitarPlayer("Golden Finger", "expensive")

println(introduce(producer))
// --> The famous Jazz producer Jack Back

println(introduce(singer))
// --> The super star Tororo

println(introduce(player))
// --> A great guitar player

Combining multiple patterns

Multiple patterns can be combined with | operator:

// Tell if a number is in a defined interval.

def tinyEvenNumber(x: Int): String = x match {
  // match multiple patterns
  case 0 | 2 | 4 | 6 | 8 => "an even number inside the interval [0, 10["
  case _ => "other"
}

println(tinyEvenNumber(4))
// --> an even number inside the interval [0, 10[

println(tinyEvenNumber(33))
// --> other

Pattern guards

Pattern guards are extra conditions that are added to patterns. They are represented by a if condition with no else:

// Delete files and folders.

abstract class FileSystemItem
case class File(name: String) extends FileSystemItem
case class Dir(
  name: String,
  content: List[FileSystemItem]) extends FileSystemItem

def remove(item: FileSystemItem): Unit = {
  println(
    item match {
      // deleting an empty directory,
      // using a `if-guard` to ensure that the directory is empty
      case Dir(_, content) if content.isEmpty => "directory deleted"

      // deleting a file
      case File(_) => "file deleted"

      // non empty directory and other cases...
      case _ => "it is not safe to delete this item"
    }
  )
}

val file = File("file.txt")
remove(file)
// --> file deleted

val emptyDir = Dir("empty-dir", List())
remove(emptyDir)
// --> directory deleted

val nonEmptyDir = Dir("full-dir", List(File("file.txt")))
remove(nonEmptyDir)
// --> it is not safe to delete this item

Match regular expressions

In Scala, String values can be converted to regular expressions using String.r method.

Anonymous functions with pattern matching

Anonymous functions can use pattern matching on their parameters while omitting the match keyword:

// Read the temperature and tell if its hot or cold.

// the following function is a shorter version of:
// `def calorimeter(x: Int) => String = x match { ... }`
def calorimeter: (Int) => String = {
  case temperature if temperature > 30 => "hot"
  case _ => "cold"
}

println(s"it is ${ calorimeter(34) }")
// --> it is hot

Manage exceptions with throw and try-catch-finally

Here is how an exception can be thrown and handled in Scala:

// Running computations on a remote "cloud" system.

// create a custom exception class
final case class CloudError(message: String = "") extends Exception(message)

try {
  // run code that may throw an exception
  println("running computations on the cloud")
  // throw an exception
  throw CloudError("cannot connect to the cloud")
} catch {
  // catch `CloudError` exception
  case CloudError(message) => println(s"cloud error: $message")
  // catch other errors
  case _ : Throwable => println("error: something happened")
} finally {
  // run this code whether an exception has been caught or not
  println("cloud computations done")
}

/* -->
running computations on the cloud
cloud error: cannot connect to the cloud
cloud computations done
*/

import names

import statement is used to import names from packages. The imported names are then available in the current name scope:

// Import names from packages.

// import all names defined in `scala.sys` package
import scala.sys._

// import `Pi` value
import scala.math.Pi

// import two functions: `sin` and `cos`,
// `cos` function is renamed `cosinus`
import scala.math.{sin, cos => cosinus}

// import all names but `tan`, `tan` name is dropped
import scala.math.{tan => _, _}

lazy values

A lazy value is a value that is evaluated when it is used for the first time. If a lazy value is never used it will not be evaluated:

// A system that waters plants.

// this block will be evaluated when `humidity` will be used
lazy val humidity = {
  println("retrieving humidity from sensors")
  30
}

println("prepare the watering of the plants")

// implicitly evaluate the block above to retrieve `humidity` value
if (humidity < 50)
  println("watering the plants")
else
  println("not wasting water")

/* -->
prepare the watering of the plants
retrieving humidity from sensors
watering the plants
*/

Definition annotations

A Scala annotation is a statement that starts with @ symbol. It associates information with an element definition.

Scala API defines several annotations @deprecated, @tailrec, @throws, etc… To create new annotations it is recommended to implement them in Java language.

// Give π value.

val π: Double = 3.1415

// `deprecated` annotation specifies that the value is deprecated
@deprecated("use π method", "version 2.0")
val pi: Double = 3.14

var radius = 5
var circumference = 2 * pi * radius

// run with -deprecation
// --> warning: method pi is deprecated (since version 2.0): use π method
You can leave a comment or request changes here.

This project is maintained by Y. Somda (yoeo)
Logo by Icon Island — theme by orderedlist — remixed by yoeo