pizza compiler
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
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
The Pizza Compiler > Introduction
Outline
1 Introduction
2 Features
3 Rough edges
4 Related work
5 Conclusion
Center for Software Technology Sander Mak
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
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
The Pizza Compiler > Introduction
Let’s move on to the features...
Ask questions if code is unclear
Center for Software Technology Sander Mak
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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