Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Essence and Fundamentals of Scala

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.

Daniel Westheide

April 26, 2018
Tweet

More Decks by Daniel Westheide

Other Decks in Programming

Transcript

  1. The Essence and
    Fundamentals of
    Scala
    26,04.2018

    ERFURT / JAVA USER GROUP TH

    View Slide

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

    View Slide

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

    View Slide

  4. Intro

    View Slide

  5. Basics

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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))
    }

    View Slide

  11. Scala’s object-oriented
    angle

    View Slide

  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)"
    }

    View Slide

  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)"
    }

    View Slide

  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)"
    }

    View Slide

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

    View Slide

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

    View Slide

  17. Scala’s functional
    foundation

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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!"
    }

    View Slide

  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]

    View Slide

  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!"
    }

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  39. A rather powerful type
    system

    View Slide

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

    View Slide

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

    View Slide

  42. Type safety
    • cheap value classes
    • phantom types
    • “there is a Prolog in the type system”
    • http://danielwestheide.com/blog/2015/06/28/put-
    your-writes-where-your-master-is-compile-time-
    restriction-of-slick-effect-types.html

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

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

    View Slide

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

    View Slide

  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]] = ???

    View Slide

  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

    View Slide

  52. Thank you!
    Got any questions? :)

    View Slide