Scala

Evaluation rules

var defines a variable reference, it is mutable and it should be avoided

lazy only initialised when required and as late as possible (deferred evaluation), default is strict and is not recomputed like by-name parameters

1
2
3
4
5
6
7
8

defmyFunction=2// evaluated when calledvalmyImmutableValue=2// evaluated immediatelylazyvaliMLazy=2// evaluated once when neededdefsort(x:List[Double])// call by valuedefsort(x:=>List[Double])// call by name// ds is a sequence of Double, containing a varying number of argumentsdefvarargsFunction(ds:Double*)=???

Call by-value: evaluates the function arguments before calling the function

Call by-name: evaluates the function first, and then evaluates the arguments if need be (each time the parameter is referenced inside the function)

Type Parameters

Conceptually similar to C++ templates or Java generics. These can apply to classes, traits or functions.

1
2
3

classTypedClass[F](arg1:F){???}newTypedClass[Int](1)newTypedClass(1)// the type is being inferred, i.e. determined based on the value arguments

deffunc[T<:TopLevel](arg:T):T={...}// T must derive from TopLevel or be TopLeveldeffunc[T>:Level1](arg:T):T={...}// T must be a supertype of Level1deffunc[T>:Level1<:TopLevel](arg:T):T={...}

Variance

Quote

Variance being a tricky business, users usually get it wrong, and they come away thinking that wildcard and generics
are overly complicated. With definition-side variance, you express your intent to the compiler, and the compiler will
double check that the methods you want available will indeed be available.

Upper Bounds:[S <: T] means: S is a subtype of T. Let's suppose that T is actually an Iterable, then S could one of Seq, List or Iterable.

Lower Bounds:[S >: T] means: S is a supertype of T, or T is a subtype of S. So, if T is a List, S could be one of List, Seq, Iterable, or AnyRef.

Mixed Bounds:[S >: T2 <: T1] means: s is any type on interval between T1 and T2. In this case we have basically a mix of the two cases above.

Let's consider NonEmpty <: IntSet, then can we infer that List[NonEmpty] <: List[IntSet]?
Intuitively, this makes sense: a list of non-empty sets is a special case of a list of arbitrary sets.
We call types for which this relationship holds covariant because their subtyping relationship varies with the type parameter. Thus Lists in scala are covariant.

Does covariance make sense for all types, not just for List? No. For instance, in Scala, arrays are not covariant.

When does it make sense to subtype one type with another?

It is safe to assume that a type T is a subtype of a type U (T <: U) if you can substitute a
value of type T wherever a value of type U is required. This is called the Liskov Substitution Principle.

Objects and Code organization

Quote

Scala has no globally visible methods: every method must be contained in an object or a class. However, using methods named apply inside global objects, you can support usage patterns that look like invocations of global methods.

As you can read above, I introduced Objects in terms of the functions they contain. It's very important to stress on this aspect, because Classes and Objects should be seen under a different light using Scala, especially if you come from an imperative OOP language, like Java or C++. They are just a way to organise your functions and at some point, using traits, objects (companion objects) and case classes (data constructors) you will eventually be able to build up your coding architecture based on types and composition of functions.

General object hierarchy

Note

All members of packages scala and java.lang as well as all members of the object scala.Predef are automatically imported.

scala.Nothing is a trait that is the bottom subtype of every subtype of scala.Any

scala.Any base type of all types. It has methods hashCode and toString that can be overridden

scala.AnyVal is the base type of all primitive types: Double, Float, etc.

scala.Null is a subtype of any scala.AnyRef, and scala.Nothing is a subtype of any other type without any instance.

Null is a trait and is the bottom type similiar to Nothing but only for AnyRef not AnyVal

null is the only instance of type Null

Nil is an empty list that is defined as a List[Nothing]

None is an empty option that is defined as a Option[Nothing]

Unit is a subtype of AnyVal, it's only value is () and it is not represented by any object in the underlying runtime system. A method with return type Unit is analogous to a Java method which is declared void

Now, what we can do is defining a Factory Object which contains methods that construct other objects, without exposing each class implementation. Basically, we can hide each class inside a Singleton Object, which will represent just a tag for the overloaded methods that will give us the ability to instantiate each subclass dinamically, and using polymorphism at the same time.

Important

Note that OOP is not a paradigm, but it's just a way to define our code structure in a logic manner that is similar to playing with LEGOs. OOP can be seen like an orthogonal dimension compared to functional, declarative or imperative paradigms.

// We start defining a Singleton ObjectobjectElement{// we can now hide classes as private fields of this objectprivateclassArrayElements(valcontents:Array[String])extendsElementprivateclassLineElement(s:String)extendsElement{valcontents=Array(s)overridedefwidth=s.lengthoverridedefwidth=1}privateclassUniformElement(ch:Char,overridevalwidth:Int,overridevalheigth:Int)extendsElement{privatevalline=ch.toString*widthdefcontents=Array.fill(height)(line)}// FACTORYdefelem(contents:Array[String]):Element=newArrayElement(contents)defelem(chr:Char,width:Int,heigth:Int):Element=newUniformElement(chr,width,height)defelem(line:String):Elem=newLineElement(line)}

Objects creation are centralized and the details now are hidden.

Open/Closed Principle

This will eventually give an easy way to understand how to use these elements, and at the same time this small change will give the developer the Open/Closed Principle for free because less detail is exposed.

“Software entities … should be open for extension, but closed for modification.”

This provides more opportunities to change the implementation of the library without breaking client code. At the same time a class will have a single responsibility, and only one potential change in the software’s specification should be able to affect the specification of the class (Single Responsibility Principle). So, writing SOLID code pays off at the end.

Factory method

In Java you can create a private constructor by making it private. In Scala one can achieve the same behaviour prepending the private modifier to the default constructor.

1
2
3
4
5
6
7
8
9
10
11
12

classPointprivate(coordX:Float,coordY:Float){valx=coordXvaly=coordY/** Public auxiliary constructor * Setting the point at the origin of the Cartesian axes * calling the default private constructor using the */defthis()=this(0.0,0.0)defgetPoint=(x,y)}

A possible solution to define a new Point is given by the usage of a companion object and a factory method that will give us a convenient way to define a new object, without actually call the new operator. To do so one can add the apply() method to the newly created object, which will have the same acess rights to the Point class if placed in the same file.

Therefore, the apply() method will be able to use the private construction of the Point class and then it will become a factory method:

Collections snippet

valr:Range=1until5// 1, 2, 3, 4vals:Range=1to5// 1, 2, 3, 4, 51to10by3// 1, 4, 7, 106to1by-2// 6, 4, 2// Operations on sequencesvalxs=List(...)xs.length// number of elements, complexity O(n)xs.last// last element (exception if xs is empty), complexity O(n)xs.init// all elements of xs but the last (exception if xs is empty), complexity O(n)xstaken// first n elements of xsxsdropn// the rest of the collection after taking n elementsxs(n)// the nth element of xs, complexity O(n)xs++ys// concatenation, complexity O(n)xs.reverse// reverse the order, complexity O(n)xsupdated(n,x)// same list than xs, except at index n where it contains x, complexity O(n)xsindexOfx// the index of the first element equal to x (-1 otherwise)xscontainsx// same as xs indexOf x >= 0xsfilterp// returns a list of the elements that satisfy the predicate pxsfilterNotp// filter with negated p xspartitionp// same as (xs filter p, xs filterNot p)xstakeWhilep// the longest prefix consisting of elements that satisfy pxsdropWhilep// the remainder of the list after any leading element satisfying p have been removedxsspanp// same as (xs takeWhile p, xs dropWhile p)List(x1,...,xn)reduceLeftop// (...(x1 op x2) op x3) op ...) op xnList(x1,...,xn).foldLeft(z)(op)// (...( z op x1) op x2) op ...) op xnList(x1,...,xn)reduceRightop// x1 op (... (x{n-1} op xn) ...)List(x1,...,xn).foldRight(z)(op)// x1 op (... ( xn op z) ...)xsexistsp// true if there is at least one element for which predicate p is truexsforallp// true if p(x) is true for all elementsxszipys// returns a list of pairs which groups elements with same index togetherxsunzip// opposite of zip: returns a pair of two listsxs.flatMapf// applies the function to all elements and concatenates the resultxs.sum// sum of elements of the numeric collectionxs.product// product of elements of the numeric collectionxs.max// maximum of collectionxs.min// minimum of collectionxs.flatten// flattens a collection of collection into a single-level collectionxsgroupByf// returns a map which points to a list of elementsxsdistinct// sequence of distinct entries (removes duplicates)x+:xs// creates a new collection with leading element xxs:+x// creates a new collection with trailing element x// Operations on mapsvalmyMap=Map("I"->1,"V"->5,"X"->10)// create a mapmyMap("I")// => 1 myMap("A")// => java.util.NoSuchElementException myMapget"A"// => None myMapget"I"// => Some(1)myMap.updated("V",15)// returns a new map where "V" maps to 15 (entry is updated)// if the key ("V" here) does not exist, a new entry is added// Operations on Streamsvalxs=Stream(1,2,3)valxs=Stream.cons(1,Stream.cons(2,Stream.cons(3,Stream.empty)))// same as above(1to1000).toStream// => Stream(1, ?)x#::xs// Same as Stream.cons(x, xs)// In the Stream's cons operator, the second parameter (the tail)// is defined as a "call by name" parameter.// Note that x::xs always produces a List

Ordering

There is already a class in the standard library that represents orderings: scala.math.Ordering[T] which contains
comparison functions such as lt() and gt() for standard types. Types with a single natural ordering should inherit from
the trait scala.math.Ordered[T].

1
2
3
4
5

importmath.Orderingdefmsort[T](xs:List[T])(implicitord:Ordering)={...}msort(fruits)(Ordering.String)msort(fruits)// the compiler figures out the right ordering

map() and flatMap()

Abstract

To be added

Category Theory

Abstract

To be added

Algrebraic Data Type

Abstract

To be added

Typeclass

Quote

[...] Type class is a class (group) of types, which satisfy some contract defined in a trait with addition that such functionality (trait and implementation) can be added without any changes to the original code. One could say that the same could be achieved by extending a simple trait, but with type classes it is not necessary to predict such a need beforehand.

There is no special syntax in Scala to express a type class, but the same functionality can be achieved using constructs that already exist in the language. That’s what makes it a little difficult for newcomers to spot a type class in code. A typical implementation of a type class uses some syntactic sugar as well, which also doesn’t make it clear right away what we are dealing with.

Monoid

The Monoid is essentially the first purely algebraic data structures. The term monoid is taken from the Category Theory, and it means a category with one object. This kind of algebraic data structures are the corner stone of the technique that gives us the ability to write polymorphic functions. A Monoid is made of:

A type T

A binary operation, which is associative, that takes two values of type T and combines them into one