KEMBAR78
A Scala tutorial | PDF
A 30-min Scala tutorial
Topics
• Introduction

• Scala core features

• Scala expressiveness 

• Scala case classes

• Scala pattern matching

• Scala implicit classes

• Scala higher-order functions

• Scala Option monad

• Summary
Introduction
• Scala Introduced in January 2004 by Martin Odersky

• Scala supports both OOP and FP paradigms

• Scala JVM based and statically typed language

• Well known products implemented in Scala - Apache
Spark, Apache Kafka, Apache Flink

• Scala scores pretty well in programming language
popularity rankings (13)
Concise syntax
Scala is designed to be concise, many
of Scala’s design decisions aimed to
address the verbosity of Java. For
example here is the code that defines
new class UserInfo.
This class has two properties. The first
one is read-write property Name and
the second one is BirthDate which is
read-only.
JAVA
import org.joda.time.LocalDate;
class UserInfo {
private String name;
private LocalDate birthDate;
public UserInfo(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getBirthDate() {
return birthDate;
}
}
Concise syntax
And now the same code in Scala.
Just a bit about syntax, in Scala
variable name, comes first and then
comes variable type.
A var is a variable. It’s a mutable
reference to a value. On the other
hand, val is a value, an immutable
reference. In this single line, you
can see the expressiveness of Scala.
SCALA
import org.joda.time.LocalDate
class UserInfo(var name: String, val birthDate: LocalDate)
Case classes
When it comes to comparing objects
in Java, it compares references. For
example, the following code will
return false.
But sometimes we would like to
compare objects by underlying
values and not by reference. For
example comparing two different
objects that represent the same user
should return true.
In order to do it in Java, we have to
implement equals and hashcode
methods. 
Compare users
public static boolean compareUsers() {
LocalDate date = LocalDate.now();
UserInfo a = new UserInfo("John", date);
UserInfo b = new UserInfo("John", date);
return (a == b);
}
Case classes
In Scala, we have case classes.
Case class automatically defines
equals, hashcode and getters for
constructor arguments.
And most important it can be used
in pattern matching.
So by implementing UserInfo as a
Case Class, we will get the
following result:
import org.joda.time.LocalDate
case class UserInfo(var name: String, birthDate: LocalDate)
val date = LocalDate.now()
val a = UserInfo("John", date)
val b = UserInfo("John", date)
a == b // returns True
Pattern matching
Pattern matching is a mechanism
for checking a value against a
pattern.
You can think about Scala’s pattern
matching as a more powerful
version of switch statement in
Java. A match expression has a
value, the match keyword, and at
least one case clause.
In Scala it is possible to perform
pattern matching on types
The more powerful case - matching
integers sequence
// example 1
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
// example 2
def matchOnType(x: Any): String = x match {
case x: Int => s"$x is integer"
case x: String => s"$x is string"
case _ => "unknown type"
}
matchOnType(1) // returns 1 is integer
matchOnType("1") // returns 1 is string
// example 3
def matchList(x: List[Int]): String = x match {
case List(_) => "a single element list"
case List(_, _) => "a two elements list"
case List(1, _*) => "a list starting with 1"
}
matchList(List(3)) // returns a single elements list
matchList(List(0, 1)) // returns a two elements list
matchList(List(1, 0, 0)) // returns a list starting with 1
Pattern matching
And the most powerful case: a
pattern matching on case classes.
import org.joda.time.LocalDate
case class UserInfo(var name: String, birthDate: LocalDate)
def isUserJohn(x: Any): Boolean = x match {
case UserInfo("John", _) => true
case _ => false
}
val list = List(
"wrong user type",
UserInfo("Ben", LocalDate.now()),
UserInfo("John", LocalDate.now()))
list.filter(isUserJohn) // list with UserInfo John
Implicit classes
The implicit keyword makes the class’s
primary constructor available for implicit
conversions when the class is in scope.
Assume that you were asked to
extend UserInfo class with
a getAge method. So there are two
options. Create a separate library
of UserInfo utility methods, like
an UserInfoUtil class or to create a
subclass which inherits all attributes and
methods of UserInfo and extends it
with getAge.
In Scala, you can add your own
behavior(s) by using implicit class.
object Extensions {
implicit class UserInfoExt(user: UserInfo) {
def getAge: Int = {
Years.yearsBetween(
LocalDate.now(), user.birthDate).getYears
}
}
}
import Extensions._
val user = UserInfo("John", LocalDate.now())
user.getAge
Higher-order functions
Scala allows the definition of higher-
order functions. These are functions
that take other functions as
parameters, or whose result is a
function. The classic examples of
higher-order functions
are map and filter.
Map applies a function on all
elements of a collection. For
example, let’s multiply by 2 each
element of a given list
Filter creates a list of elements for
which a function returns true. Here
is a short and concise example:
val list = List(1, 2, 3, 4, 5)
// map
def multiply(x: Int): Int = x * 2
list.map(multiply)
// map with lambda
list.map(i => i * 2)
// filter
def isEven(x: Int): Boolean = x % 2 == 0
list.filter(isEven)
// filter with lambda
list.filter(i => i % 2 == 0)
// filter with lambda 2
list.filter(_ % 2 == 0)
// higher order function definition
def apply(f: Int => String, x: Int): String = f(x)
def printInt(x: Int): String = s"Printing integer $x"
apply(printInt, 3)
Option Monad
Monad is a design pattern that allows
structuring programs generically
while automating away boilerplate
code needed by the program logic and
provides an easy way for composing
and sequencing operations on some
contained value(s).
Scala Option Monad can help to deal
with ‘coping with errors’ problem that
is very common in many languages.
The goal is to
implement getSelectedTextLength m
ethod which returns the length of
selected text on the active page,
otherwise, it returns 0. The simple
approach is to implement it as
follows:
class Document {
def getActivePage: Page = ???
}
class Page {
def getSelectedText: String = ???
}
def getSelectedTextLength(doc: Document): Int = {
if(doc != null) {
val page = doc.getActivePage
if(page != null){
val text = page.getSelectedText
if(text != null){
text.length
}
else 0
}
else 0
}
else 0
}
Option Monad
Such implementation is OK, but it
has nested indentation,
aka pyramid of doom. There is
another way to implement it:
class Document {
def getActivePage: Page = ???
}
class Page {
def getSelectedText: String = ???
}
def getSelectedTextLength(doc: Document): Int = {
if(doc == null)
return 0
val page = doc.getActivePage
if(page == null)
return 0
val text = page.getSelectedText
if(text == null)
return 0
text.length
}
Option Monad
It looks flat and clean but has if (x
== null) return 0 a pattern which
appears many times. We can
simplify it by using exceptions.
This version looks good but has
some problem though.
If NullPointerExceptionis thrown
from getActivePage or getSelectedT
ext it will be unintentionally
handled by our code and by doing
so, our code will hide the potential
bug.
class Document {
def getActivePage: Page = ???
}
class Page {
def getSelectedText: String = ???
}
def getSelectedTextLength(doc: Document): Int = {
try {
doc.getActivePage.getSelectedText.length
}
catch {
case _: NullPointerException => 0
case e: Exception => throw e
}
}
Option Monad
In Scala it can be solved by
using Option Monad. Option
monad wrappes value of any given
type and have two specific
implementations: None when a
value does not exist (null) or Some
for the existing value, plus it defines
flatMap operation which allows
composing operations sequence
together.
trait Option[A] {
def flatMap[B](f: A => Option[B]): Option[B]
}
case class None[A]() extends Option[A] {
def flatMap[B](f: A => Option[B]): Option[B] = new None
}
case class Some[A](a: A) extends Option[A] {
def flatMap[B](f: A => Option[B]): Option[B] = {
f(a)
}
}
Option Monad
In Scala it can be solved by
using Option Monad. Option
monad wrappes value of any given
type and have two specific
implementations: None when a
value does not exist (null) or Some
for the existing value, plus it defines
flatMap operation which allows
composing operations sequence
together.
trait Option[A] {
def flatMap[B](f: A => Option[B]): Option[B]
}
case class None[A]() extends Option[A] {
def flatMap[B](f: A => Option[B]): Option[B] = new None
}
case class Some[A](a: A) extends Option[A] {
def flatMap[B](f: A => Option[B]): Option[B] = {
f(a)
}
}
Option Monad
In Scala it can be solved by
using Option Monad. Option
monad wrappes value of any given
type and have two specific
implementations: None when a
value does not exist (null) or Some
for the existing value, plus it defines
flatMap operation which allows
composing operations sequence
together.
trait Option[A] {
def flatMap[B](f: A => Option[B]): Option[B]
}
case class None[A]() extends Option[A] {
def flatMap[B](f: A => Option[B]): Option[B] = new None
}
case class Some[A](a: A) extends Option[A] {
def flatMap[B](f: A => Option[B]): Option[B] = {
f(a)
}
}
Option Monad
So by using Option monad, we can
reimplement the code as follows:
class Page {
def getSelectedText: Option[String] = None
}
class Document {
def getActivePage: Option[Page] = None
}
def getSelectedTextLength(doc: Option[Document]): Int = {
doc
.flatMap(_.getActivePage)
.flatMap(_.getSelectedText)
.map(_.length).getOrElse(0)
}
getSelectedTextLength(Some(new Document))
Summary
Scala isn’t perfect, it comes with its own limitation and drawbacks. It has a steep learning
curve because its principles are based on mathematical type theory which is hard to master.
When not maintained carefully, the Scala source code is hard to read and understand. Some
big tech companies like LinkedIn, Twitter, Yammer reported that they are abandoning or
decreasing their dependency on Scala.
On the other hand, Scala is flexible, productive and comes with a pretty rich toolbox.
Compatibility with Java allows Scala developers to enjoy of rich Java ecosystem of libraries,
frameworks, and tools. All these facts make Scala my language of choice when it comes to
developing Big Data pipelines or high load server backends.

A Scala tutorial

  • 1.
  • 2.
    Topics • Introduction • Scalacore features • Scala expressiveness • Scala case classes • Scala pattern matching • Scala implicit classes • Scala higher-order functions • Scala Option monad • Summary
  • 3.
    Introduction • Scala Introducedin January 2004 by Martin Odersky • Scala supports both OOP and FP paradigms • Scala JVM based and statically typed language • Well known products implemented in Scala - Apache Spark, Apache Kafka, Apache Flink • Scala scores pretty well in programming language popularity rankings (13)
  • 5.
    Concise syntax Scala isdesigned to be concise, many of Scala’s design decisions aimed to address the verbosity of Java. For example here is the code that defines new class UserInfo. This class has two properties. The first one is read-write property Name and the second one is BirthDate which is read-only. JAVA import org.joda.time.LocalDate; class UserInfo { private String name; private LocalDate birthDate; public UserInfo(String name, LocalDate birthDate) { this.name = name; this.birthDate = birthDate; } public String getName() { return name; } public void setName(String name) { this.name = name; } public LocalDate getBirthDate() { return birthDate; } }
  • 6.
    Concise syntax And nowthe same code in Scala. Just a bit about syntax, in Scala variable name, comes first and then comes variable type. A var is a variable. It’s a mutable reference to a value. On the other hand, val is a value, an immutable reference. In this single line, you can see the expressiveness of Scala. SCALA import org.joda.time.LocalDate class UserInfo(var name: String, val birthDate: LocalDate)
  • 7.
    Case classes When itcomes to comparing objects in Java, it compares references. For example, the following code will return false. But sometimes we would like to compare objects by underlying values and not by reference. For example comparing two different objects that represent the same user should return true. In order to do it in Java, we have to implement equals and hashcode methods.  Compare users public static boolean compareUsers() { LocalDate date = LocalDate.now(); UserInfo a = new UserInfo("John", date); UserInfo b = new UserInfo("John", date); return (a == b); }
  • 8.
    Case classes In Scala,we have case classes. Case class automatically defines equals, hashcode and getters for constructor arguments. And most important it can be used in pattern matching. So by implementing UserInfo as a Case Class, we will get the following result: import org.joda.time.LocalDate case class UserInfo(var name: String, birthDate: LocalDate) val date = LocalDate.now() val a = UserInfo("John", date) val b = UserInfo("John", date) a == b // returns True
  • 9.
    Pattern matching Pattern matchingis a mechanism for checking a value against a pattern. You can think about Scala’s pattern matching as a more powerful version of switch statement in Java. A match expression has a value, the match keyword, and at least one case clause. In Scala it is possible to perform pattern matching on types The more powerful case - matching integers sequence // example 1 def matchTest(x: Int): String = x match { case 1 => "one" case 2 => "two" case _ => "many" } // example 2 def matchOnType(x: Any): String = x match { case x: Int => s"$x is integer" case x: String => s"$x is string" case _ => "unknown type" } matchOnType(1) // returns 1 is integer matchOnType("1") // returns 1 is string // example 3 def matchList(x: List[Int]): String = x match { case List(_) => "a single element list" case List(_, _) => "a two elements list" case List(1, _*) => "a list starting with 1" } matchList(List(3)) // returns a single elements list matchList(List(0, 1)) // returns a two elements list matchList(List(1, 0, 0)) // returns a list starting with 1
  • 10.
    Pattern matching And themost powerful case: a pattern matching on case classes. import org.joda.time.LocalDate case class UserInfo(var name: String, birthDate: LocalDate) def isUserJohn(x: Any): Boolean = x match { case UserInfo("John", _) => true case _ => false } val list = List( "wrong user type", UserInfo("Ben", LocalDate.now()), UserInfo("John", LocalDate.now())) list.filter(isUserJohn) // list with UserInfo John
  • 11.
    Implicit classes The implicit keyword makesthe class’s primary constructor available for implicit conversions when the class is in scope. Assume that you were asked to extend UserInfo class with a getAge method. So there are two options. Create a separate library of UserInfo utility methods, like an UserInfoUtil class or to create a subclass which inherits all attributes and methods of UserInfo and extends it with getAge. In Scala, you can add your own behavior(s) by using implicit class. object Extensions { implicit class UserInfoExt(user: UserInfo) { def getAge: Int = { Years.yearsBetween( LocalDate.now(), user.birthDate).getYears } } } import Extensions._ val user = UserInfo("John", LocalDate.now()) user.getAge
  • 12.
    Higher-order functions Scala allowsthe definition of higher- order functions. These are functions that take other functions as parameters, or whose result is a function. The classic examples of higher-order functions are map and filter. Map applies a function on all elements of a collection. For example, let’s multiply by 2 each element of a given list Filter creates a list of elements for which a function returns true. Here is a short and concise example: val list = List(1, 2, 3, 4, 5) // map def multiply(x: Int): Int = x * 2 list.map(multiply) // map with lambda list.map(i => i * 2) // filter def isEven(x: Int): Boolean = x % 2 == 0 list.filter(isEven) // filter with lambda list.filter(i => i % 2 == 0) // filter with lambda 2 list.filter(_ % 2 == 0) // higher order function definition def apply(f: Int => String, x: Int): String = f(x) def printInt(x: Int): String = s"Printing integer $x" apply(printInt, 3)
  • 13.
    Option Monad Monad is adesign pattern that allows structuring programs generically while automating away boilerplate code needed by the program logic and provides an easy way for composing and sequencing operations on some contained value(s). Scala Option Monad can help to deal with ‘coping with errors’ problem that is very common in many languages. The goal is to implement getSelectedTextLength m ethod which returns the length of selected text on the active page, otherwise, it returns 0. The simple approach is to implement it as follows: class Document { def getActivePage: Page = ??? } class Page { def getSelectedText: String = ??? } def getSelectedTextLength(doc: Document): Int = { if(doc != null) { val page = doc.getActivePage if(page != null){ val text = page.getSelectedText if(text != null){ text.length } else 0 } else 0 } else 0 }
  • 14.
    Option Monad Such implementationis OK, but it has nested indentation, aka pyramid of doom. There is another way to implement it: class Document { def getActivePage: Page = ??? } class Page { def getSelectedText: String = ??? } def getSelectedTextLength(doc: Document): Int = { if(doc == null) return 0 val page = doc.getActivePage if(page == null) return 0 val text = page.getSelectedText if(text == null) return 0 text.length }
  • 15.
    Option Monad It looksflat and clean but has if (x == null) return 0 a pattern which appears many times. We can simplify it by using exceptions. This version looks good but has some problem though. If NullPointerExceptionis thrown from getActivePage or getSelectedT ext it will be unintentionally handled by our code and by doing so, our code will hide the potential bug. class Document { def getActivePage: Page = ??? } class Page { def getSelectedText: String = ??? } def getSelectedTextLength(doc: Document): Int = { try { doc.getActivePage.getSelectedText.length } catch { case _: NullPointerException => 0 case e: Exception => throw e } }
  • 16.
    Option Monad In Scalait can be solved by using Option Monad. Option monad wrappes value of any given type and have two specific implementations: None when a value does not exist (null) or Some for the existing value, plus it defines flatMap operation which allows composing operations sequence together. trait Option[A] { def flatMap[B](f: A => Option[B]): Option[B] } case class None[A]() extends Option[A] { def flatMap[B](f: A => Option[B]): Option[B] = new None } case class Some[A](a: A) extends Option[A] { def flatMap[B](f: A => Option[B]): Option[B] = { f(a) } }
  • 17.
    Option Monad In Scalait can be solved by using Option Monad. Option monad wrappes value of any given type and have two specific implementations: None when a value does not exist (null) or Some for the existing value, plus it defines flatMap operation which allows composing operations sequence together. trait Option[A] { def flatMap[B](f: A => Option[B]): Option[B] } case class None[A]() extends Option[A] { def flatMap[B](f: A => Option[B]): Option[B] = new None } case class Some[A](a: A) extends Option[A] { def flatMap[B](f: A => Option[B]): Option[B] = { f(a) } }
  • 18.
    Option Monad In Scalait can be solved by using Option Monad. Option monad wrappes value of any given type and have two specific implementations: None when a value does not exist (null) or Some for the existing value, plus it defines flatMap operation which allows composing operations sequence together. trait Option[A] { def flatMap[B](f: A => Option[B]): Option[B] } case class None[A]() extends Option[A] { def flatMap[B](f: A => Option[B]): Option[B] = new None } case class Some[A](a: A) extends Option[A] { def flatMap[B](f: A => Option[B]): Option[B] = { f(a) } }
  • 19.
    Option Monad So byusing Option monad, we can reimplement the code as follows: class Page { def getSelectedText: Option[String] = None } class Document { def getActivePage: Option[Page] = None } def getSelectedTextLength(doc: Option[Document]): Int = { doc .flatMap(_.getActivePage) .flatMap(_.getSelectedText) .map(_.length).getOrElse(0) } getSelectedTextLength(Some(new Document))
  • 20.
    Summary Scala isn’t perfect,it comes with its own limitation and drawbacks. It has a steep learning curve because its principles are based on mathematical type theory which is hard to master. When not maintained carefully, the Scala source code is hard to read and understand. Some big tech companies like LinkedIn, Twitter, Yammer reported that they are abandoning or decreasing their dependency on Scala. On the other hand, Scala is flexible, productive and comes with a pretty rich toolbox. Compatibility with Java allows Scala developers to enjoy of rich Java ecosystem of libraries, frameworks, and tools. All these facts make Scala my language of choice when it comes to developing Big Data pipelines or high load server backends.