Scala is a programming language that mixes object oriented and functional programming in a powerful and flexible way. While it can not be considered as a mainstream language, it has seen a growing adoption trend.An important ingredient for this diffusion is its complete interoperability with Java and the fact that it runs on a solid platform such as the JVM.
It is currently the 4th most loved programming language and the 2nd top paying technology of 2016 (StackOverflow Developers Survey).
These slides have been used for a 4h seminar at the University of Cagliari the 17th of December 2016
3. OUTLINE
1. Introduction to Scala
2. The basics
3. Fundamental data structures
4. Web services with Akka-Http
a) the language
b) a bit of history
c) the ecosystem
d) SBT & Scala REPL
4. OUTLINE
1. Introduction to Scala
2. The basics
3. Fundamental data structures
4. Web services with Akka-Http
a) values
b) functions
c) classes and case classes
d) objects and traits
5. OUTLINE
1. Introduction to Scala
2. The basics
3. Fundamental data structures
4. Web services with Akka-Http
a) List[T]
b) Option[T]
c) Map[T], Try[T], Future[T]
6. OUTLINE
1. Introduction to Scala
2. The basics
3. Fundamental data structures
4. Web services with Akka-Http
a) Akka and Akka-Http
b) directives & routes
c) (un)marshalling
d) a simple web service
8. SCALA, WHAT
Technically, Scala is a blend of object-oriented and functional programming
concepts in a statically typed language.
Martin Odersky
§ object-oriented => encourages to structure code into classes and objects
§ functional => embraces typical functional programming structures:
immutability, higher order functions, pattern matching, tuples, etc
§ statically typed => enforces type safety and program correctness
9. SCALA, WHY
§ Programming with Scala is fun!
§ Similar feel of dynamic languages, but with type safety.
§ Less boilerplate than usual object-oriented languages.
§ Succint code, less to write 👍 less to read 👍 👍 👍
§ Complete, consistent and pleasant API.
§ Immutability by default 👉 easier to reason about the code.
§ Runs on the JVM and interoperates with Java
§ You can use your favourite Java libraries … in Scala.
§ There are an increasing number of job offers with higher average salary
10. SCALA, WHO – WHERE - WHEN
§ Martin Odersky, professor at ETH and then EPFL (Switzerland)
§ Designer of Java’s generics and javac compiler
§ 2004. First public release for both JVM and .NET
§ 2006. v2.0 released
§ 2011. Typesafe(now Lightbend) for commercial support, training, etc
§ 2012. Dropped support for .NET
§ 2016/11. Scala 2.12 released with new compiler, full Java 8 interop
23. BASIC SYNTAX
§ Declaring a variable with var
§ For loops
§ While loops
§ Declaring a value with val
val message : String = “Hello Scala” ;
Optional, type is inferred.
Optional, discouraged
24. BASIC SYNTAX
§ Declaring a variable with var
§ For loops
§ While loops
§ Declaring a value with val
val message = “Hello Scala” // type String is inferred
val pair = (1,2) // tuples work as in OCaml
// access tuple fields with with ._<n>
(5, “ciao”)._2 // “ciao”
25. BASIC SYNTAX
§ Declaring a variable with var
§ For loops
§ While loops
§ Declaring a value with val
§ Declaring a function with def
def add(a: Int, b: Int) : Int = { a + b }
Optional, return type is inferred
Mandatory for recursive functions
26. BASIC SYNTAX
§ Declaring a variable with var
§ For loops
§ While loops
§ Declaring a value with val
§ Declaring a function with def
def add(a: Int, b: Int) : Int = { a + b }
Optional, for one liners
27. BASIC SYNTAX
§ Declaring a variable with var
§ For loops
§ While loops
§ Declaring a value with val
§ Declaring a function with def
def add(a: Int, b: Int) = a + b
> add: (a: Int, b: Int)Int
28. BASIC SYNTAX
§ Declaring a variable with var
§ For loops
§ While loops
§ Declaring a value with val
§ Declaring a function with def
§ If-then-else: remember is an expression, not a command
if(x == 0) “zero” else “not-zero”
29. OCaml Scala
• Both values and functions
declared with let keyword
• Super duper type inference
• Functions curried by default
let add a b = a + b
int -> int -> int
§ Functional with objects
• Use val to declare values
• Use def to declare functions
• Type annotation is often needed
• Functions take argument tuples by default
def add(a: Int, b: Int) = a + b
def add(a: Int)(b: Int) = a + b
§ Object Oriented and Functional
§ Complete interoperability with Java
32. FUN WITH FUNCTIONS
§ Higher order functions
def operator(a:Int, b: Int)(f: (Int,Int) => Int) = f(a,b)
// (Int, Int) => ((Int, Int) => Int) => Int
operator(5, 6)((a,b) => a + b) // 11
operator(5, 6){(a,b) => a + b} // 11. avoid for 1-liners
operator(5, 6)(_ + _) // 11. the most concise
def add(a: Int, b: Int) = a + b
operator(5, 6)(add) // 11. when f is complex/long/reusable
34. FUN WITH FUNCTIONS
§ Polymorphism
def transform[A, B](f: A => B)(x: A): B = f(x)
// ( A => B) => A => B
def numberToString(x: Int) = x.toString()
def transformNumberToString = transform(numberToString) _
// Int => String
transformNumberToString(5L) // “5”
37. HIGHER ORDER FUN - EXCERCISE
§ Define and use a function that takes two functions and two Ints x and y
§ That evaluates to a pair with the result of the two functions
§ Both applied to x and y
§ It should respect a similar signature
g ((Int, Int) => Int) => // function f1
((Int, Int) => Int) => // function f2
(Int, Int) => (Int,Int)
use :paste on REPL to write multi-line statements or to paste code written elsewhere
39. CLASSES
§ Mostly as in Java … but immutable by default.
import java.time.LocalDate
import java.time.temporal.ChronoUnit
class Person(firstName: String, lastName: String = "Rossi",
protected val birthday: LocalDate){
val fullName = s"$firstName $lastName”
def age = ChronoUnit.YEARS.between(birthday, LocalDate.now)
def isOlderThan(other: Person): Boolean = birthday.isBefore(other.birthday)
}
new Person( ”Giulio”, “Bianchi”, LocalDate.now)
new Person( firstName = “Mario”, birthday = LocalDate.now)
constructor parameters
default value for
lastName
40. CLASSES
§ Mostly as in Java … but immutable by default.
import java.time.LocalDate
import java.time.temporal.ChronoUnit
class Person(firstName: String, lastName: String = "Rossi",
protected val birthday: LocalDate){
val fullName = s"$firstName $lastName”
def age = ChronoUnit.YEARS.between(birthday, LocalDate.now)
def isOlderThan(other: Person): Boolean = birthday.isBefore(other.birthday)
}
constructor parameter bound to public value
41. CLASSES
§ Mostly as in Java … but immutable by default.
import java.time.LocalDate
import java.time.temporal.ChronoUnit
class Person(firstName: String, lastName: String = "Rossi",
protected val birthday: LocalDate){
val fullName = s"$firstName $lastName”
def age = ChronoUnit.YEARS.between(birthday, LocalDate.now)
def isOlderThan(other: Person): Boolean = birthday.isBefore(other.birthday)
}
public value initiliazation
42. CLASSES
§ Mostly as in Java … but immutable by default.
import java.time.LocalDate
import java.time.temporal.ChronoUnit
class Person(firstName: String, lastName: String = "Rossi",
protected val birthday: LocalDate){
val fullName = s"$firstName $lastName”
def age = ChronoUnit.YEARS.between(birthday, LocalDate.now)
def isOlderThan(other: Person): Boolean = birthday.isBefore(other.birthday)
}
method without parameters
43. CLASSES
§ Mostly as in Java … but immutable by default.
import java.time.LocalDate
import java.time.temporal.ChronoUnit
class Person(firstName: String, lastName: String = "Rossi",
protected val birthday: LocalDate){
val fullName = s"$firstName $lastName”
def age = ChronoUnit.YEARS.between(birthday, LocalDate.now)
def isOlderThan(other: Person): Boolean = birthday.isBefore(other.birthday)
}
method with parameters
44. CLASSES – wait a minute
§ Everything is an object – there are no primitive types (int, long, double, …)
§ Operators for Int, Long, Double, … are methods
5 + 6 /* is the same as */ (5).+(6)
§ Can call 1-arg methods as infix operators (do it juditiously)
val giulio = new Person( ”Giulio”, “Bianchi”, LocalDate.of(1990, 12, 20)
val mario = new Person( firstName = “Mario”, LocalDate.of(1991, 11, 17)
giulio.isOlderThan(mario) /* sames as */ giulio isOlderThan mario
45. OBJECTS
§ Hybrid between Singleton/static class
§ Have no constructor
§ fields and methods can be imported as packages -> import DateUtils._
import java.time.LocalDate // yeah, importing java libraries
object DateUtils{
def today = LocalDate.now
val monthsInYear = 12
}
46. COMPANION OBJECTS
§ If a class and an object with same name are defined in the same file
§ The object can access all the class’ fields
§ A way to define what in Java would be static fields and methods
import java.time.LocalDate
class Person(val fullName: String, val birthday: LocalDate)
object Person{
def older(me: Person, other: Person) = {
if(me.birthday.isBefore(other.birthday)){ me }
else { other }
}
}
47. CASE CLASSES
case class Person(firstName: String, lastName: String, birthday: LocalDate)
• all constructor parameters are also public values
• does not require new to be instantiated
• default implementation for equals, getHashCode, toString *
• copy method
• instances of case classes can be decomposed through pattern matching
• Scala way for defining algebraic data types
* Highly recommanded for classes in Java -> Effective Java – items 8, 9, 10
48. CASE CLASSES - example
OCAML
type number = Zero | Succ of number
let numberToInt n = match n with
| Zero -> 0
| Succ(x) -> 1 + numberToInt x
SCALA
sealed abstract class Number
object Zero extends Number
case class Succ(n:Number) extends Number
def numberToInt(n: Number) : Int =
n match {
case Zero => 0
case Succ(x) => 1 + numberToInt(x)
}
49. TRAITS
§ Like Java interfaces – more like Java 8 interfaces
§ Can contain virtual as well as concrete methods
§ Can contain value definitions
§ Multiple traits can be mixed in a single class(or object)
abstract class Pokemon
trait WaterAttacks{ def waterAttack(): Unit}
trait PsychicAttacks{ def confusion() = println("Psyduck! Psyduck!") }
class Psyduck extends Pokemon with WaterAttacks with PsychicAttacks{
override def waterAttack() = println("Water Pulse!")}
(new Psyduck).waterAttack()
51. PATTERN MATCHING - EXCERCISE
§ types needed to evaluate basic integer arithmetic expressions (*, / , %, +, -)
§ define a function for evaluating these expressions.
use :paste on REPL to write multi-line statements or to paste code written elsewhere
53. LIST [T]
§ Core of most functional languages. Behave mostly as OCaml
§ Powerful API to traverse, transform, filter, etc… with high order functions!
val emptyList: List[Int] = Nil // or List() or List.empty
val numbers = List(2, 4, 5, 12, 20) // here we have type inference
val moreNumbers = 6 :: 14 :: numbers // prepend --> [6, 14, 2, 4, 5, 12, 20]
numbers.head // 2
numbers.tail // [4, 5, 12, 20]
numbers(3) // 12
moreNumbers.filter(x => x > 10) // [14, 12, 20]
moreNumbers.filterNot( _ > 10) // [6, 2, 4, 5]
moreNumbers.partition(x => x > 10) // ( [14, 12, 20], [6, 2, 4, 5] )
54. LIST [T]
val moreNumbers = 6 :: 14 :: List(2, 4, 5, 12, 20) //prepend as in Ocaml
def isEven(x: Int) = x % 2 == 0
moreNumbers.take(3) // [6, 14, 2]
moreNumbers.drop(5) // [12, 20]
moreNumbers.takeWhile(isEven) // [6, 14, 2, 4]
moreNumbers.grouped(3) // [[6, 14, 2], [4, 5, 12], [20]]
moreNumbers.grouped(3).flatten // [6, 14, 2, 4, 5, 12, 20]
// flatten turns a List[List[T]] into a corresponding List[T]
55. LIST [T]
val otherNumbers = (1 to 5).toList // fancy way to generate sequences
List (1, 2, 3).map(f) /* equivalent to */ List ( f(1), f(2), f(3) )
otherNumbers.map(x => (1 to x).toList) // [[1], [1,2], [1,2,3], … ]
56. LIST [T]
val otherNumbers = (1 to 5).toList // fancy way to generate sequences
List (1, 2, 3).reduce(f) /* equivalent to */ f( f(1, 2), 3)
otherNumbers.reduce((a,b) => a * b) // 120 (((1*2) *3) *4) * 5
57. LIST [T]
val otherNumbers = (1 to 5).toList // fancy way to generate sequences
List (1, 2, 3).fold(100)(f) /* equivalent to */ f( f( f(100,1), 2), 3)
otherNumbers.fold(3)((a,b) => a * b) // 360 ((((3*1) *2) *3) * 4) * 5
58. LIST [T]
val otherNumbers = (1 to 5).toList // fancy way to generate sequences
List (1, 2, 3).scan(100)(f) // is a map to the fold's intermediate results
otherNumbers.scan(3)((a,b) => a * b) // List(3, 3, 6, 18, 72, 360)
59. LIST [T] – map, flatten, flatMap
val otherNumbers = (1 to 5).toList // fancy way to generate sequences
otherNumbers.map( (1 to _).toList) // [[1], [1,2], [1,2,3], … ]
otherNumbers.map( (1 to _).toList).flatten // [1, 1, 2, 1, 2, 3, … ]
otherNumbers.flatMap( (1 to _).toList) // [1, 1, 2, 1, 2, 3, … ]
In this context
map [T, B]( f: T => B) : List[B]
flatMap[T, B]( f: T => List[B]) : List[B]
In Scala there are a number of containers that expose map and flatMap
60. LIST [T] – other goodies
val cities = List(“Rome”, “Paris”, “Madrid”, “London”)
val countries = List(“Italy”, “France”, “Spain”, “Great Britain”)
(cities zip countries).toMap // Map[String, String]
val numbers = (1 to 25 by 3).toList
numbers.groupBy( n => n % 2 == 0) // Map[Boolean, List[Int]]
[false -> List(1, 7, 13, 19, 25), true -> List(4, 10, 16, 22)]
Other Scala collections similar to List[T].
Especially Vector[T] and Array[T]. Mostly used for Java interoperability
61. MAP [K, V]
§ Called Map in Java and C++
§ Called Dictionary in C# and Python
§ Called Associative Arrays in PHP
§ A container for mappings from a value of type K to a value of type V
val citiesToCountry = (cities zip countries).toMap
val withPadania = citiesToCountry + ( “Milano” -> “Padania” )
val withoutParis = withPadania – “Paris”
§ Getting the elements
updated.get(“Napoli”) // get(key: K): Option[V]
updated(“Riga”) // NoSuchElementException if not found
62. MAP [K, V]
§ A container for mappings from a value of type K to a value of type V
§ If accessed through ( ) it can be seen as a partial function K => V
List(“Rome”, ”London”).map(withoutParis) // List(“Italy”, “Great Britain”)
§ And transformed to a total function K -> V using .withDefaultValue
val total = citiesAndCountries.withDefaultValue(“Unknown”)
total(“Cagliari”) // unknown
total.get(“Cagliari”) // -> None
§ Operations to transform and iterate. Map, mapValues, flatMap, filter, foreach,
64. COLLECTIONS - EXCERCISE
1. Get the sum of the files size in your user folder (we can limit at the top level)
2. Build a map from the folder name to its total size
§ You will need
import java.io._
val home = new File(<folder name>)
home.listFiles
use :paste on REPL to write multi-line statements or to paste code written elsewhere
67. OPTION [T]
§ The Option type denotes with Some[T] the presence of a value, and with None its absence
§ Represents a value that can be or not be there. In Ocaml should be something similar to:
type ‘a option = Some of ‘a | None
How do you expect it to be implemented in Scala?
sealed abstract class Option[T]
case object None extends Option[T]
case class Some[T](x: T) extends Option[T]
§ Make Tony happy, never (ever ever) return null
§ return or expect an Option[T] whenever the value can be missing.
68. OPTION [T]
def getUserById(id: Long) : Option[User] // explicit uncertainty
case class User(firstName: String, middleName: Option[String], … )
val maybeUser = db.getUserById(5)
val user = maybeUser.getOrElse( throw new UserNotFoundException(…) )
OR
val user = db.getUserById(5) match {
case Some(found) => found
case None => throw new UserNotFoundException(…)
}
OR (but better not to)
db.getUserById(5).get // this will throw NoSuchElementException if None
69. OPTION [T]
def getUserById(id: Long) : Option[User] // explicit uncertainty
val maybeUser = db.getUserById(5)
val user = maybeUser.getOrElse( throw new UserNotFoundException(…) )
OR
val user = db.getUserById(5) match {
case Some(found) => found
case None => throw new UserNotFoundException(…)}
§ What if I need to pass the User to another function or compute something?
seems cumbersome
70. map [T, B]( f: T => B): Option[B]
Some(5).map(x * 10) // --> Some(50)
None.map(x * 10) // --> None
§ Let’s naively count the number of hashtags used by an user
getUser(5).map { user =>
getTweets( user ).map { tweet => // getUserTweets: List[String]
tweet.count( _ == ’#' ) // count elements by a predicate
}.reduce(_ + _) // or better use .sum
}
Some(161247) or None if the user has been not found
OPTION [T] – map!
71. def getUserById(id: Long): Option[User]
def getUserTweets(user: User): Option[List[String]
val maybeMaybeUserTweets =
db.getUserById(5).map( user => getUserTweets(user))
map [T, B]( f: T => B): Option[B]
§ Here B is Option[List[String]] so
§ Option[B] is then Option[Option[List[String]]]
OPTION [T] – map!
73. def getUserById(id: Long): Option[User]
def getUserTweets(user: User): Option[List[String]
flatMap [T, B]( f: T => Option[B]): Option[B]
Here B is List[String] hence the result is an Option[List[String]]
getUser(5).flatMap(getTweets).map( _.map( _.count( _ == ’#' )).sum )
Some(161247) or None
OPTION [T] – flatMap!
74. Some(5).contains(6) // -> false
Some(13).forall(_ % 2 == 0) // -> false
None.contains(“ciao”) // -> false
Some(24)
.filterNot( x => x < 10) // -> Some(24)
.map( x => x / 3) // -> Some(8)
.filter( x => x % 5 == 0) // -> None
.foreach(println)
What’s the output?
It does not print anything, since None contains no values
OPTION [T] as a one item List[T]
75. § A number of Scala containers allow to encapsulate certain mechanisms
§ Most notably
Option[T] // a function that can return a value or not
Try[T] // a function that can complete successfully or fail
Future[T] // a function that can take some time to complete
§ And others that support map and flatMap operations allowing to concatenate
manipulations of these containers in an coherent way
MONADIC DESIGN
76. § Dealing with exceptions in Scala can be done in two ways
§ (Almost) Java Style
try{ 10/0 } catch {
case e: ArithmeticException => println(e.getMessage)
case e: Exception => println(“never catch generic exception”)
}
§ Using Try[T]
Try{ 10/0 } match {
case Failure(exc) => println(exc.getMessage)
case Success(value) => value
}
TRY-CATCH or TRY [T]
What’s the
difference?
77. § Try is a data structure. You can assign it to values.
§ The API is mostly the same as List[T] and Option[T]
map [T, B]( f: T => B) : Try[B]
flatMap [T, B]( f: T => Try[B]): Try[B]
filter, foreach, flatten, …
def getUserById(id: Long): Try[Option[User]] // db connection may fail
val divByTwo = Try{10 / 2}
val divByZero = Try{10 / 0}
divByZero.map{ number => number * 3 } // Failure(ArithmeticException)
divByTwo.map{ number => number * 3 } // Success(15)
TRY [T]
78. § Encapsulates a long computation (e.g. calling a web service)
§ A way to handle concurrency
§ A Future is an object that at some point may contain the required value
§ Mostly same API of List[T], Option[T], Try[T]
map [T, B]( f: T => B) : Future[B]
flatMap [T, B]( f: T => Future[B]): Future[B]
filter, foreach, flatten, …
def slowIncrement(x: Int) = Future{ Thread.sleep(1000); x + 1 }
§ map used to chain a synchronous operation
§ flatMap to chain an asynchronous one
FUTURE [T]
81. Actor
1
Actor
2
Actor
3
Actor
4
ACTOR
§ Primitive unit of computation
§ Has a mail box
§ Reads one message at a time from its
mailbox
§ Performs a different action depending
on the message it receives:
§ Can send messages to other actors
§ Can create other actors
§ Can mutate its state and change the way
to react to next messages
§ No shared memory with other actors
82. § Concurrency is message based and asynchronous
§ No synchronization primitives
§ Actor systems can span multiple jvms
§ Actors interact in the same way on the same host or separate host
§ Actor hierarchies provide supervision and fault-reaction
§ Fault-tolerance, error is just an event that needs to be handled
83. § Full Http Server and Http Client implementation
§ The main goal is to write/consume RESTful APIs
§ Build upon Akka
§ Not a framework, but a set of libraries
§ Formerly called Spray.io, later incorporated in Akka
§ Different API levels for implementing the same things
§ Takes advantage of Scala ability to create Domain Specific Languages
§ Routing DSL
§ Marshalling/UnMarshaling of json/xml requests
84. ROUTING DSL
§ An HTTP route is composed of an HTTP Method and a relative URI
§ Each routing hierarchy is created upon Directives
GET /unica/students/45353
pathPrefix(“unica”){
path(“students” / LongNumber){ id => // / is an used to chain path segments
get{
complete(studentsService.getStudent(id))
}
}
}
85. DIRECTIVES
§ Each directive is a small (and nestable) building block
§ Each directive can filter/transform/complete a request or extract values from it
§ HttpMethods (get, post, put, …)
§ Extract headers, url parameters or entities (headerValue, entity, parameters)
§ Routes are concatenated using the ~ (tilde) operator
86. accept if starts with: /unica/students
accept only if URL is /unica/students
Accept only if method is POST
Unmarshall body to instance of Student
Businness logic, save the student
Respond with Status 201
If rejected from the above
Accept only if URL /unica/students/:id
Accept only if method is GET
Complete calling the businness logic
pathPrefix(“unica” / “students“){
pathEnd{
post{
entity(as[Student]){ request =>
complete{
studentsService.register(request)
Created
}
}
} ~
path(LongNumber){ id =>
get{
complete(studentsService.getStudent(id))
}
}
}
}
Incoming Request
89. § http://www.scala-lang.org/
§ http://docs.scala-lang.org/tutorials/
§ https://twitter.github.io/scala_school/
§ https://www.coursera.org/specializations/scala
§ akka.io
§ http://doc.akka.io/docs/akka-http/current/index.html
§ Programming in Scala 3rd Edition – Martin Odersky
§ Akka in Action - Raymond Roestenburg
§ francescousai.info/blog
WHERE TO GO FROM HERE