pizza compiler

41
The Pizza Compiler The Pizza Compiler extending Java in a functional way Sander Mak Centre for Software Technology, Universiteit Utrecht October 19, 2006 Center for Software Technology Sander Mak

Upload: sander-mak-sandermak

Post on 27-Jan-2015

128 views

Category:

Technology


1 download

DESCRIPTION

Pizza is a language that extends Java (1.4) with first-class functions, pattern matching and generics. Although dated, a closer look into the compiler is interesting.

TRANSCRIPT

Page 1: Pizza compiler

The Pizza Compiler

The Pizza Compilerextending Java in a functional way

Sander Mak

Centre for Software Technology, Universiteit Utrecht

October 19, 2006

Center for Software Technology Sander Mak

Page 2: Pizza compiler

The Pizza Compiler > Introduction

Outline

1 Introduction

2 Features

3 Rough edges

4 Related work

5 Conclusion

Center for Software Technology Sander Mak

Page 3: Pizza compiler

The Pizza Compiler > Introduction

About the Pizza project

Main Pizza contributors: Martin Odersky and Philip Wadler

Project started 1996 (Java 1.1 era)

Last version: 2002

So, why study an abandoned research project?

1 The ideas are still very interesting (they don’t age as fast as ourcomputers...)!

2 Better insight into design of Java 1.5

3 See how FP and OO can integrate

Center for Software Technology Sander Mak

Page 4: Pizza compiler

The Pizza Compiler > Introduction

Characteristics

Pizza language

Language adds three novel features:1 Parametric Polymorphism (generics)2 First class functions3 Algebraic datatypes

Pizza is a strict superset of Java

Language defined by translation intoJava

Pizza compiler

Compiler outputs bytecode directly

But, can output Java sources(pre-processor)

No dependency chasing

Of course: written in Pizza!

Center for Software Technology Sander Mak

Page 5: Pizza compiler

The Pizza Compiler > Introduction

Let’s move on to the features...

Ask questions if code is unclear

Center for Software Technology Sander Mak

Page 6: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Introducing Parameterized Types

Collections API (List, Hashtable, etc.) relies on genericprogramming

Up to Java 1.4 simulated by implementing them with the top ofthe type-hierarchy: Object

class Hashtable { .. public Object get(Object key) { .. } }

This leads to runtime type errors: programmer is responsible fortypes in a collection consistent

Parameterized types solve this problem

class Hashtable<K,V> { .. public V get(K key) { .. } }

No runtime checks necessary anymore

Center for Software Technology Sander Mak

Page 7: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Introducing Parameterized Types

Type parameters can have bounds:

class Tree<A implements TreeNode> { .. }class Maximum<A implements Comparable<A>> { .. }

Very similar to Haskell type-classes!

Primitive types (int, char) are allowed as instantiation fortype parameters

Omitting a bound implies the bound Object

Instantiating parameterized types:

List<int> = new List<int>();Tree<TreeNodeImpl> = new Tree<TreeNodeImpl>();

Center for Software Technology Sander Mak

Page 8: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Two translation schemes

Homogeneous (Type Erasure)

Creates one Java implementation for each parameterized class

Replace type variables by their respective bounds

Parameterized array types are replaced with pizza.support.arraytype

Heterogeneous

Creates specialised instances for each instantiation of typeparameter

Even at runtime specialisations can be generated, using reflection

Center for Software Technology Sander Mak

Page 9: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Translation scheme - Homogeneous (Type erasure)

Pizza source

class TranslateMe<A, B extends String> {public A test(A a, B[] bs){

B b = bs[0];return a; }

}

Translated Java source

class TranslateMe {public Object test(Object s, pizza.support.array bs) {

String b = (String)bs.at(0);return a;

}}

Problem: how can primitive types be used as an object?

Center for Software Technology Sander Mak

Page 10: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Invariant/covariant subtyping

Is List<String> a subtype of List<Object>?

Consider:

class Loophole {public static String loophole (Byte y) {LinkedList<String> xs = new LinkedList<String>();LinkedList<Object> ys = xs; // aliasingys.add(y);return xs.iterator().next();

}}

The aliasing of xs and ys will result in a compile time error, toguarantee soundness.Therefore, these types are not involved in a subtyping relation:parameterized types have invariant subtyping.

Center for Software Technology Sander Mak

Page 11: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Invariant/covariant subtyping

Is List<String> a subtype of List<Object>?

Consider:

class Loophole {public static String loophole (Byte y) {

LinkedList<String> xs = new LinkedList<String>();LinkedList<Object> ys = xs; // aliasingys.add(y);return xs.iterator().next();

}}

The aliasing of xs and ys will result in a compile time error, toguarantee soundness.Therefore, these types are not involved in a subtyping relation:parameterized types have invariant subtyping.

Center for Software Technology Sander Mak

Page 12: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Invariant/covariant subtyping

Is List<String> a subtype of List<Object>?

Consider:

class Loophole {public static String loophole (Byte y) {

LinkedList<String> xs = new LinkedList<String>();LinkedList<Object> ys = xs; // aliasingys.add(y);return xs.iterator().next();

}}

The aliasing of xs and ys will result in a compile time error, toguarantee soundness.Therefore, these types are not involved in a subtyping relation:parameterized types have invariant subtyping.

Center for Software Technology Sander Mak

Page 13: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Invariant/covariant subtyping

But.. String[] is a subtype of Object[] (covariant)!

Consider:

class Loophole {public static String loophole (Byte y) {

String[] xs = new String[1];Object[] ys = xs; // aliasingys[0] = y;return xs[0];

}}

The assignment ys[0] = x will give a runtime error.This is possible because JVM tracks runtime types of arrays!

Center for Software Technology Sander Mak

Page 14: Pizza compiler

The Pizza Compiler > Features > Parametric Polymorphism

Invariant/covariant subtyping

But.. String[] is a subtype of Object[] (covariant)!

Consider:

class Loophole {public static String loophole (Byte y) {

String[] xs = new String[1];Object[] ys = xs; // aliasingys[0] = y;return xs[0];

}}

The assignment ys[0] = x will give a runtime error.This is possible because JVM tracks runtime types of arrays!

Center for Software Technology Sander Mak

Page 15: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Datatypes in Haskell

data List a = Nil | Cons a (List a)

We could emulate this using a parameterized class List<A>, but...

sum Nil = 0sum (Cons e es) = e + sum es

Elegant pattern matching is still not possible.

Observation

1 Objects and inheritance ease adding new constructors to types,given that #functions is relatively fixed

2 Algebraic datatypes ease adding new functions over types usingmatching, give that constructors are relatively fixed

Can we combine this?

Center for Software Technology Sander Mak

Page 16: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Datatypes in Haskell

data List a = Nil | Cons a (List a)

We could emulate this using a parameterized class List<A>, but...

sum Nil = 0sum (Cons e es) = e + sum es

Elegant pattern matching is still not possible.

Observation

1 Objects and inheritance ease adding new constructors to types,given that #functions is relatively fixed

2 Algebraic datatypes ease adding new functions over types usingmatching, give that constructors are relatively fixed

Can we combine this?

Center for Software Technology Sander Mak

Page 17: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Sum using ADT and pattern matching

class List<A> {case Nil;case Cons(A head, List<A> tail);

}

We can now define sum:

public static int sum(List<int> l){switch (l) {case Nil :

return 0;case Cons (int e, List<int> es) :

return (e + sum(es));}

Unfortunately the compiler didn’t agree...

Center for Software Technology Sander Mak

Page 18: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Sum using ADT and pattern matching

class List<A> {case Nil;case Cons(A head, List<A> tail);

}

We can now define sum:

public static int sum(List<int> l){switch (l) {

case Nil :return 0;

case Cons (int e, List<int> es) :return (e + sum(es));

}

Unfortunately the compiler didn’t agree...

Center for Software Technology Sander Mak

Page 19: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Sum using ADT and pattern matching

class List<A> {case Nil;case Cons(A head, List<A> tail);

}

We can now define sum:

public static int sum(List<int> l){switch (l) {

case Nil :return 0;

case Cons (int e, List<int> es) :return (e + sum(es));

}

Unfortunately the compiler didn’t agree...

Center for Software Technology Sander Mak

Page 20: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Sum using ADT and pattern matching

Compiler error

An exception has occurred in the compiler. (v1.0g)Please file a bug report at Sourceforge.net

Thank you.Exception in thread "main" java.lang.StackOverflowError

After some experimentation:

Fix

public static Integer sum(List<Integer> l){switch (l) {

case Nil :return new Integer(0);

case Cons (Integer e, List<Integer> es):return new Integer(e.intValue() + sum(es).intValue());

}

Center for Software Technology Sander Mak

Page 21: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Sum using ADT and pattern matching

Compiler error

An exception has occurred in the compiler. (v1.0g)Please file a bug report at Sourceforge.net

Thank you.Exception in thread "main" java.lang.StackOverflowError

After some experimentation:

Fix

public static Integer sum(List<Integer> l){switch (l) {

case Nil :return new Integer(0);

case Cons (Integer e, List<Integer> es):return new Integer(e.intValue() + sum(es).intValue());

}

Center for Software Technology Sander Mak

Page 22: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Pattern matching

Wildcard pattern is allowed for unused variablesNested pattern matching is supportedOverlapping patterns are detected, and not allowedPizza compiler AST is an Algebraic datatypeEnumeration types are within reach (as in Java 1.5):

class Color {case Red;case Blue;

String toString() {switch (this) {case Red: return “Red”;case Blue: return “Blue”;}

}}

Center for Software Technology Sander Mak

Page 23: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Translation to Java

class List {static final List Nil = new List(1);public final int Listtag;static List Cons(Object head, List tail) {

return (List)new Cons(head, tail);}List(int Listtag) {

super(); this.Listtag = Listtag;}static class Cons extends List {

Object head; List tail;Cons(Object head, List tail) {

super(2); this.head = head;this.tail = tail;

}}

}

Center for Software Technology Sander Mak

Page 24: Pizza compiler

The Pizza Compiler > Features > Algebraic Datatypes

Translation to Java

class List {static final List Nil = new List(1);public final int Listtag;static List Cons(Object head, List tail) {

return (List)new Cons(head, tail);}List(int Listtag) {

super(); this.Listtag = Listtag;}static class Cons extends List {

Object head; List tail;Cons(Object head, List tail) {

super(2); this.head = head;this.tail = tail;

}}

}

Constructing an instance of List<int> in Pizza

List<int> nums = List.Cons(1,List.Cons(2,List.Nil));

Center for Software Technology Sander Mak

Page 25: Pizza compiler

The Pizza Compiler > Features > First-class functions

Why pass functions?

Let’s examine a sorted set implementation

Current practice

class Set implements SortedSet {public Set(Comparator c){..}.. }

interface Comparator {int compare(Object o1, Object o2); }

Extra class definition and implementation necessary to provide orderingfunctionality...

What if...

class TreeSet {public TreeSet((Object,Object) -> int compare)

{ .. order = compare(o1,o2); .. } .. }

Center for Software Technology Sander Mak

Page 26: Pizza compiler

The Pizza Compiler > Features > First-class functions

Why pass functions?

Let’s examine a sorted set implementation

Current practice

class Set implements SortedSet {public Set(Comparator c){..}.. }

interface Comparator {int compare(Object o1, Object o2); }

Extra class definition and implementation necessary to provide orderingfunctionality...

What if...

class TreeSet {public TreeSet((Object,Object) -> int compare)

{ .. order = compare(o1,o2); .. } .. }

Center for Software Technology Sander Mak

Page 27: Pizza compiler

The Pizza Compiler > Features > First-class functions

Syntax

Pizza adds function types and values to Java

Function declaration (member or local)

(Object,Object) -> int compare =fun (Object o1,Object o2) -> int {.. implementation ..};

Implementation may reference o1, o2, and everything in enclosing class(not itself!)

Function declaration top-level

public int compare(Object o1, Object o2) {.. implementation ..};

Top-level functions can be passed around by using their name withoutparentheses.

Comparator example is solvable using anonymous class instances.But how powerful/scalable is this approach?

Center for Software Technology Sander Mak

Page 28: Pizza compiler

The Pizza Compiler > Features > First-class functions

Let’s make a Pizza foldr!

public class Foldr {static <D,E> D foldr((E,D) -> D f, D unit, List<E> l){switch(l){

case Nil: return unit;case Cons(E h, List<E> t): return f(h,foldr(f,unit,t));

}}

Type of foldr in Haskell

foldr :: (a → b → b) → b → [a] → b

static Integer add(Integer i1, Integer i2){return new Integer(i1.intValue() + i2.intValue());

}

public static void main(String[] args){(List<Integer>) -> int sum =

fun (List<Integer> l) -> int{(foldr(add,new Integer(0),l))};

}}

Center for Software Technology Sander Mak

Page 29: Pizza compiler

The Pizza Compiler > Features > First-class functions

Let’s make a Pizza foldr!

public class Foldr {static <D,E> D foldr((E,D) -> D f, D unit, List<E> l){switch(l){

case Nil: return unit;case Cons(E h, List<E> t): return f(h,foldr(f,unit,t));

}}

static Integer add(Integer i1, Integer i2){return new Integer(i1.intValue() + i2.intValue());

}

public static void main(String[] args){(List<Integer>) -> int sum =

fun (List<Integer> l) -> int{(foldr(add,new Integer(0),l))};

}} Center for Software Technology Sander Mak

Page 30: Pizza compiler

The Pizza Compiler > Features > First-class functions

Does this first-class function feature transform Pizza/Java intoHaskell?spacerWell... not really:

No currying

No referential transparency

No lazy evaluation

Center for Software Technology Sander Mak

Page 31: Pizza compiler

The Pizza Compiler > Features > First-class functions

Translation to Java

Translated Functions classcontains a method for everyfirst-class function body

FunctionsClosures extendspizza.support.Closure and hasan apply method for eachsignature

A class with First classfunctions results in 2translated classes

Most important task: ensurescoped variables are available

spacerspacerspacerThere are 3 cases to consider:

1 Local definition of a function

2 Passing of functions

3 Application of a function

Center for Software Technology Sander Mak

Page 32: Pizza compiler

The Pizza Compiler > Features > First-class functions

Translation to Java

Functions.pizza

1 class Functions {2 public Functions(int a){3 (int) -> int incr = fun (int i)->int {return i+a;};4 int result = this.f(incr);5 }6 public int f((int) -> int incr){7 return incr(2);8 }9 }

Center for Software Technology Sander Mak

Page 33: Pizza compiler

The Pizza Compiler > Features > First-class functions

Translation to Java

Functions.pizza

1 class Functions {2 public Functions(int a){3 (int) -> int incr = fun (int i)->int {return i+a;};4 int result = this.f(incr);5 }6 public int f((int) -> int incr){7 return incr(2);8 }9 }

Case 1: Local definition (line 3)

pizza.support.Closure incr = new FunctionsClosures(this,0, new Object[]{new Integer(a)});

Center for Software Technology Sander Mak

Page 34: Pizza compiler

The Pizza Compiler > Features > First-class functions

Translation to Java

Functions.pizza

1 class Functions {2 public Functions(int a){3 (int) -> int incr = fun (int i)->int {return i+a;};4 int result = this.f(incr);5 }6 public int f((int) -> int incr){7 return incr(2);8 }9 }

Case 1: Local definition (line 3)

int closureFunctions0(int i, Object[] freevars) {int a = ((Number)freevars[0]).intValue();return i+a;

}

Center for Software Technology Sander Mak

Page 35: Pizza compiler

The Pizza Compiler > Features > First-class functions

Translation to Java

Functions.pizza

1 class Functions {2 public Functions(int a){3 (int) -> int incr = fun (int i)->int {return i+a;};4 int result = this.f(incr);5 }6 public int f((int) -> int incr){7 return incr(2);8 }9 }

Case 2: Passing functions (line 4)

int result = this.f(incr);

Center for Software Technology Sander Mak

Page 36: Pizza compiler

The Pizza Compiler > Features > First-class functions

Translation to Java

Functions.pizza

1 class Functions {2 public Functions(int a){3 (int) -> int incr = fun (int i)->int {return i+a;};4 int result = this.f(incr);5 }6 public int f((int) -> int incr){7 return incr(2);8 }9 }

Case 3: Applying functions (line 7)

public int f(pizza.support.Closure incr) {return ((Number)incr.apply(new Integer(2))).intValue();

}

Center for Software Technology Sander Mak

Page 37: Pizza compiler

The Pizza Compiler > Rough edges

Language mismatches and other problems

There is a fixed maximum on the #arguments for first-classfunctions (definition of Closure)

Access modifiers are not fine-grained enough for accuratetranslation

Lots of typecasts are inserted to pass bytecode verification. Mostare not necessary because of Pizza’s typesystem

Generic array creation and generic instantiation are not possibledue to type erasure

’Strict superset’-claim is false: cannot use (new) keyword fun asidentifier

Center for Software Technology Sander Mak

Page 38: Pizza compiler

The Pizza Compiler > Related work

Other approaches

GJ: Generic Java spin-off from PizzaOnly the parameterized types from PizzaSome differences: only reference type parameters,compatibility with non-generic classes, generic arraycreationEnded up in Java 1.5!

Scala language by Martin OderskyDeparts from notion of translation into the JavalanguageResult: more freedom to implement new featuresRuns on JVM

C++ Templates superficially similar to parameterized typesMore like macros: each instantiation results in a newclassTemplates are not type-checked (only theirinstances)Center for Software Technology Sander Mak

Page 39: Pizza compiler

The Pizza Compiler > Conclusion > Opinion

Quality of error messages

A measure of compiler quality can be the quality of its errors:

Failure of bound-inference for generic types leads to good,understandable feedback

Omitting a case in a switch leads to a vague error message abouta missing return statement

There is an (undocumented) fixed maximum on the number ofarguments for a first class function. Violating this gives a long,vague error message

I would say that Pizza is usable, but not in production environments.

Center for Software Technology Sander Mak

Page 40: Pizza compiler

The Pizza Compiler > Conclusion > Opinion

Quality in general

Papers on Pizza are very well written

A very thorough description of the Pizza typesystem shows thatthe project is not mere hacking of cool features

Implementation is not robust, not unusable either

I would say that Pizza is a very good example of a research projectthat had significant impact (through Generic Java → Java 1.5)

Center for Software Technology Sander Mak

Page 41: Pizza compiler

The Pizza Compiler > Conclusion > The End

I hope this overview encourages you to find new ways to enhanceexisting ideas! spacerspacer

Odersky and Wadler:

’We are not short on innovations, but we need more ways to translateinnovation in to practice’

spacer

Try it yourself @ pizzacompiler.sourceforge.net

Center for Software Technology Sander Mak