This presentation introduces the main features of Scala, an object oriented and functional programming language. The main focus of the slides is to show how the language implements natively some of the patterns and best practices that are no present in other programming languages, such as Java, C++ and so on.
In detail, the presentation spans these concepts:
- Language's main syntax
- Classes, abstract classes, objects and traits (mixin)
- The Option class
- An introduction to generics
- Implicit classes
- Functions
- Recursion (simple and tail)
- Currying
- Call by value / name
The presentation is took from the Software Engineering course I run in the bachelor-level informatics curriculum at the University of Padova.
1. SHORT INTRODUCTION TO SCALA
INGEGNERIA DEL SOFTWARE
Università degli Studi di Padova
Dipartimento di Matematica
Corso di Laurea in Informatica, A.A. 2014 – 2015
rcardin@math.unipd.it
2. Ingegneria del software mod. B
INTRODUCTION
Object/functional programming language
Based on Java Virtual Machine
Full interoperability with Java libraries
Pure object orientation (no primitive types)
Every function is a value
High order functions (lambdas)
Promoting immutability
Currying
Pattern matching
Statically typed
Intelligent type system
Extensible
2Riccardo Cardin
2003:
internal
use to
EPFL
2004:
version
1.0
2006:
version
2.0
2007:
the
«growth
year»
2014: version
2.11 (JVM 8)
3. Ingegneria del software mod. B
INTRODUCTION
Language main features
Read-Eval-Print-Loop (REPL) interpreter
An interactive shell to execute statements "on the fly"
Variables
UML syntax -> name : type
val x: Int = 1 + 1 Immutable
lazy val x: Int = 1 + 1 Immutable and lazy
var x: Int = 1 + 1 Mutable
def x: Int = 1 + 1 Executed every time
Very smart type inferer val x = 1 x: Int
No semicolon
Blocks, blocks everywhere...
3Riccardo Cardin
4. Ingegneria del software mod. B
INTRODUCTION
Language main features
Every statement is a "pure" expression
No return statement needed!
The if-else statement is a value
Imperative for statement is not supported
Use the for-yield statement instead
Every char can be used to define names (%$#@?...)
Full integration with every Java library
4Riccardo Cardin
val y = 4
// x has type String
val x = if (y < 3) "minor" else "greater"
// Returns an array of integers between 2 and 101
for (i <- (1 to 100)) yield i + 1
5. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Everything is an object
5Riccardo Cardin
Values
References (Scala and Java)
6. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Classes
6Riccardo Cardin
class Person(val name: String, val surname: String,
val birth: Date) {
// Main Ctor body here
// Every secondary Ctor must call the main Ctor as
// first statement (very restrictive)
def this(name: String) = this(name, "", null)
def age(): Int = { /* return the age */ }
// Override intention must be declared
override def toString = s"My name is $name $surname"
}
7. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Classes
Main constructor is declared within Class name
Ctor body is made of statements in class body
Accessor methods are build automatically
val immutable value, getter only
var mutable value, getter and setter
def defines methods (and functions too...)
Use equal "=" iff the method is a function
No public class
You can have more than a class into a .scala file
No static methods (wait a minute...)
7Riccardo Cardin
8. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
...so let’s introduce objects notation!
8Riccardo Cardin
9. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Object
Native Singleton pattern implementation
There can be only one instance of ChuckNorris
Companion object
An object called as an existing class
Companion object’s methods are static method of the class
9Riccardo Cardin
object ChuckNorris extends Person("Chuck Norris") {
// This is a static method
def roundhouseKick = "Roundhouse"
}
println ChuckNorris.roundhouseKick
class Person(val name: String, val surname: String)
object Person { // Companion object
def someStupidStaticMethod = "This is a static method"
}
println Person.someStupidStaticMethod
10. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Object
Companion objects implement also Factory Method
pattern natively
Every apply method creates an instance of the class
Syntactic sugar: Class.apply(...) Class(...)
It’s like a class application (...function anyone?)
10Riccardo Cardin
class Person(val name: String,
val surname: String)
object Person {
// Factory method
def apply(n: String, s: String) =
new Person(n, s)
}
// Application of the class will return a new instance
val ricky = Person("Riccardo", "Cardin") // No new operator
There can be
more than
one apply!!!!
11. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Object
Used also to define the entry point of an application
The Java way
The Scala way
11Riccardo Cardin
object Hello extends App {
// All the code in the object’s ctor body will be
// executed as our application.
// Arguments are accessible via an ‘args’ variable,
// available in the App trait
println("Hello World!")
}
object Hello {
// Every object containing a main method could be
// Executed. There are no restriction on file name
def main(args: Array[String]) {
println("Hello World!")
}
}
12. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Abstract classes
Also defines a main constructor
It has to be called from the derived classes’ main Ctor
12Riccardo Cardin
abstract class Animal(val species: String) {
def walk // Abstract method
}
class Person(val name: String,
val surname: String)
// Calling main ctor directly in class
// definition
extends Animal("Human") {
def walk = { /* Do some stuff */ }
}
val ricky = new Person("Riccardo", "Cardin")
13. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Traits: interfaces or abstract classes?
Like interfaces, they have not a constructor
To implement a single trait, use extends
Otherwise, use with (mixin)
But, they can have methods with implementation
and attributes
Similar (but more powerful) to default methods in Java 8
Multiple inheritance problem
Class linearization: use the rightmost type (more or less...)
13Riccardo Cardin
trait A { override def toString = "I’m A" }
trait B { override def toString = "I’m B" }
trait C { override def toString = "I’m C" }
class D extends A with B, C
new D().toString // Prints ‘I’m C’
14. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Traits
Like an interface
Like an abstract class
14Riccardo Cardin
// No implementation at all
trait Sorter {
def sort(list: List[Int]): List[Int]
}
class QuickSorter extends Sorter {
def sort(list: List[Int]) = { /* Do something */ }
}
trait AbstractDao { // No Ctor (wtf!)
var pool: Array[Datasource]
def getPool = pool // Implemented method
def save // abstract method
def delete // abstract method
}
class UserDao extends AbstractDao {
// Has to implement save and delete
}
15. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Traits
Inside a mixin the super refers to the previuos trait
in the linearized-type
Used to implement the Decorator pattern natively
15Riccardo Cardin
trait Employee {
// ...
def whois(): String
}
class Engineer(name: String, office: String) extends Employee
trait ProjectManager extends Employee {
abstract override def whois() {
// super refers to the previuos trait on the left
super.whois(buffer)
println("and I’am a project manager too!")
}
}
new Engineer("Riccardo","Development") with ProjectManager
Statically binded
Decorator pattern
16. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Traits and Objects
Using the companion object of a trait, we can
implement the Abstract Factory pattern
16Riccardo Cardin
// Abstract factory
trait AbstractFactory
// Private classes’ scope is limited to the
// definition file
private class ConcreteFactory1 extends AbstractFactory { /* ... */ }
private class ConcreteFactory2 extends AbstractFactory { /* ... */ }
object AbstractFactory{
def apply(kind: String) =
kind match {
case “factory1" => new ConcreteFactory1 ()
case “factory2" => new ConcreteFactory1 () factory 2
}
}
val factory = AbstractFactory("factory1")
17. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Case classes and pattern matching
Scala has a built in pattern matching mechanism
Case class: regular class that can be pattern-matched
Turns all ctor params into val (immutable) constants
Generates a companion object w/ apply methods
Generates toString, equals and hashCode methods
properly
17Riccardo Cardin
def matchTest(x: Int): String = x match {
case 1 => "one"
case _ => "many"
}
println(matchTest(3)) // Prints ‘many’
Case clauses are
evaulated from
top to bottom
trait Term
case class Var(name: String) extends Term
case class Fun(arg: String, body: Term) extends Term
Var("x") == Var("x") // Prints true
Checks the equality
among attributes
18. Ingegneria del software mod. B
OBJECT ORIENTED SCALA
Case classes and pattern matching
Native implementation of Value Object pattern
Obviously, case classes can be used in pattern
matching...
The match is done recursively on the structure of the type
18Riccardo Cardin
trait Term
case class Var(name: String) extends Term
case class Fun(arg: String, body: Term) extends Term
def printTerm(term: Term) { term match {
// The clauses uses placeholders for variables.
// If we don’t need the value, we can use Var(_)
case Var(n) =>
print(“We have a variable!”)
case Fun(x, b) =>
print(“We have a function!”)
}
}
19. Ingegneria del software mod. B
MISCELLANEA
Generics: more or less like Java Generics
Subtyping of generic types is "invariant"
...but it can also be used in a variant (+T) o
contravariant (-T) fashion
There are also upper type bound (T<:A) and lower
type bound (T>:A)
19Riccardo Cardin
class Node[T](elem: T)
val node = new Node(3) // Smart type inference
case class ListNode[+T](h: T, t: ListNode[T]) {
def head: T = h
def tail: ListNode[T] = t
def prepend[U >: T](elem: U): ListNode[U] =
ListNode(elem, this)
}
20. Ingegneria del software mod. B
MISCELLANEA
Option[A] type
A list with zero (None) or one element (Some[A])
Native implementation of the Null Object Pattern
No more NullPointerExceptions!!
Use them with pattern matching: results garanted!
20Riccardo Cardin
trait Option[T] {
def isDefined: Boolean
def get: T // Throws an exception if None
def getOrElse(t: T): T
}
case class Some[T](elem: T) extends Option[T]
case object None extends Option[Nothing]
def negate(x: Option[Int]) = x match {
case None => throw new IllegalArgumentException
case Some(i) => -i
}
21. Ingegneria del software mod. B
MISCELLANEA
Implicit classes
Used to translate object between similar types
-i.e, from Java types (int) to Scala type (Int)
Native implementation of Adapter pattern!
21Riccardo Cardin
trait Log {
def warning(message: String)
def error(message: String)
}
final class Logger {
def log(level: Level, message: String) { /* ... */ }
}
implicit class LoggerToLogAdapter(logger: Logger) extends Log {
def warning(message: String) { logger.log(WARNING, message) }
def error(message: String) { logger.log(ERROR, message) }
}
// The instance of Logger is translated to an instance of Log
val log: Log = new Logger()
The main ctor is used to
resolve the conversion
22. Ingegneria del software mod. B
MISCELLANEA
Tuples
Have you ever want a simple couple of value
container, while you were programming in Java?
Scala has a type, Tuples, that is just a tuple of
values
Don’t abuse of it: remember that things called classes?
Used internally by the compilator to treat function’s
arguments
You can build tuples with «up to 22» values
22Riccardo Cardin
val things = ("a", 1, 3.5) // (java.lang.String, Int, Double)
println(things._1) // Prints ‘a’
println(things._2) // Prints 1
// Create a tuple using the ‘->’ operator
val tuple = 1 -> "a" // (Int, java.lang.String)
24. Ingegneria del software mod. B
FUNCTIONAL SCALA
FP, a "new" programming paradigm
Not so young, instead
Main principles
Functions are high order, first-class citizen
Refer to it, pass as argument to another function…
Pure function
No side effects, immutability (referential transparency)
Recursion
24Riccardo Cardin
1930
Lambda
calculus
1958
Lisp
1990
Haskell
2003
Scala
2007
Clojure
25. Ingegneria del software mod. B
FUNCTIONAL SCALA
Functions: first-class citizens
Functions are compiled into object of an anonymous
class extending a trait FunctionX
X represents the number of parameters
abstract def apply(v1: T1, v2: T2, …): R
Strategy Pattern anyone?
25Riccardo Cardin
def name (parameters-list)[: return-type]
parameters-list := name: type
Definizione
val max = (x: Int, y: Int) => if (x < y) y else x
// Compiler builds an anonimous class, extending the trait
// FunctionX, where X is the number of parameters
val anonfun2 = new Function2[Int, Int, Int] {
// Application method, as in companion objects
def apply(x: Int, y: Int): Int = if (x < y) y else x
}
assert(max(0, 1) == anonfun2(0, 1))
26. Ingegneria del software mod. B
FUNCTIONAL SCALA
Functions (a.k.a. lambdas)
Return clause is redundant: last expression of the
function body is the value returned
26Riccardo Cardin
// Complete definition
def hello1(s: String): String = “Hello ” + s
// Inferred return type
def hello2(s: String) = “Hello ” + s
// Using function literal
def hello3 = (s: String) => “Hello ” + s
// Assigning a function to another function, inferred type
def hello4 = hello3 // copy of definition
// ...or assigning a function to a val
val hello5 = hello4 // as val (no difference, unless...)
// Assigning a function to another function, not inferred type
def hello6: String => String = hello1
// For every param it can be use the wildcard ‘_’ if it is used only
// one time in the function body
def hello: String => String = "Hello " + _
28. Ingegneria del software mod. B
FUNCTIONAL SCALA
Recursion
Let’s try to define the factorial function
Every step depends on the local variable n and on a
recursive call of the function
Every recursion step allocates a new frame in the stack
Simple recursion StackOverflowError
28Riccardo Cardin
// Simple recursion
def factorial(n: Int): Int =
if (n == 0) 1 else n * factorial(n – 1)
n * f(n – 1) n * f(n – 2) n * f(n – 3) n * f(n – 4) ...stack:
29. Ingegneria del software mod. B
FUNCTIONAL SCALA
Recursion
We need to limit the use of the stack: tail recursion
Every step depends only on the variables stored in
the current stack frame
Stack frame can be reused!!!
@tailrec Tells Scala compiler to optimize the
execution using a while loop
29Riccardo Cardin
// Tail recursion
def factorial(n: Int): Int = {
@tailrec
def factAcc(n: Int, acc: Int): Int =
if (n == 0) acc else fastAcc(n – 1, acc * n)
// Initial call
fastAcc(n)
}
We need an
accumulator
variable
30. Ingegneria del software mod. B
FUNCTIONAL SCALA
Currying
f: (A, B) => C, than curry(f) = fc: A => (B => C)
Used to abstract functions, separating the
“configuration” of a function from its “inputs”
Can you see the application of the Template Method pattern?
30Riccardo Cardin
Translation of the evaluation of a function that takes multiple arguments into
a sequence of function, each with a single argument (partial application)
// Let’s generalize functions on sequence of integers
def aggregate(id: Int, op: (Int, Int) => Int)(n: Int): Int =
if (n == 0) id
else op(aggregate(neutral, op)(n-1), n)
// Factorial of integers from n to 0
def factorial = aggregate(1, _ * _)_
// Summing integers from n to 0
def sum = aggregate(0, _ + _)_
Do you know
Haskell Curry?
31. Ingegneria del software mod. B
FUNCTIONAL SCALA
Call by value / name
Using the call by value default approach, each
function’s argument is evaluated immediatly
Scala implements also the call by name approach
Arguments are evatuated only when needed
This could lead to multiple evatualtion
31Riccardo Cardin
// Call by value
def firstNonZero(a: Int, b: Int) = if (a != 0) a else b
println(firstNonZero(1, 3)) // Prints 1
def z(): Int = z() // Non terminating function
println(firstNonZero(1, z)) // Non terminating function
// Call by name
def firstNonZeroByName(a: Int, b: => Int) = if (a != 0) a else b
println(firstNonZeroByName(1, z)) // Prints 1
Try to have a look
to streams...
32. Ingegneria del software mod. B
FUNCTIONAL SCALA
First order function and call by name
What if you want to implement the Command
pattern, saving the commands’ history?
32Riccardo Cardin
object Invoker {
// Sequence of executed commands
private var history: Seq[() => Unit] = Seq.empty
// Using call by name approach, to store the function
// inside history w/out executing it
def invoke(command: => Unit) {
command
history :+= command _
}
}
Invoker.invoke(println("foo"))
Invoker.invoke {
println("bar 1")
println("bar 2")
}
// At this point, history contains to functions
33. Ingegneria del software mod. B
FUNCTIONAL SCALA
And much more...
Scala has a very good Collection framework
Immutability and recursion
foreach, filter, map, flatMap, foldLeft, ...
Streams: infinite sequence of values
Natively dependency injection mechanism
Cake pattern, Implicit parameters, Reader monad
Actors model
Monads
...
33Riccardo Cardin