Fundamentals to learn Scala from scratch

A functional programming language

A little over 2 and a half years ago I began to learn Scala coming from a Python background. I has been working as a graduate/associate developer at the time developing in Python, until I was placed into a team who were using Scala predominantly. Before I had joined the team I had never heard of Scala although I did study functional programming in Haskell at university.

In the first few months of Scala development, I was working with some senior Scala developers who found it quite difficult to teach me the basics of the language whilst also working on projects with tight deadlines. This forced me to do some in depth research online which was initially googling how to learn Scala as you may also have. I have summarised my research into topics below these range from how to get started with Scala to the main concepts/fundamentals. This should hopefully help you on your journey to learn Scala.


Getting started with learning Scala

Scala REPL

Scala REPL (“Read-Evaluate-Print-Loop”) is an interpreter that is used in the command line, in most cases it is used to run a few lines of code . In order to use REPL you simply type in scala in your command line (ofcourse you must install scala first). When many developers begin to learn scala they will start of by using REPL, I would also recommend this. Below is an example of how the REPL should look when you type in scala in the terminal.

Scala REPL on macOS – iTerm material theme

If you want to run some scala code without installing scala locally you can use a few websites which work pretty well:

Scala Syntax, fundamentals

Comments

In order to write comments in scala you use // to denote that the text that follows is a comment. Many developers also use // TODO when writing a comment this is commonly used to mark places where some additional code/refactoring is required, but will be implemented later. It is generally said that if you use comments to explain your code then your code is too complicated.

Print line

println() is used to output the result of an expression. In most cases println is used to add temporary logging to a section of code when debugging.

// println examples
println(1) // 1
println(10 + 10) // 20
println("tech grounds") // tech grounds
println("tech-" + "grounds") // tech-grounds

Code blocks

A code block is Scala is denoted by { }. A code block is used when you have more then one expression and you want to combine them, to do this you would surround the code with the braces as shown below.

{
    val x = 10
    val y = 20
    println(x + y)
} // 30

It is not common practice to use code blocks as shown above but instead they are used most commonly used in methods, classes, objects, and logic.

Some more information on code blocks:

  • The definitions inside a code block are only visible from within the code block
  • A code block is an expression
  • The result of the last expression/statement in the code block is the result of the overall code block

Vals/Vars

The val & var keywords are used when naming the result of an expression.

val a: Int = 10 + 20 in this example 10 + 20 is the expression, a is the named result which is called a value and the : Int is the output type of the value, the : is part of the syntax to denote the output type.

Some additional information on vals:

  • val means value
  • Cannot be reassigned, meaning it is immutable
  • Referencing a val does not re-compute it
  • The type of a value can be left out as scala will automatically infer it, but if the val is public then it is recommended to add the type.
// val examples
val a: String = "This is a string"
println(a) // This is a string

val b: Int = 10 + 20
println(b) // 30

val n = 10 + 20 // same as above but without a type, scala will infer the type
println(n) // 30

Some additional information on vars:

  • var means variable
  • Can be reassigned, meaning it is mutable.
  • Most developers tend not to use vars when programming in scala as it is seen as bad practice
// var examples
var y = 2 + 10
y = 2 // This compiles because 'y' is declared with the 'var' keyword
println(y * y) // 4

Imports

Imports are used to access members (classes, traits, functions, etc.) in other packages and are defined by using the keyword import. An import is not required for accessing members of the same package.

// import examples
import database._  // import everything from the database package
import database.Database  // import the class Database
import database.{Database, DatabaseSettings}  // Only imports selected members of the package
import database.{DatabaseSettings => DSettings}  // import and rename incase of clashes or just for convenience 

Some more information on imports:

  • Imports can be used in most places in the code
  • If import names are clashing you can rename one of the imports and use that explicitly

Functions

In scala a function is an expression that takes parameters.

The syntax for a function is as follows (x: String) => x + "!!!", on the left side of the => you have the parameters & on the right side you have the expression which makes use of the parameter(s). Below are a few examples of different functions:

// an anonymous function
(x: String) => x + "!!!" 

// A named function
val divideByTwo = (x: Int) => x / 2
println(divideByTwo(4)) // 2

// A function with more then one parameter
val divide = (x: Int, y: Int) => x / y

// A function with no parameters
val getStrings = () => "hello, world!"
println(getStrings()) // hello, world!

Some more information on functions:

Summary

Everything mentioned above was explained very briefly & should get you started on your journey to learn scala, here are a few links for additional information on the topics above:



Beyond the basic concepts

Methods

As mentioned previously, methods differ slightly from functions, I will go through the main differences and how to write some methods.

A method in Scala is declared with the def keyword, this is followed by the name of the method you are declaring unlike in functions where you do not have the def keyword. The method name is then followed by the parameters just as you have in a function. And lastly the method parameters are followed by the method body. Below are a few examples of different ways of writing a method:

// a method with a return type and a code block.
def divide(x: Int, y: Int): Int = {
  x + y
}

// a bare minimum method without a return type or code block
def divide(x: Int, y: Int) = x + y

// same method as above but with a return type
def divide(x: Int, y: Int): Int = x + y

// a private method, can only be declared within an object, class etc.
object Maths {
    private def divide(x: Int, y: Int) = x + y
}

In the first example above I used a code block, this was unnecessary as code blocks are usually used when a method is more then a single line. When I am writing methods I usually add the return type as by default a method is public.

Some more information on methods:

  • Can be of 3 types: public (by default), private & protected
  • You can have default parameter values in a method e.g. def divide(x: Int = 10, y: Int = 5)

You can read some more detailed explanations on methods here: Methods in Scala

Case classes

A case class in Scala is like a normal class but it has a notable differences, which I will explain. The most useful part of a case class is that when created, it generates code for you. This code generated is as follows:

  • an apply method, this is so that you do not have to use the keyword new when creating a new instance
  • a copy method, this is used when making a copy of a case class, will show some examples
  • an unapply method, this is very useful in match statements
  • a toString method, this is mostly for debugging
  • equals and hashCode methods, these are used to allow instances to be compared

To create a case class you use the following syntax: case class NameofClass(param1: type, param2: type).

The list above explains the main differences but there is also no mutator method which means you cant reassign a parameter in a case class. This is because by default case class parameters are created as vals. Here are some example of case classes and their usage:

// a basic case class and its usage
case class Film(name: String)
val joker = Film("The Joker 2019") 

// as the parameters are vals you can do this
println(joker.name)  // prints "The Joker 2019"
joker.name = "The Joker 2019"

// comparing instances of case classes
val film1 = Film("Dark Knight")
val film2 = Film("Iron man")
val compareThem = film1 == film2 // false

// copying a case class
val film1 = Film("The Joker")
val newFilm = film1.copy(name = "Iron man 2")
println(newfilm.name) // "Iron man 2"

I would recommend doing some more in depth research into case classes as this was the very basics, here are some additional resources:

map() method

The map method in Scala allows you to apply transformations to a collection. A collection in Scala is a data structure that can hold a group of objects. Collections in Scala range from Seq’s, List’s, Sets etc. The map method is key if you are going to work with collections. Below is how map() in Scala works.

// a generic collection
val collection = (object1, object2, object3)

// full syntax
collection.map(x => aFunction(x)) // x is the current object being iterated over

// another way to write the same as above
collection.map(aFunction) // aFunction is a function

// the above will return the following
collection(aFunction(object1), aFunction(object2), aFunction(object3)) // map() applies the function to each object in the collection
Example of map(), the addOne function adds 1 to the value passed in.

There are many different situations where you can use map(), I will leave some examples below to show you a few uses of the map method.

// print the object
val names = List("oliver","bob","john")
names.map(println)

// divide each object by 2
val numbers = Seq(10, 20, 30)
def divideNums(num: Int) = num / 2
numbers.map(divideNums) // short syntax
numbers.map(x => divideNums(x)) // longer syntax

Some additional information on map():

  • map() returns a new collection of the same type as the initial collection
  • map() is a HOF(higher order function), this means it takes a function as a parameter
  • In the Scala this is the definition of map(). def map[B](f: (A) ⇒ B): Traversable[B]

Here is an guide on how to write your own map method: map in Scala

For comprehension & yield

A for comprehension is very similar to a for loop in the sense that it iterates over some objects in a data structure, but in Scala a for comprehension can do much more and can also return a value using yield.

The syntax of a for comprehension is as follows (with yield): for { val <- algorithm } yield val

A for comprehension is usually used for transforming objects in a collection or creating instances of case classes by most developers. Now ill show you a few usages of a for comprehension. Please feel free to try them yourself on REPL.

// simple for comprehension without a yield, simply prints the numbers 1 to 5
for(number <- 1 to 5){
  println(s"This is number: $number")
}

// a for comprehension with a guard statement, 
// will check the current number being iterated is not equal to 1, only then print it 
for(number <- 1 to 5 if number != 1){
  println(s"This is number: $number")
}

// a for comprehension with a yield, takes a list and returns a list 
val numbers = List(1,2,3,4,5)
val number = for {
  num <- numbers if (num == 1 || num == 5)
} yield num
println(s"new numbers = $number")

// a for comprehension with multiple generators, num and newnums
val number = for {
  num <- 1 to 10
  newnums <- 11 to 20
} yield num * newnums
println(s"new numbers = ${number}")

// nested for comprehension
val nums = Seq(1,2,3)
val letters = Seq('a', 'b', 'c')

for (n <- nums) {
    for (c <- letters) {
        println(n + ", " + c)
    }
}

Some additional information on for comprehensions:

  • The type of the collection that is returned is the same type that you were iterating over
  • The initial collection is left unchanged. The for/yield creates a new collection based on to the algorithm you use

Pattern matching

Pattern matching is used for checking a value against a pattern/cases. A pattern match in Scala is a more advanced version of the switch statement in Java, this means it can also be used in place of a a bunch of if/else statements.

The syntax for a match statement contains a value, the match keyword, and a minimum of one case clause. This is a basic example:

// a basic match statement

val k = "hello"

k match {
case "bye" => "wrong"
case "hello" => "correct"
case _ => "unknown"
}

In the above example the case _ is called a “wild card” case for any other case. Here are some more examples:

// 1. a method that makes use of pattern matching
def doSomething(x: Int): Int = x match {
  case 0 => x + 50
  case 50 => x * 50
  case _ => "other"
}
println(doSomething(50)) // 2500
println(doSomething(120)) // 0

// 2. matching on case classes
abstract class Video
case class Movie(name: String, year: Int, available: Boolean) extends Video
case class TVShow(name: String, episode: Int) extends Video

def showVideoDetails(video: Video): String = {
  video match {
    case Movie(name, year, _) =>
      s"The movie is $name & year of release is: $year"
    case TVShow(name, episode) =>
      s"The tv show is $name & Episode: $episode"
    case _ => "error unknown input"
  }
}

val aMovie = Movie("the dark knight", 2008, false) // calling the method with this movie below
showVideoDetails(aMovie) // "The movie is the dark knight & year of release is: 2008"

// 3. pattern matching with guards
def showVideoDetails(video: Video, videoYear: Int): String = {
  video match {
    case Movie(name, year, _) if videoYear == year => // This will only match if the year matches
      s"The movie is $name & year of release is: $year"
    case TVShow(name, episode) =>
      s"The tv show is $name & Episode: $episode"
    case _ => "error unknown input"
  }
}

// 4. matching on type
def isMovieOrShow(video: Video) = video match {
  case m: Movie => "its a movie"
  case t: TVShow => "its a tv show"
}

Option

Option in Scala is a data structure that allows for optionality. Whenever an expression/computation has the possibility of not returning a value, you can use an Option. The Option has two cases: Some[Type] or None. Some represents a valid value and None represents a missing value.

Below is an example of a very basic Some and None:

// Some
val aSome : Option[Int] = Some(1)

// None
val aNone : Option[Int] = None

Option has some useful methods that you can use to check the values etc, here are some of them:

def isDefined: Boolean - Returns true if the option is an instance of Some, else returns false

def isEmpty: Boolean - Returns true if the option is None, false otherwise.

def get: X - Returns the option’s value.

An example of a good use case to use Option is when you are retrieving data from a database. Lets say you query for a user in the db and they do not exist you can return None and then handle it elsewhere, whereas before you would have to deal with the exception right away. Options can also be used in a pattern match, here is a good example of that:

abstract class Video

case class Movie(name: String, year: Int, available: Boolean) extends Video

case class TVShow(name: String, episode: Int) extends Video

def showMovies(video: Video): Option[String] = {
  video match {
    case Movie(name, year, _) =>
      Some(s"The movie is $name & year of release is: $year")
    case TVShow(_, _) =>
      None
    case _ => None
  }
}

// calling the method with a movie
val aMovie = Movie("the dark knight", 2008, false)
showMovies(aMovie) // Some("The movie is the dark knight & year of release is: 2008")

// calling the method with a TV show
val aTVShow = TVShow("Friends", 10)
println(showMovies(aTVShow)) // None

// handling the option (some and none)
// this method will check if the value is a some or none and handle them
def handleResult(videos: List[Video]): List[String] = {
  val result: List[Option[String]] = videos.map(showMovies)

  result map {
    case Some(value) => value
    case None => "not a movie"
  }
}

// a list of videos
val listOfVideos = List(TVShow("Friends", 10), Movie("the dark knight", 2008, false))

// result, as you can see the tv show was handled and returned the "not a movie" string
handleResult(listOfVideos) // List(not a movie, The movie is the dark knight & year of release is: 2008)

Summary

Everything mentioned above was explained very briefly & should get you started on your journey to learn Scala, here are a few links for additional information on the topics above:



SBT – Build tool

SBT is an interactive build tool used for Scala and other languages such as Java. I use sbt for all my project but when developing in Scala you can also use other build tools such as Maven. You can install sbt by following this guide.

Here are the main features of sbt (from the docs):

  • Little or no configuration required for simple projects
  • Scala-based build definition that can use the full flexibility of Scala code
  • Accurate incremental recompilation using information extracted from the compiler
  • Continuous compilation and testing with triggered execution
  • Packages and publishes jars
  • Generates documentation with scaladoc
  • Supports testing with ScalaCheck, specs, and ScalaTest.
  • Starts the Scala REPL with project classes and dependencies on the classpath
  • Modularization supported with sub-projects
  • External project support (list a git repository as a dependency!)
  • Parallel task execution, including parallel test execution
  • Library management support: inline declarations, external Ivy or Maven configuration files, or manual management

The main component of a Scala project is the build.sbt file (known as the build definition), it allows you to define your Scala project. It gives you the ability to add dependencies(other libraries), your sbt version and various other settings. Here is a basic build.sbt file:

name := "project_name"

version := "0.1"

scalaVersion := "2.12.0"

sbtVersion := "0.13"

scalacOptions += "-Ypartial-unification"

scalacOptions += "-language:higherKinds"

libraryDependencies ++= Seq(
  "org.typelevel" %% "cats-core" % "1.6.0"
)

IDE – IntelliJ

An IDE (integrated development environment) is an application that provides extensive facilities to software developers/programmers. An IDE usually contains the following: source code editor, debugger, compiler/interpreter and a terminal.

I mainly use the IntelliJ IDE when developing in Scala. This is because its the easiest to setup and is the most feature rich right now. I like the features and plugins it has such as Scalafmt which formats the code you write, to make it more readable. I also used intelliJ to write a lot of the code in this tutorial. IntelliJ works on Windows and Mac, I use Mac mainly but as home I tend to use Windows, but it works amazingly on both.

Here is snippet of how it looks like:

IntelliJ on Windows

Checkout my post on the top 5 Scala books to take you from beginner to expert. These are some of the books I have read in the past and really helped me improve my knowledge to take me to where I am today.


3 thoughts on “Fundamentals to learn Scala from scratch”

  1. Pingback: Simple Gatling load test example in Scala - Tech-Grounds

Leave a Reply

Your email address will not be published. Required fields are marked *