570

# The Essence and Fundamentals of Scala

JUG Thüringen, Erfurt

Scala is one of the most popular alternative languages on the JVM. In this talk, we are going to look at what makes this language so interesting: A strong foundation in functional as well as object-oriented programming, combined with a powerful type system. We'll look at how these principles have shaped the language and how to put them to use in practice. April 26, 2018

## Transcript

1. The Essence and
Fundamentals of
Scala
26,04.2018
ERFURT / JAVA USER GROUP TH

• senior consultant at INNOQ
• author of “The Neophyte’s Guide to Scala”
• co-founder of ScalaBridge Berlin
• website/blog: http://danielwestheide.com

3. Agenda
• intro
• basics
• Scala’s object-oriented angle
• Scala’s functional foundation
• a rather powerful type system

4. Intro

5. Basics

6. Look ma, a value!
scala> val screamedGreeting = "HELLO WORLD!!!!"
screamedGreeting: String = HELLO WORLD!!!!

7. Look ma, a variable!
scala> var screamedGreeting = "HELLO WORLD!!!!"
screamedGreeting: String = HELLO WORLD!!!!

8. This looks familiar…
scala> val firstBangPos = screamedGreeting.indexOf('!')
firstBangPos: Int = 11
scala> val bang = screamedGreeting.charAt(firstBangPos)
bang: Char = !

9. Defining functions
def areaOfRightTriangle(a: Int, b: Int): Double = a * b / 2.0

10. Defining functions
def triangleArea(a: Int, b: Int, c: Int): Double = {
val s = (a + b + c) / 2
Math.sqrt(s * (s - a) * (s - b) * (s - c))
}

11. Scala’s object-oriented
angle

12. A simple class
class Point(x: Int, y: Int) {
require(x > 0 && y > 0)
println(s"Point created with x: \$x and y: \$y")
def toString: String = s"Point(x: \$x, y: \$y)"
}

13. Uniform access principle
class Point(x: Int, y: Int) {
require(x > 0 && y > 0)
println(s"Point created with x: \$x and y: \$y")
val toString: String = s"Point(x: \$x, y: \$y)"
}

14. Minimal boilerplate
class Point(val x: Int, val y: Int) {
require(x > 0 && y > 0)
println(s"Point created with x: \$x and y: \$y")
def toString: String = s"Point(x: \$x, y: \$y)"
}

15. Singleton objects
object Point {
def create(x: Int, y: Int): Point = new Point(x, y)
}

16. Everything is an object
val x = 5 + 3 * 2
val x = 5.+(3.*(2))

17. Scala’s functional
foundation

18. Expressions everywhere
def pluralize(singular: String, n: Int): String = {
val text = if (n > 1) singular + "s" else singular
n + " " + text
}

19. Functions as values
val isLong: (Email) => Boolean =
(email: Email) => email.body.length >= 100

20. Functions as values
val isLong: Email => Boolean = (email) => email.body.length >= 100

21. Functions as values
val isLong: Email => Boolean = email => email.body.length >= 100

22. Functions as values
val isLong: Email => Boolean = _.body.length >= 100

23. Functions as values
val isLong = (email: Email) => email.body.length >= 100

24. Higher-order functions
type EmailFilter = Email => Boolean
def newMailsForUser(mails: Seq[Email],
f: EmailFilter): Seq[Email] =
mails.filter(f)
newMailsForUser(myEmails, isLong)

25. Higher-order functions
type EmailFilter = Email => Boolean
def newMailsForUser(mails: Seq[Email],
f: EmailFilter): Seq[Email] =
mails.filter(f)
newMailsForUser(myEmails, _.body.length >= 100)

26. Higher-order functions
def minimumSize(n: Int): EmailFilter =
email => email.body.length >= n
def maximumSize(n: Int): EmailFilter = _.body.length <= n
scala> val isLong = minimumSize(100)
isLong: EmailFilter = \$\$Lambda\$1285/789178034@1bee0085

27. Currying
scala> val minimumSize: Int => Email => Boolean = size => email =>
email.body.length >= size
minimumSize: Int => (Email => Boolean) = \$
\$Lambda\$1305/559179052@8b670c0
scala> minimumSize(100)(email)
res0: Boolean = false
scala> val isLong = minimumSize(100)
isLong: Email => Boolean = \$\$Lambda\$1308/1940974851@55061418

28. Function composition
scala> val wordCount: Email => Int = _.body.split(' ').length
wordCount: Email => Int = \$\$Lambda\$1392/153337703@938e54a
scala> def higherThan(threshold: Int): Int => Boolean = _ >
threshold
higherThan: (threshold: Int)Int => Boolean
scala> val isLong = higherThan(100) compose wordCount
isLong: Email => Boolean = scala.Function1\$
\$Lambda\$1395/2035659988@71c1e70f

29. Function composition
scala> val wordCount: Email => Int = _.body.split(' ').length
wordCount: Email => Int = \$\$Lambda\$1392/153337703@938e54a
scala> def higherThan(threshold: Int): Int => Boolean = _ >
threshold
higherThan: (threshold: Int)Int => Boolean
scala> val isLong = wordCount andThen higherThan(100)
isLong: Email => Boolean = scala.Function1\$
\$Lambda\$1374/1682100030@112f8f8f

30. Algebraic data types
sealed trait TrafficLight
case object Red extends TrafficLight
case object Yellow extends TrafficLight
case object Green extends TrafficLight

31. Algebraic data types
def mayDrive(tl: TrafficLight): Boolean = tl match {
case Green => true
case Red | Yellow => false
}

32. Algebraic data types
scala> val me = ("Daniel", 37)
me: (String, Int) = (Daniel,37)
scala> val (name, age) = ("Daniel", 37)
name: String = Daniel
age: Int = 37

33. Algebraic data types
final case class Person(name: String, age: Int)
def greet(p: Person): String = p match {
case Person("Daniel", _) =>
"You look younger than ever!"
case Person(_, age) if age > 30 =>
"Yo, have you come here to die?"
case _ =>
“Hi!"
}

34. Functional error handling
sealed abstract class Option[+A]
final case class Some[+A](value: A) extends Option[A]
case object None extends Option[Nothing]

35. Functional error handling
def greet(name: Option[String]): String = name match {
case Some(name) => s"Nice to meet you, \$name!"
case None => "Hello, stranger!"
}

36. Functional error handling
final case class Person(name: String, age: Option[Int])
def find(name: String): Option[Person] = ???
scala> val name = find("Daniel").map(_.name).getOrElse("N/A")
displayedName: String = N/A
scala> val age = find("Daniel").flatMap(_.age)
age: Option[Int] = None

37. For comprehensions
for {
user <- find("Daniel")
age <- user.age
} yield age

38. For comprehensions
• Option
• List and other collection types
• Either
• Try
• Future

39. A rather powerful type
system

40. Type safety
val strings: Array[String] = Array("foo", "bar")
val objects: Array[Any] = strings
objects = 42

41. Type safety
final class Array[T]
sealed abstract class List[+A]

42. Type safety
• cheap value classes
• phantom types
• http://danielwestheide.com/blog/2015/06/28/put-
your-writes-where-your-master-is-compile-time-
restriction-of-slick-effect-types.html

43. Abstraction
def foo[A, B](tuple: (A, B)): A = ???

44. Abstraction
def higher[A <: Ordered[A]](x: A, y: A): A =
if (x > y) x else y
higher(5, 2)

45. Abstraction
def higher[A](x: A, y: A, ordering: Ordering[A]): A =
if (ordering.gt(x, y)) x else y
higher(5, 2, Ordering.Int)

46. Abstraction
def higher[A](x: A, y: A)(ordering: Ordering[A]): A =
if (ordering.gt(x, y)) x else y
higher(5, 2)(Ordering.Int)

47. Abstraction
def higher[A](x: A, y: A)(implicit ordering: Ordering[A]): A =
if (ordering.gt(x, y)) x else y
higher(5, 2)

48. Abstraction
object Ordering {
implicit object Int extends IntOrdering
}

49. Abstraction
def higher[A: Ordering](x: A, y: A): A =
if (implicitly[Ordering[A]].gt(x, y)) x else y
higher(5, 2)

50. Abstraction
def sequenceOption[A](as: List[Option[A]]): Option[List[A]] = ???
def sequenceTry[A](as: List[Try[A]]): Try[List[A]] = ???
def sequenceFuture[A](as: List[Future[A]]): Future[List[A]] = ???

51. Abstraction
trait Traverse[F[_]] {
def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]]
}
val ageOptions: List[Option[Age]] = List(Some(Age(23)), None)
val agesOption: Option[List[Age]] = ageOptions.sequence
val responseFutures: List[Future[Response]] = List(svc1(), svc2())
val responsesFuture: Future[List[Response]] =
responseFutures.sequence

52. Thank you!
Got any questions? :)