Pizza compiler

1,375 views

Published on

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.

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,375
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
33
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Pizza compiler

  1. 1. 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
  2. 2. The Pizza Compiler > Introduction Outline Introduction 1 Features 2 Rough edges 3 Related work 4 Conclusion 5 Center for Software Technology Sander Mak
  3. 3. 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? The ideas are still very interesting (they don’t age as fast as our 1 computers...)! Better insight into design of Java 1.5 2 See how FP and OO can integrate 3 Center for Software Technology Sander Mak
  4. 4. The Pizza Compiler > Introduction Characteristics Pizza language Language adds three novel features: Parametric Polymorphism (generics) 1 First class functions 2 Algebraic datatypes 3 Pizza is a strict superset of Java Language defined by translation into Java 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
  5. 5. The Pizza Compiler > Introduction Let’s move on to the features... Ask questions if code is unclear Center for Software Technology Sander Mak
  6. 6. The Pizza Compiler > Features > Parametric Polymorphism Introducing Parameterized Types Collections API (List, Hashtable, etc.) relies on generic programming Up to Java 1.4 simulated by implementing them with the top of the type-hierarchy: Object class Hashtable { .. public Object get(Object key) { .. } } This leads to runtime type errors: programmer is responsible for types 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
  7. 7. 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 for type 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
  8. 8. 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.array type Heterogeneous Creates specialised instances for each instantiation of type parameter Even at runtime specialisations can be generated, using reflection Center for Software Technology Sander Mak
  9. 9. 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
  10. 10. The Pizza Compiler > Features > Parametric Polymorphism Invariant/covariant subtyping Is List<String> a subtype of List<Object>? Center for Software Technology Sander Mak
  11. 11. 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; // aliasing ys.add(y); return xs.iterator().next(); } } Center for Software Technology Sander Mak
  12. 12. 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; // aliasing ys.add(y); return xs.iterator().next(); } } The aliasing of xs and ys will result in a compile time error, to guarantee soundness. Therefore, these types are not involved in a subtyping relation: parameterized types have invariant subtyping. Center for Software Technology Sander Mak
  13. 13. 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; // aliasing ys[0] = y; return xs[0]; } } Center for Software Technology Sander Mak
  14. 14. 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; // aliasing ys[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
  15. 15. 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 =0 sum (Cons e es) = e + sum es Elegant pattern matching is still not possible. Center for Software Technology Sander Mak
  16. 16. 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 =0 sum (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 Algebraic datatypes ease adding new functions over types using 2 matching, give that constructors are relatively fixed Can we combine this? Center for Software Technology Sander Mak
  17. 17. The Pizza Compiler > Features > Algebraic Datatypes Sum using ADT and pattern matching class List<A> { case Nil; case Cons(A head, List<A> tail); } Center for Software Technology Sander Mak
  18. 18. 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)); } Center for Software Technology Sander Mak
  19. 19. 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
  20. 20. 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 quot;mainquot; java.lang.StackOverflowError Center for Software Technology Sander Mak
  21. 21. 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 quot;mainquot; 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
  22. 22. The Pizza Compiler > Features > Algebraic Datatypes Pattern matching Wildcard pattern is allowed for unused variables Nested pattern matching is supported Overlapping patterns are detected, and not allowed Pizza compiler AST is an Algebraic datatype Enumeration 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
  23. 23. 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
  24. 24. 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
  25. 25. 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 ordering functionality... Center for Software Technology Sander Mak
  26. 26. 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 ordering functionality... What if... class TreeSet { public TreeSet((Object,Object) -> int compare) { .. order = compare(o1,o2); .. } .. } Center for Software Technology Sander Mak
  27. 27. 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 without parentheses. Comparator example is solvable using anonymous class instances. But how powerful/scalable is this approach? Center for Software Technology Sander Mak
  28. 28. 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 Center for Software Technology Sander Mak
  29. 29. 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 }
  30. 30. The Pizza Compiler > Features > First-class functions Does this first-class function feature transform Pizza/Java into Haskell? spacer Well... not really: No currying No referential transparency No lazy evaluation Center for Software Technology Sander Mak
  31. 31. The Pizza Compiler > Features > First-class functions Translation to Java A class with First class functions results in 2 translated classes Most important task: ensure scoped variables are available spacer spacer Translated Functions class spacer contains a method for every There are 3 cases to consider: first-class function body Local definition of a function FunctionsClosures extends 1 pizza.support.Closure and has Passing of functions 2 an apply method for each Application of a function 3 signature Center for Software Technology Sander Mak
  32. 32. The Pizza Compiler > Features > First-class functions Translation to Java Functions.pizza 1 class Functions { 2 public Functions(int a){ (int) -> int incr = fun (int i)->int {return i+a;}; 3 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
  33. 33. The Pizza Compiler > Features > First-class functions Translation to Java Functions.pizza 1 class Functions { 2 public Functions(int a){ (int) -> int incr = fun (int i)->int {return i+a;}; 3 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
  34. 34. The Pizza Compiler > Features > First-class functions Translation to Java Functions.pizza 1 class Functions { 2 public Functions(int a){ (int) -> int incr = fun (int i)->int {return i+a;}; 3 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
  35. 35. The Pizza Compiler > Features > First-class functions Translation to Java Functions.pizza 1 class Functions { 2 public Functions(int a){ (int) -> int incr = fun (int i)->int {return i+a;}; 3 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
  36. 36. The Pizza Compiler > Features > First-class functions Translation to Java Functions.pizza 1 class Functions { 2 public Functions(int a){ (int) -> int incr = fun (int i)->int {return i+a;}; 3 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
  37. 37. The Pizza Compiler > Rough edges Language mismatches and other problems There is a fixed maximum on the #arguments for first-class functions (definition of Closure) Access modifiers are not fine-grained enough for accurate translation Lots of typecasts are inserted to pass bytecode verification. Most are not necessary because of Pizza’s typesystem Generic array creation and generic instantiation are not possible due to type erasure ’Strict superset’-claim is false: cannot use (new) keyword fun as identifier Center for Software Technology Sander Mak
  38. 38. The Pizza Compiler > Related work Other approaches GJ: Generic Java spin-off from Pizza Only the parameterized types from Pizza Some differences: only reference type parameters, compatibility with non-generic classes, generic array creation Ended up in Java 1.5! Scala language by Martin Odersky Departs from notion of translation into the Java language Result: more freedom to implement new features Runs on JVM C++ Templates superficially similar to parameterized types More like macros: each instantiation results in a new class Templates are not type-checked (only their instances) Center for Software Technology Sander Mak
  39. 39. 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 about a missing return statement There is an (undocumented) fixed maximum on the number of arguments 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
  40. 40. The Pizza Compiler > Conclusion > Opinion Quality in general Papers on Pizza are very well written A very thorough description of the Pizza typesystem shows that the 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 project that had significant impact (through Generic Java → Java 1.5) Center for Software Technology Sander Mak
  41. 41. The Pizza Compiler > Conclusion > The End I hope this overview encourages you to find new ways to enhance existing ideas! spacer spacer Odersky and Wadler: ’We are not short on innovations, but we need more ways to translate innovation in to practice’ spacer Try it yourself @ pizzacompiler.sourceforge.net Center for Software Technology Sander Mak

×