Complete guide : Scala programming language

Life is too short to write an introduction. Let's get started!
PLACEHOLDER

Syntax of Scala

Scala has a friendly mixture of C-like syntax and functional programming expressions, but in a more imperative way. Scala code remains short and concise with high readability.
scala syntax

Language Concepts

Following are the interesting facts about the language

Functional Programming (Scala-ish)

Scala is a functional programming language but not purely. By design, it is inspired heavily by Haskell so we see lots of borrowed features as follows.

Algebraic data types

A concept of algebraic data types can easily confuse people. Unlike usual data types, algebraic data types allow following behaviours of variables.

case class MyData(a: Int, b: String, c: List[Int])


trait Shape
case object NoShape extends Shape
case class Circle(r: Double) extends Shape
case class Rect(w: Double, h: Double) extends Shape

Pattern matching

Instead of writing explicit if-else to match certain conditional patterns, Scala offers a more expressive way of writing pattern matching like following.

myArray match {
case Nil => "Empty list"
case head :: _ => head.toString
}

Matching case classes are also easy.

shape match {
case NoShape => None
case Circle(r) => Some(Math.PI  r  r)
case Rect(w,h) => Some(w * h)
case _ => None
}

Mixing more complex cases are also possible with pattern matching.

data match {
case v @ (_ : Type1 | _ : Type2) => process(v)
case v: Type3 if v.isEnabled => v.process
case _ => process(0)
}

Magically, pattern matching can also be exploited inside a function like .map(), .filter(), .flatMap().

Seq(NoShape, Rect(3,3), Circle(4)).map{
case NoShape => 0
case Circle(r) => r
case Rect(a,b) => a*b
}

Monad Laws

Monad is a process which you can flatMap and it has to adhere following laws.

Identity law

Applying a flatMap function on a monad and its true value yields identical results. See the following example.

// Define a flatMap function
def f(a: Int) = Some(a * 10)

Following two lines yield the identical output.

Some(4).flatMap(f) -- equals Some(40)
f(4) -- equals Some(40)

Associativity law

Sounds very familiar? Yes, this is the same associativity law in math. The flatMap operators have associative property. Thus you can do the following.

def f(a: Int) = Some(a * 10)
def g(a: Int) = Some(a * 5)


Some(4).flatMap(f).flatMap(g) -- equals Some(200)
Some(4).flatMap(i => f(i).flatMap(g)) -- also equals Some(200)

Monoid

Monoid is a concept of a set of data which you applies some certain operators on its elements, the result still remain a members of the set. Confusing? Consider the following rules.

The result of an addition of two monoid elements remains an element of the monoid.

Let's consider a set of integer as a monoid. Picking up any pair of these integers and add them together still yield an integer. It never goes off the monoid set.
Given $\forall a \in I, \forall b \in I$
Then,
$c = a + b, c \in I$

Adding an element with an identity does not change the value.

This applies to any element inside the monoid. Say 0 is an identity value for integer, you add it to any integer values you still get the same value.
identity + a = a

Foldability and reducability

You may have been a user of .fold() or .reduce() operators in other languages. Folding or reducing a list of monoidic values results in a monoidic value in the end. Have a look at the signatures below.
List's fold function signature.

def fold[A1 >: A](z: A1)(op: (A1, A1) ⇒ A1): A1

List's reduce function signature.

def reduce[A1 >: A](op: (A1, A1) ⇒ A1): A1

It is obvious such that both .fold() and .reduce() obey monadic rules. They take monadic elements as parameters and their output still remain inside the same monadic set.
You start with a list of integer. Reducing them into one single scalar value still gets you an integer.
Scala, however, does not natively allow you to implement your own Monoid. There are some widely-used functional programming framework which supports this feature more natively including cats

Mixin + Traits

Mixin is supported and motivated natively by Scala. You can define a trait having certain set of functionality and share across multiple classes or even traits.
See the example below.

trait Splittable[A] {
def split: Seq[A]
}
.
trait Joinable[A] {
def join(ns: Seq[A]): Seq[A]
}

Then you can pluck the traits above into certain classes or traits you want to mix these functionality in natively as follows.

class MyData[A] extends BaseClass[A]
with Splittable[A]
with Joinable[A]
.
.
class YourData[A,B] extends BaseClass[A]
with Joinable[A]

Generic Typings

You may wonder what is the use of [A] in the example above. Thinking of C++ generic class which you can define a class which works with any kind of type T. In Scala, this idea works similarly but with more flexibility.

With generic type

Let's define a class or trait with a generic type as follows.

trait T[A] { def f[A](a: A): A }
class C[A] extends T[A]

The example above is simple especially for the people who are already familiar with C++ generic class. Instead of restricting the trait and class with specific type like Int, Boolean, String, etc, you loosen the constraint with generic type A.

Tighter type constraint, upperbound

In C++, generic typing does not allow you to constraint the type T to only works with numeric types or certain classes. Lucky enough, Scala allows you to do so.

trait Base
trait Child extends Base
trait GrandChild extends Child
.
.
class C[T]
class D[T <: Base]

From example above, the key differences between C and D are.

Tighter type constraint, lowerbound

In oppose to constraint above, we can constrain the deepest subtypes to bind with the class as follows.

trait Base
trait Child extends Base
trait GrandChild extends Child
.
.
class D[T <: Child]
class E[T >: Child]

From example above.

Destructuring Patterns

Similar to Python, you can destructure the values like so.

val Seq(a,b,c) = Seq(1,2,3)
val n::ns = Seq(1,2,3)

Then you'll get the following values assigned to variables.
a=1, b=2, c=3, n=1, ns=Seq(2,3)
It works with case class too.

case class C(a: Int, b: Int=0, c: Boolean=false)
.
val k = C(30)
val C(a,b,c) = k

Then you'll get the following values assigned to variables.
a=30, b=0, c=false

Best Practices

I recommend reading Effective Scala as a starting point. The guide is highly comprehensive and suggests lots of common practice of writing a more readable and better Scala code.