Your SlideShare is downloading.
×

- 1. From DOT to Dotty Martin Odersky ScalaX, London, 8 December 2016
- 2. DOT: Foundations dotty: Prototype … for future Scala
- 3. Why Do Foundations Matter? • They help ensure properties such as type soundness. • They serve as a feedback loop for language design. • They help detect hidden connections between language features.
- 4. Why Not Pick Existing Foundations? • Because they would lead to variants of existing languages. • Foundations are formative!
- 5. Our Aim We are looking for a minimal theory that can model 1. type parameterization, 2. modules, 3. objects and classes. minimal: We do not deal with inheritance here.
- 6. Our Aim We are looking for a minimal theory that can model 1. type parameterization, 2. modules, 3. objects and classes. There were several attempts before, including νObj, which was proposed as a basis for Scala (ECOOP 2003). But none of them felt completely canonical or minimal. Related: 1ML, which can model (1) and (2) by mapping to System F.
- 7. Dependent Types • We will model modules as objects with type members. • This requires a notion of dependent type - the type referred to by a type member depends on the owning value. • In Scala we restrict dependencies to paths. • In the calculus presented here we restrict it further to variables. Variable x Path p = x | p.a
- 8. Dependent Types in Code We can model heterogeneous maps like this:
- 9. Dependent Types in Code
- 10. Dependent Types in Code
- 11. Foundations: DOT The DOT calculus is intended to be a minimal foundation of Scala. Its type structure is a blueprint for the types used internally in the compiler.
- 12. DOT Terms • Translated to Scala notation, the language covered by DOT is: Value v = (x: T) => t Function new { x: T => d } Object Definition d = def a = t Method definition type A = T Type Term t = v Value x Variable t1(t2) Application t.a Selection { val x = t1; t2 } Local definition.
- 13. DOT Types The Types covered by DOT are: Type T = Any Top type Nothing Bottom type x.A Selection (x: T1) => T2 Function { def a: T } Method declaration { type T >: T1 <: T2 } Type declaration T1 & T2 Intersection { x => T } Recursion
- 14. DOT Types The Types covered by DOT are: Type T = Any Top type Nothing Bottom type x.A Selection (x: T1) => T2 Function { def a: T } Method declaration { type T >: T1 <: T2 } Type declaration T1 & T2 Intersection { x => T } Recursion Should Scala have these?
- 15. DOT Types The Types covered by DOT are: Type T = Any Top type Nothing Bottom type x.A Selection (x: T1) => T2 Function { def a: T } Method declaration { type T >: T1 <: T2 } Type declaration T1 & T2 Intersection { x => T } Recursion Will replace the T1 with T2 syntax
- 16. DOT Types The Types covered by DOT are: Type T = Any Top type Nothing Bottom type x.A Selection (x: T1) => T2 Function { def a: T } Method declaration { type T >: T1 <: T2 } Type declaration T1 & T2 Intersection { x => T } RecursionScala has only refinements T { d1 … dn} with this as self reference.
- 17. DOT Syntax in Greek Note: terms are in ANF form. This simplifies some things, but is not essential.
- 18. Type Assignment
- 19. Definition Type Assignment
- 20. Subtyping
- 21. Expressiveness Simple as the model is, it is actually quite expressive. Directly representable: ▶ type parameters ▶ variance ▶ nominal typing ▶ generative modules ▶ self types ▶ ADTs and simple classes Requires smallish extension: ▶ Classes with inheritance
- 22. Meta Theory Simple as the model is, the soundness proof of DOT was surprisingly hard. ▶ Attempts were made since about 2008. ▶ Previous publications (FOOL 12, OOPSLA 14) report about (some) advances and (lots of) difficulties. ▶ Essential challenge: Subtyping theories are programmer-definable.
- 23. Programmer-Definable Theorems In Scala and DOT, the subtyping relation is given in part by user-definable definitions: type T >: S <: U { T: S .. U } This makes T a supertype of S and a subtype of U. By transitivity, S <: U. So the type definition above proves a subtype relationship which was potentially not provable before.
- 24. Bad Bounds What if the bounds are non-sensical? Example: type T >: Any <: Nothing By the same argument as before, this implies that Any <: Nothing Once we have that, again by transitivity we get S <: T for arbitrary S and T. That is, the subtyping relations collapses to a single point! This means that most proof techniques for soundness fail.
- 25. Dealing with it Observation: To prove preservation, we need to reason at the top-level only about environments that arise from an actual computation Such environments correspond to run-time stores which binds variables to values. And values have guaranteed good bounds because all type members in definitions are aliases. By an elaborate argument one can make use of this observation to show soundness.
- 26. For Details
- 27. Consequences for Language Design • So far: Some soundness issues were known, but it was not clear how to fix them. • Can one impose restrictions to guarantee good bounds? • Has been tried for a while but was not complete. • The meta theory taught us a principle to ensure soundness: Every prefix p of a type selection p.A must be a computed value.
- 28. Things To Avoid trait BadBounds { type A >: Any <: Nothing } lazy val x: BadBounds = ??? BadBounds # A val x: BadBounds = null Need to drastically restrict types we can write in a lazy val: Only concrete types with good bounds are allowed.Need to drastically restrict types we can write in a projection: Only concrete types with good bounds are allowed.
- 29. Things To Avoid trait BadBounds { type A >: Any <: Nothing } lazy val x: BadBounds = ??? BadBounds # A val x: BadBounds = null Need to track null in the type system (straightforward) Need to track initialization status (hard)
- 30. dotty dotty is the working name for our new Scala compiler. • Builds on DOT in its internal data structures. • Generics get expressed as type members. • Supports the next iteration(s) of the Scala programming language.
- 31. A Whirlwind Tour Of Dotty Constructs
- 32. Dropped Features DelayedInit Macros Existential Types Procedure Syntax Early Initializers General Type Projection def run() { ... } Will be rewritten automatically to def run(): Unit = { ... } class Foo extends DelayedInit class C extends { val x = e } with D Use trait parameters instead T # X - Was shown to be unsound for general types T. - Projection C#X from class types C still available. (the reflection based kind) def m(...) = macro impl(...) C[U] forSome { type U } Wildcards C[_]still supported.
- 33. Implemented New Features Multiversal Equality Intersection Types Union types Trait parameters Function arity adaptation pairs.map((x, y) => x + y) instead of pairs.map { case (x, y) => x + y } T & U - replaces T with U - is commutative T | U avoids huge lubs @static methods and fields non-blocking lazy vals trait T(x: Int) { ... } object O { @static val x = ... @static def f() = ... } lazy val x = ... // thread-local @volatile lazy val x - ... // thread-safe, // avoids dead-locks type-safe ==, !=
- 34. And Further Down the Road ? Implicit Function Types scala.meta scrap all 22’s effects
- 35. Implicit Function Types
- 36. What Are Implicit Function Types? and what is “Contextual Abstraction”?
- 37. “Abstraction” The ability to name a concept and use just the name afterward
- 38. “Contextual” The context comprises all the inputs that let a program do its work, including: • configuration data • capabilities • dependency injection • type class instances
- 39. Implicit Parameters • Technique of choice to pass inputs to program parts that need them. • Advantage over normal parameters: No boilerplate code to pass them along the edges of a call graph. • But we still need to declare them as parameters everywhere they are passed!
- 40. Example: Transaction Handling
- 41. Example (2)
- 42. Example (3)
- 43. Example Run
- 44. Can We Do Better? • Problem: Boilerplate code for declaring implicit parameters • Repeating this 3x does not look so terrible. • But in the dotty compiler there are 2641(!) occurrences of • We’d like to get rid of them.
- 45. Towards A Solution Let’s massage the definition of f1 a bit: f1’s right hand side is now an implicit function value. What is its type? So far: Transaction => Int From now on: implicit Transaction => Int or, desugared: ImplicitFunction1[Transaction, Int]
- 46. Inside ImplicitFunction1 ImplicitFunction1 can be thought of being defined as follows: Analogously for all other arities.
- 47. Two Rules for Typing 1. Implicit functions get implicit arguments just like implicit methods. Given: f expands to f(a) 2. Implicit functions get created on demand. If the expected type of b is implicit A => B, then b expands to implicit _: A => b
- 48. Revised Example: But where does current come from?
- 49. Revised Example (2)
- 50. Summary • Implicit function types are a neat way to abstract over contexts. • It’s a very powerful feature, because it allows one to inject implicit values in a scope simply by defining type. • This opens up a lot of possibilities. • I expect it will fundamentally affect the kind of Scala code in the future.
- 51. When Can I Expect This? Scala 2.12 Scala 2.13 Scala 3.0 TASTY, middle end? stdlib collections dotty MVP dotty 0.x releases 2016 backend, classpath handling Scala 2.14 2017 2018 This is open source work, depends on community’s contributions. à Roadmap is tentative, no promises: “MVP” = minimal viable prototype
- 52. Contributors Theory (DOT): Nada Amin, Tiark Rompf, Sandro Stucki, Samuel Grütter. based on previous work by Adriaan Moors, Donna Malayeri, Geoffrey Washburn, and others. Implementation (dotty): Many contributors, including Dmitry Petrashko Nicolas Stucki Guillaume Martres Sebastien Douraene Felix Mulder Ondrej Lhotak Liu Fengyun Vera Salvisberg Close collaboration with scalac team Adriaan Moors Seth Tisue Jason Zaugg Stefan Zeiger Lukas Rytz
- 53. Find out more on scala-lang.org/blog
- 54. Thank You