Featherweight Scala. Week 14

Similar documents
The Scala Experiment

The Scala Experiment {

Dependent Object Types - A foundation for Scala's type system

Dependent Object Types - A foundation for Scala s type system

G Programming Languages - Fall 2012

Axiomatic Rules. Lecture 18: Axiomatic Semantics & Type Safety. Correctness using Axioms & Rules. Axiomatic Rules. Steps in Proof

Scalable Component Abstractions

Mixin Up the ML Module System

Type Systems. Today. 1. Organizational Matters. 1. Organizational Matters. Lecture 1 Oct. 20th, 2004 Sebastian Maneth. 1. Organizational Matters

Overview. Elements of Programming Languages. Objects. Self-Reference

Type Systems. Pierce Ch. 3, 8, 11, 15 CSE

Lecture 09: Introduction to Scala

Jam: A Smooth Extension With Java Mixins

Scala. Fernando Medeiros Tomás Paim

Overview. Elements of Programming Languages. Objects. Self-Reference

The Trouble with Types

Overview. Elements of Programming Languages. Advanced constructs. Motivating inner class example

Lists. Michael P. Fourman. February 2, 2010

CS558 Programming Languages

Programming in Scala Second Edition

CS558 Programming Languages

Type-indexed functions in Generic Haskell

Shared Subtypes. Subtyping Recursive Parameterized Algebraic Data Types

Programming with Math and Logic

Static type safety guarantees for the operators of a relational database querying system. Cédric Lavanchy

CS558 Programming Languages

Concepts of program design Exam January 31, 13:30 16:30

Scala : an LLVM-targeted Scala compiler

DEFINING A LANGUAGE. Robert Harper. Friday, April 27, 12

Big Bang. Designing a Statically Typed Scripting Language. Pottayil Harisanker Menon, Zachary Palmer, Scott F. Smith, Alexander Rozenshteyn

CS152: Programming Languages. Lecture 24 Bounded Polymorphism; Classless OOP. Dan Grossman Spring 2011

Lambda Calculi With Polymorphism

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

Topic VIII. The state of the art Scala < > Scala By Example by M. Odersky. Programming Methods Laboratory, EPFL, 2008.

A Nominal Theory of Objects with Dependent Types

CSCI-GA Scripting Languages

Objects as Session-Typed Processes

Types and Type Inference

Generic polymorphism on steroids

The DOT Calculus. (Dependent Object Types) Nada Amin. June 18, Scala Days

Formal Semantics. Aspects to formalize. Lambda calculus. Approach

Lecture #13: Type Inference and Unification. Typing In the Language ML. Type Inference. Doing Type Inference

Life in a Post-Functional World

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

CS205: Scalable Software Systems

Compilers and computer architecture: Semantic analysis

CS 320: Concepts of Programming Languages

CS412/CS413. Introduction to Compilers Tim Teitelbaum. Lecture 17: Types and Type-Checking 25 Feb 08

INF 212/CS 253 Type Systems. Instructors: Harry Xu Crista Lopes

Improving Scala's Safe Type-Level Abstraction

Lecture 3: Recursion; Structural Induction

A Practical Optional Type System for Clojure. Ambrose Bonnaire-Sergeant

Classical Themes of Computer Science

Types and Programming Languages. Lecture 5. Extensions of simple types

TYPE INFERENCE. François Pottier. The Programming Languages Mentoring ICFP August 30, 2015

Modular Visitor Components

Scala in Martin Odersky

An introduction to C++ template programming

Types, Type Inference and Unification

Lecture 5: The Untyped λ-calculus

Lecture #23: Conversion and Type Inference

Chapter 5: Procedural abstraction. Function procedures. Function procedures. Proper procedures and function procedures

G Programming Languages - Fall 2012

Conversion vs. Subtyping. Lecture #23: Conversion and Type Inference. Integer Conversions. Conversions: Implicit vs. Explicit. Object x = "Hello";

n n Official Scala website n Scala API n

COMP 181. Agenda. Midterm topics. Today: type checking. Purpose of types. Type errors. Type checking

Harvard School of Engineering and Applied Sciences Computer Science 152

CS-XXX: Graduate Programming Languages. Lecture 22 Class-Based Object-Oriented Programming. Dan Grossman 2012

Type Checking and Type Inference

Scala Where It Came From Where It is Going

Chapter 11 :: Functional Languages

Object-Oriented Design

GADTs. Wouter Swierstra and Alejandro Serrano. Advanced functional programming - Lecture 7. [Faculty of Science Information and Computing Sciences]

Lecture 8: Summary of Haskell course + Type Level Programming

Programming Languages Fall 2014

COS 320. Compiling Techniques

Chapter 13: Reference. Why reference Typing Evaluation Store Typings Safety Notes

INF 212 ANALYSIS OF PROG. LANGS Type Systems. Instructors: Crista Lopes Copyright Instructors.

Formal Systems and their Applications

INF 212 ANALYSIS OF PROG. LANGS FUNCTION COMPOSITION. Instructors: Crista Lopes Copyright Instructors.

The DOT Calculus. (Dependent Object Types) Nada Amin. May 12, flatmap(oslo)

GADTs. Alejandro Serrano. AFP Summer School. [Faculty of Science Information and Computing Sciences]

At build time, not application time. Types serve as statically checkable invariants.

Tradeoffs. CSE 505: Programming Languages. Lecture 15 Subtyping. Where shall we add useful completeness? Where shall we add completeness?

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

A Type System for Functional Traversal-Based Aspects

Inheritance (Chapter 7)

Functional Languages. Hwansoo Han

On Understanding Data Abstraction... Revisited

Meta-programming with Names and Necessity p.1

Introducing Wybe a language for everyone

Programming Languages Lecture 15: Recursive Types & Subtyping

COMP 1130 Lambda Calculus. based on slides by Jeff Foster, U Maryland

GADTs. Wouter Swierstra. Advanced functional programming - Lecture 7. Faculty of Science Information and Computing Sciences

What Is Computer Science? The Scientific Study of Computation. Expressing or Describing

Programming with Types

Safe Instantiation in Generic Java

Practical Affine Types and Typestate-Oriented Programming

Unifying functional and object-oriented programming with Scala 齐琦, 海南大学计算机发展前沿系列课程

Don t Believe the Hype. CS152: Programming Languages. Lecture 21 Object-Oriented Programming. Class-based OOP. So what is OOP?

Transcription:

Featherweight Scala Week 14 1

Today Previously: Featherweight Java Today: Featherweight Scala Live research, unlike what you have seen so far. Focus today on path-dependent types Plan: 1. Rationale 2. Examples 3. Formal syntax and type checking 2

Based on... This presentation is based on the slides of the talk: Martin Odersky, François Garillot, Vincent Cremet, and Sergueï Lenglet. A Core Calculus for Scala Type Checking. Mathematical Foundations of Computer Science (MFCS), 2006. 3

Component Software State of the Art In principle, software should be constructed from re-usable parts ( components ). In practice, software is still most often written from scratch, more like a craft than an industry. Programming languages share part of the blame. Most existing languages offer only limited support for components. This holds in particular for statically typed languages such as Java and C#. 4

How To Do Better? Hypothesis 1: Languages for components need to be scalable; the same concepts should describe small as well as large parts. Hypothesis 2: Scalability can be achieved by unifying and generalizing functional and object-oriented programming concepts. 5

Why Unify FP and OOP? Both have complementary strengths for composition: Functional programming: Makes it easy to build interesting things from simple parts, using higher-order functions, algebraic types and pattern matching, parametric polymorphism. Object-oriented programming: Makes it easy to adapt and extend complex systems, using subtyping and inheritance, dynamic configurations, classes as partial abstractions. 6

An Experiment To validate our hypotheses we have designed and implemented a concrete programming language, Scala. An open-source distribution of Scala has been available since Jan 2004. Currently: 4000 downloads per month. Version 2 of the language has been released Jan 2006. A language by itself proves little; its applicability can only be validated by practical, serious use. 7

Scala Scala is an object-oriented and functional language which is completely interoperable with Java and.net. It removes some of the more arcane constructs of these environments and adds instead: (1) a uniform object model, (2) pattern matching and higher-order functions, (3) novel ways to abstract and compose programs. 8

Example: Peano Numbers To give a feel for the language, here s a Scala implementation of natural numbers that does not resort to a primitive number type. trait Nat { def iszero: Boolean; def pred: Nat; def succ: Nat = new Succ(this); def + (x: Nat): Nat = if (x.iszero) this else succ + x.pred; def - (x: Nat): Nat = if (x.iszero) this else pred - x.pred; } 9

Example: Peano Numbers... trait Nat { def iszero: Boolean; def pred: Nat; def succ: Nat = new Succ(this); def + (x: Nat): Nat = if (x.iszero) this else succ + x.pred; def - (x: Nat): Nat = if (x.iszero) this else pred - x.pred; } class Succ(n: Nat) extends Nat { def iszero: Boolean = false; def pred: Nat = n } 10

Example: Peano Numbers... trait Nat { def iszero: Boolean; def pred: Nat; def succ: Nat = new Succ(this); def + (x: Nat): Nat = if (x.iszero) this else succ + x.pred; def - (x: Nat): Nat = if (x.iszero) this else pred - x.pred; } object Zero extends Nat { def iszero: Boolean = true; def pred: Nat = throw new Error("Zero.pred"); } 11

Interoperability Scala is completely interoperable with Java (and with some qualifications also to C#). A Scala component can: access all methods and fields of a Java component, create instances of Java classes, inherit from Java classes and implement Java interfaces, be itself instantiated and called from a Java component. None of this requires glue code or special tools. This makes it very easy to mix Scala and Java components in one application. 12

Components A component is a program part, to be combined with other parts in larger applications. Requirement: Components should be reusable. To be reusable in new contexts, a component needs interfaces describing its provided as well as its required services. Most current components are not very reusable. Most current languages can specify only provided services, not required services. Note: Component API! 13

No Statics! A component should refer to other components not by hard links, but only through its required interfaces. Another way of expressing this is: All references of a component to others should be via its members or parameters. In particular, there should be no global static data or methods that are directly accessed by other components. This principle is not new. But it is surprisingly difficult to achieve, in particular when we extend it to classes. 14

Functors One established language abstraction for components are SML functors. Here, Component ˆ= Functor or Structure Interface ˆ= Signature Required Component ˆ= Functor Parameter Composition ˆ= Functor Application Sub-components are identified via sharing constraints. 15

Functors... Functors have shortcomings, however: No recursive references between components No inheritance with overriding Structures are not first class. 16

Modules are Objects In Scala: Component ˆ= Class Interface ˆ= Abstract Class, or Trait Required Component ˆ= Abstract Member or Self Composition ˆ= Modular Mixin Composition Advantages: Components instantiate to objects, which are first-class values. Recursive references between components are supported. Inheritance with overriding is supported. Sub-components are identified by name no explicit wiring is needed. 17

Language Constructs for Components Scala has three concepts which are particularly interesting in component systems. Abstract type members allow to abstract over types that are members of objects. Self-type annotations allow to abstract over the type of self. Modular mixin composition provides a flexible way to compose components and component types. Theoretical foundations: νobj calculus [Odersky et al., ECOOP03], Featherweight Scala [Odersky et al., MFCS06]. Scala s concepts subsume SML modules. More precisely, (generative) SML modules can be encoded in νobj, but not vice versa. 18

Component Abstraction There are two principal forms of abstraction in programming languages: parameterization (functional) abstract members (object-oriented) Scala supports both styles of abstraction for types as well as values. Both types and values can be parameters, and both can be abstract members. (In fact, Scala works with the functional/oo duality in that parameterization can be expressed by abstract members). 19

Abstract Types Here is a type of cells using object-oriented abstraction. trait AbsCell { type T val init: T private var value: T = init def get: T = value def set(x: T): Unit = { value = x } } The AbsCell class has an abstract type member T and an abstract value member init. Instances of that class can be created by implementing these abstract members with concrete definitions. val cell = new AbsCell { type T = Int; val init = 1 } cell.set(cell.get * 2) The type of cell is AbsCell { type T = Int }. 20

Path-dependent Types You can also use AbsCell without knowing the specific cell type: def reset(c: AbsCell): Unit = c.set(c.init); Why does this work? c.init has type c.t The method c.set has type c.t => Unit. So the formal parameter type and the argument type coincide. c.t is an instance of a path-dependent type. In general, such a type has the form x 0.....x n.t, where x 0 is an immutable value x 1,..., x n are immutable fields, and t is a type member of x n. 21

22

Safety Requirement Path-dependent types rely on the immutability of the prefix path. Here is an example where immutability is violated. var flip = false def f(): AbsCell = { flip =!flip if (flip) new AbsCell { type T = Int; val init = 1 } else new AbsCell { type T = String; val init = "" } } f().set(f().get) // illegal! Scala s type system does not admit the last statement, because the computed type of f().get would be f().t. This type is not well-formed, since the method call f() is not a path. 23

Example: Symbol Tables As an example, let s look at extensible components of real compilers. Compilers need to model symbols and types. Each aspect depends on the other. Both aspects require substantial pieces of code. The first attempt of writing a Scala compiler in Scala defined two global objects, one for each aspect: 24

First Attempt: Global Data object Symbols { object Types { class Symbol { class Type { def tpe: Types.Type def sym: Symbols.Symbol...... } } // static data for // static data // symbols} // for types } } Problems: 1. Symbols and Types contain hard references to each other. Hence, impossible to adapt one while keeping the other. 2. Symbols and Types contain static data not reentrant. 25

Second Attempt: Nesting Static data can be avoided by nesting the Symbols and Types objects in a common enclosing class: class SymbolTable { object Symbols { class Symbol { def tpe: Types.Type;... } } object Types { class Type {def sym: Symbols.Symbol;... } } } This solves the re-entrancy problem. But we gave up separate compilation, and did not solve the reuse problem. There are still hard references to types. 26

Third Attempt: A Component-Based Solution Question: How can one express the required services of a component? Answer: By abstracting over them! Two forms of abstraction: parameterization and abstract members. Only abstract members can express recursive dependencies, so we will use them. trait Symbols { trait Types { type Type type Symbol class Symbol { def tpe: Type } class Type { def sym: Symbol } } } Symbols and Types are now classes that each abstract over the identity of the other type. How can they be combined? 27

Modular Mixin Composition Here s how: class SymbolTable extends Symbols with Types Instances of the SymbolTable class contain all members of Symbols as well as all members of Types. Concrete definitions in either base class override abstract definitions in the other. Modular mixin composition generalizes the single inheritance + interfaces concept of Java and C#. It is similar to traits [Schaerli et al, ECOOP 2003], but is more flexible since base classes may contain state. 28

Fourth Attempt: Mixins + Self-Types The last solution modeled required types by abstract types. This is limiting, because one cannot instantiate or inherit an abstract type. A more general approach also makes use of self-types: class Symbols { this: Symbols with Types => class Symbol { def tpe: Type } } class Types { this: Types with Symbols => class Type { def sym: Symbol } } class SymbolTable extends Symbols with Types Self-types declare all of a components requirements. 29

Self-Types In a class declaration class C { this: T =>... } T is called a self-type of class C. If a self-type is given, it is taken as the type of this inside the class. Without an explicit type annotation, the self-type is taken to be the type of the class itself. Instead of this, you can also use another name for self. 30

Self-Types and Safety The self-type of a class must be a subtype of the self-types of all its base classes. When instantiating a class in a new expression, it is checked that the self-type of the class is a supertype of the type of the object being created. 31

Benefits 1. The presented scheme is very general any combination of static modules can be lifted to a assembly of components. 2. Components have documented interfaces for required as well as provided services. 3. Components can be multiply instantiated Re-entrancy is no problem. 4. Components can be flexibly extended and adapted. 32

Foundations A language like Scala is complicated. How can we convince ourselves that types are sound... and can be computed?! Featherweight Scala is a small calculus capturing: abstract type members path types mixins plus the usual OO dynamic dispatch 33

FS: Syntax Alphabets x, y, z, ϕ Variable a Value label A Type label Member decl M, N ::= val a : T = t Field decl / def def a ( y : S ) : T = t Method decl / def type A = T Type decl / def trait A extends T { ϕ M } Class def Term s, t, u ::= x Variable t.a Field selection s.a ( t ) Method call val x = new T ; t Object creation Path p ::= x p.a Type S, T, U ::= p.a Type selection p.type Singleton type T { ϕ M } Type signature 34

Example: Peano Numbers revisited trait Nat extends { this0 => def iszero(): Boolean def pred(): Nat trait Succ extends Nat { this1 => def iszero(): Boolean = false def pred(): Nat = this0 } def succ(): Nat = ( val result = new this0.succ; result ) def +(other: Nat): Nat = if (this0.iszero()) other else this0.pred().+(other.succ()) def -(other: Nat): Nat = if (other.iszero()) this0 else this0.pred().-(other.pred()) } val zero = new Nat { this0 => def iszero(): Boolean = true def pred(): Nat = error("zero.pred") } 35

Example: Generic Lists trait List extends Any { this0 => type Elem type ListOfElem = List { this1 => type Elem = this0.elem } def isempty(): Boolean def head(): this0.elem def tail(): this0.listofelem } trait Nil extends List { this0 => trait Cons extends List { this0 => def isempty(): Boolean = true val hd: this0.elem def head(): this0.elem = val tl: this0.listofelem error("nil.head") def isempty(): Boolean = false def tail(): this0.listofelem = def head(): this0.elem = hd error("nil.tail") def tail(): this0.listofelem = tl } } val list2 = new Cons { this0 => type Elem = Nat val hd: Nat = zero.succ().succ() val tl: this0.listofelem = new Nil { type Elem = Nat } } 36

Type Assignment x : T Γ Γ x : T (path-var) Γ s : S Γ t : T Γ T <: T Γ S def a ( x : T ) : U = u Γ t : S Γ S val a : T = u Γ t.a : T (select) Γ p : T (singleton) Γ p : p.type Γ s.a ( t ) : U (method) Γ, x : T t : S x fn(s) Γ T ϕ M c Γ T wf Γ val x = new T ; t : S (new) 37

Expansion and Membership i, Γ T i ϕ N i Γ T { ϕ M } ϕ ( i N i) M ( -signature) Γ p.type type A = T Γ T ϕ M Γ p.a ϕ M ( -type) Γ p.type trait A extends S Γ S ϕ N Γ p : T Γ T ϕ M Γ p.type [p/ϕ]m i ( -singleton) Γ T ϕ M ϕ / fn(m i ) Γ T M i ( -other) Γ p.a ϕ N ( -class) 38

Subtyping is reflexive, transitive, and obeys: Γ p : T Γ p.type <: T (singleton-<:) Γ p.type type A = S Γ p.a <: S (type-<:) Γ p.type trait A extends S Γ p.a <: S (class-<:) Γ T { ϕ M } <: T i (sig-<:) Γ p : q.type Γ q.type <: p.type (<:-singleton) Γ p.type type A = S i, Γ S <: T i Γ S <: p.a (<:-type) Γ S ϕ M Γ, ϕ : T { ϕ N } M N Γ S <: T { ϕ N } (<:-sig) 39

Left out... This is not the whole formalism. In the paper there is also: Judgements for member subtyping and well-formedness wf. An operational semantics. An algorithmic formulation of the calculus, with the following differences: A notion of used definitions was added to the rules which act as locks to prevent cycles in typing derivations. Some judgement forms have been split. Transitivity has been eliminated in the subtyping rules A proof of the decidability of typing and subtyping in Algorthmic FS 40

Future Work 1. Soundness proof for operational semantics (hopefully finished soon) 2. The lock-free version of the calculus is more expressive than the algorithmic one. There are programs that type-check lock-free but fail due to a cycle in the locking version. Can we refine locks so that the two versions become equivalent? 3. Extensions of the calculus, with Polymorphic methods Type bounds Abstract inheritance/higher-order polymorphism 4. A call-by-value version of the calculus 41

Relationship between Scala and Other Languages Main influences on the Scala design: Java, C# for their syntax, basic types, and class libraries, Smalltalk for its uniform object model, Beta for systematic nesting, ML, Haskell for many of the functional aspects. OCaml, OHaskell, PLT-Scheme, as other combinations of FP and OOP. Pizza, Multi-Java, Nice as other extensions of Java with functional ideas. (Too many influences in details to list them all) In the other direction, Scala has its own influence. C# 3.0 has comprehensions, closures... and => :-) 42

Related Language Research Mixin composition: Bracha (linear), Duggan, Hirschkowitz (mixin-modules), Schaerli et al. (traits), Flatt et al. (units, Jiazzi), Zenger (Keris). Abstract type members: Even more powerful are virtual classes (Cook, Ernst, Ostermann) Explicit self-types: Vuillon and Rémy (OCaml) 43

Conclusion Despite 10+ years of research, there are still interesting things to be discovered at the intersection of functional and object-oriented programming. Be ready to modify the techniques you have seen in this class. Theory and practice feed each other... 44