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.

7abf07f13ed689874500c08bc7fbd543?s=128

Daniel Westheide

April 26, 2018
Tweet

Transcript

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

    USER GROUP TH
  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
  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[1] = 42
  41. Type safety final class Array[T] sealed abstract class List[+A]

  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
  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? :)