RECONSTRUCTING SCALA

Felix Mulder

Slides: felixmulder.com

Interrupt me, guyz, plz

LET’S TAKE A TRIP DOWN MEMORY LANE

"We’d like to announce availability of the first implementation of the Scala programming language. Scala smoothly integrates object-oriented and functional programming. It is designed to express common programming patterns in a concise, elegant, and type-safe way. Scala introduces several innovative language constructs. For instance:
  • Abstract types and mixin composition unify ideas from object and module systsems.
  • Pattern matching over class hierarchies unifies functional and object-oriented data access. It greatly simplifies the processing of XML trees.
  • A flexible syntax and type system enables the construction of advanced libraries and new domain specific languages.
At the same time, Scala is compatible with Java. Java libraries and frameworks can be used without glue code or additional declarations. The current implementation of Scala runs on Java VM. It requires JDK 1.4 and can run on Windows, MacOS, Linux, Solaris, and most other operating systems. A .net version of Scala is currently under development."

- 2004-01-20 by Martin Odersky

Scala 1.x

  • Algebraic Data Types
  • Function Values
  • Generics
  • By-name Parameters

    def foo(i: => Int): Int = {
      if (true) 0
      else i
    }

Scala 1.x

  • Parameterless methods

    def length: Int = ???
  • Always eta-expanded partially applied functions:

    def map(f: Int => Int)(xs: Iterable[Int]) = ???
    val square = map(x => x * x)

Scala 1.x

  • Surprising behaviour:

    println("wat".length)
    // res: <function>

Scala 2.0 - alias puberty,

or

“a time where cake didn’t make them fat”

Scala 2.0

  • Auto-add () if reference is to a nullary function
  • Eta-expand iff:
    • Expected type is a function
    • Missing parameters are specified with "_"

      val square = map(x => x * x)(_)
  • New keywords: implicit, match, requires
  • Private qualifiers: private[X]
  • Implicit parameters

Scala 2.1

  • Multi-line strings
  • Class literals: classOf[X]
  • Protected qualifiers: protected[X]

Scala 2.3

  • Procedure syntax
  • Tuples
  • Extractors using: unapply
  • Current syntax for self-types introduced
  • Case classes cannot be abstract - uh, oh Martin!

Scala 2.4-2.5

  • private[this]
  • Early initialization

    trait T { val x: Int; println(x) }
    class C extends { val x = 5 } with T
    
    new C // res: 5

Scala 2.6 - Sturm und Drang

  • Existential Types
  • Lazy Values
  • Structural Types
  • Higher-kinded types?

Scala 2.7 - revenge of the case class

  • Case classes may now be abstract again - hooray Martin!
  • Case classes get extractor methods by default
  • Case classes allowed to have companions
  • Mixing Scala and Java code in the same project is now allowed

Scala 2.8 - end of puberty

  • Redesigned Collections
  • Specialization using: @specialized
  • Named and default arguments
  • Package objects

Scala 2.9 - beefing up

  • Parallel collections
  • DelayedInit
  • App

Scala 2.10 - the end of adolescence

  • Value Classes
  • Implicit Classes
  • String Interpolation
  • Dependent Method Types
  • Scala Reflection
  • Macros

Scala as it is Today

Common theme up until 2.10

  • Growth, experimentation, maturing features
  • Nearly nothing removed - lost features replaced
  • Future 2.X versions - more stability-oriented

Why the rapid growth?

  • Adoption

    - people asked for features

  • It was developed at a University

    - PhD students wanted to see their work applied

Reconstructing Scala

Lessons learned in 2.10-2.1X

Spirit of 2.0-2.9

Dotty

  • Simplification
  • Developer Usability
  • Correctness - proven foundation, “DOT”
  • Scala 3

ERRGONOMICS

  • Awesome Error Messages
  • Dottydoc Compiler

Getting Started with Dotty

  • Scastie
  • $ brew install lampepfl/brew/dotty
  • $ sbt new lampepfl/dotty.g8

Let’s try some things out

  • Brew
  • IDE
  • Dottydoc

Awesome Error Messages

Let’s try some things out

  • Brew
  • IDE
  • Dottydoc

Key Differences

  • Explicit Implicits
  • No more procedure syntax
  • Union Types
  • Intersection Types
  • Trait Parameters
  • Implicit Functions
  • Enums
  • Callgraph, automatic specialization
  • Library defined rewrites
  • TASTY
  • IDE using Visual Studio Code

TYPE INFERENCE

Dotty and Types: The Story So Far

by Guillaume Martres

trait List[+A] {
  def foldRight[B](z: B)(f: (A, B) => B): B = ???
}

List(1, 2, 3).foldRight(List.empty)(_ :: _) // scalac: nope 
                                            //  dotty:  yep 

Implicit Functions

implicit A => B

Tagless Final Interpreters

sealed trait Exp[T]
final case class Add[T](t1: T, t2: T) extends Exp[T]
final case class Lit[T](i: Int) extends Exp[T]
implicit val evalInt: Eval[Int] = new Eval[Int] {
  def apply(exp: Exp[Int])): Int = exp match {
    case Add(t1, t2) => apply(t1) + apply(t2)
    case Lit(const)  => const
  }
}
// 8 + (2 + 2 + 2)
def expr1[T](implicit eval: Eval[T]): T =
  eval(Add(Lit(8), Add(Lit(2), Add(Lit(2), Lit(2)))))

What if we want multiplication?

sealed trait Exp[T]
final case class Add[T](t1: T, t2: T) extends Exp[T]
final case class Mul[T](i: Int) extends Exp[T]
final case class Lit[T](i: Int) extends Exp[T]
implicit val evalInt: Eval[Int] = new Eval[Int] {
  def apply(exp: Exp[Int])): Int = exp match {
    case Add(t1, t2) => apply(t1) + apply(t2)
    case Mul(t1, t2) => apply(t1) * apply(t2)
    case Lit(const)  => const
  }
}
// 8 + (2 + 2 + 2)
def expr1[T](implicit eval: Eval[T]): T =
  eval(Add(Lit(8), Add(Lit(2), Add(Lit(2), Lit(2)))))

def expr2[T](implicit eval: Eval[T]): T =
  eval(Add(Lit(8), Mul(Lit(2), Lit(3))))
trait Exp[T] {
  def add(t1: T, t2: T): T
  def lit(i: Int) = T
}

implicit val intExp: Exp[Int] = new Exp[Int] {
  def add(t1: Int, t2: Int) = t1 + t2
  def lit(i: Int) = i
}

// 8 + (2 + 2 + 2)
def expr1[T](implicit e: Exp[T]) =
  e.add(e.lit(8), e.add(e.lit(2), e.add(e.lit(2), e.lit(2))))
object ExpSyntax {
  def lit[T](i: Int)(implicit e: Exp[T]): T = e.lit(i)
  def add[T](l: T, r: T)(implicit e: Exp[T]): T = e.add(l, r)
}
import ExpSyntax._
trait Exp[T] {
  def add(t1: T, t2: T): T
  def lit(i: Int) = T
}

implicit val intExp: Exp[Int] = new Exp[Int] {
  def add(t1: Int, t2: Int) = t1 + t2
  def lit(i: Int) = i
}

// 8 + (2 + 2 + 2)
def expr1[T: Exp] =
  add(lit(8), add(lit(2), add(lit(2), lit(2))))
trait Mul[T] {
  def mul(t1: T, t2: T): T
}

implicit val intMul: Mul[Int] = new Mul[Int] {
  def mul(i1: Int, i2: Int) = i1 * i2
}

// 8 + 3 * 2
def expr2[T : Exp : Mul] =
  add(lit(8), mul(lit(3), lit(2)))
type Ring[T] = implicit (Exp[T], Mul[T]) => T

// 8 + 3 * 2
def expr2: Ring[T] =
  add(lit(8), mul(lit(3), lit(2)))
Revisiting Tagless Final Interpreters - Olivier Blanvillain

REPL

Issues

ERROR MESSAGES

Issue #1589

IDEs & TOOLING

Issues

Build Tools

Answers

  • Why 0.1.2-RC1? Because reasons
  • I have a suggestion! TTYL <3
  • Macros? Soon! Via scalamacros and scalameta
  • SI-2712? Yes
  • Next release? Six weeks!

Question?

Thank you!