1 / 24

Transposing F to C ♯

Transposing F to C ♯. Andrew Kennedy & Don Syme Microsoft Research Cambridge, U.K. Transposing what?. As musical keys, F and C ♯ are far apart As programming languages, (System) F and (Generic) C ♯ are far apart But (message of talk):.

jenski
Download Presentation

Transposing F to C ♯

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Transposing F to C♯ Andrew Kennedy & Don SymeMicrosoft Research Cambridge, U.K.

  2. Transposing what? • As musical keys, F and C♯ are far apart • As programming languages, (System) F and (Generic) C♯ are far apart • But (message of talk): Polymorphism in Generic C♯is as expressive as polymorphism in System F

  3. Polymorphic Programming Languages Standard ML Eiffel O’Caml C++ Ada C♯ (coming soon…) Mercury Clu GJ

  4. Polymorphic features Impredicative polymorphism Parameterized classes Parameterized datatypes For-all types Polymorphic functions Higher-order polymorphism & kinds Polymorphic methods Type classes Bounds on type parameters Constraints on type parameters Variance

  5. System F and C♯ • System F (a.k.a. polymorphic lambda calculus) is very different from C♯

  6. System F into C♯ • Despite these differences, we can formalize a translation from System F into (Generic) C♯ that • is fully type-preserving (no loss of information) • is sound (preserves program behaviour) • (second message of talk) demonstrates that… polymorphic virtual methodsexpressfirst-class polymorphism

  7. ML-style polymorphism into C♯ • Define a datatype: datatype ’a Tree = Leaf of ’a | Node of ’a Tree*’a Tree • Write a polymorphic function:fun reflect (t : ’a Tree) =case t of Leaf a => Leaf a| Node(l,r) => Node(reflect r,reflect l)

  8. Compare: • Define parameterized classes: abstract class Tree<A> { ... }class Leaf<A> : Tree<A> { A value; ... }class Node<A> : Tree<A> { Tree<A> left; Tree<A> right; ... } • Add a method:abstract class Tree<A> { virtual Tree<A> reflect(); } class Leaf<A> : Tree<A> { ... override Tree<A> reflect() { return this; } } class Node<A> : Tree<A> { ... override Tree<A> reflect() { return new Node<A>(right.reflect(), left.reflect()); }}

  9. In general • ML-style datatypes can be translated into parameterized classes • Polymorphic functions can be translated into methods inside a parameterized class e.g.class Mapper<A,B> { List<B> Map(Function<A,B> f, List<A> xs) { ...} List<int> li = ...;List<string> ls = new Mapper<int,string>().Map(myFun, li);

  10. So: Core ML polymorphism can be encodedusing parameterized classes alone

  11. Polymorphic virtual methods • Define an interface or abstract class:interface Sorter { void Sort<T>(T[] a, IComparer<T> c); } • Implement the interface:class QuickSort : Sorter { ... }class MergeSort : Sorter { ... } • Use instances at many type instantiations:void TestSorter(Sorter s, int[] ia, string[] sa) { s.Sort<int>(ia, IntComparer); s.Sort<string>(sa, StringComparer);}TestSorter(new QuickSort(), ...);TestSorter(new MergeSort(), ...);

  12. Compare: • Define an SML signature:signature Sorter = sig val Sort : ‘a array * (‘a*’a->order) –> unit end • Define structures that match the signature:structure QuickSort :> Sorter = ... structure MergeSort :> Sorter = ... • Use structures at many type instantiations:functor TestSorter(S : Sorter) = struct fun test (ia, sa) = (S.Sort(ia, Int.compare); S.Sort(sa, String.compare) endstructure TestQuickSort = TestSorter(QuickSort); TestQuickSort.test(...);structure TestMergeSort = TestSorter(MergeSort); TestMergeSort.test(...);

  13. Or (Russo first-class modules): • Define an SML signature:signature Sorter = sig val Sort : ‘a array * (‘a*’a->order) –> unit end • Define structures that match the signature:structure QuickSort :> Sorter = ... structure MergeSort :> Sorter = ... • Use a function to test the structures:fun TestSorter (s, ia, sa) = let structure S as Sorter = s in (S.Sort(ia, Int.compare); S.Sort(sa, String.compare)) endTestSorter ([structure QuickSort as Sorter], ...);TestSorter ([structure MergeSort as Sorter], ...);

  14. Question: Can System F “first-class polymorphism” be encoded using polymorphic virtual methods ?

  15. To answer the question... • Take System F + recursion + call-by-value evaluation order • Formalize a type-preserving translation into Generic C# • Prove that it works

  16. In more detail • Source is System F + CBV recursion:(Types) A,B ::= X | A -> B | forall X.A(Terms) M,N ::= x | M N | rec y(x:A):B.M | M A | ΛX.V(Values) V ::= rec y(x:A):B.M | ΛX.V • Target is “C# minor” • a tiny, purely functional subset of Generic C# • very similar to Featherweight Java (Pierce, Igarashi, Wadler) • includes just enough for our translation plus a bit more (run-time types)

  17. Translation: functions • Represent function types using a parameterized class:class Arrow<X,Y> { public virtual Y app(X x); } • Function application is just invocation of app method • Represent function values by instances of “closure classes” implementing Arrow e.g. λx:X->Y.x y translates to new C<X,Y>(y) withclass C<X,Y> : Arrow<Arrow<X,Y>,Y> { X y; public override Y app(Arrow<X,Y> x) { return x.app(this.y); }} • Note: • fields store free variables of function • class is parameterized over free type variables of function

  18. Translation: recursion • Translate recursion into self-reference through this • For example, rec y(x:X):X. y(x) translates to new C<X>() withclass C<X> : Arrow<X,X> { public override X app(X x) { return this.app(x); }}

  19. Translation: polymorphism • We can’t use a single class definition for “forall” types • Instead use different class for each such typee.g. forall X. X->Y isclass ForAll_X_XtoY<Y> { public virtual Arrow<X,Y> tyapp<X>();} • Type application is just invocation of tyapp method • Represent polymorphic values by instances of “closure classes” implementing appropriate ForAll class • close over free variables and free type variables just as with functions

  20. Translation: polymorphism, cont. • Problem: translation of types doesn’t commute with substitution i.e.([A/X]B)* ≠ [A*/X]B* • Example: • forall X.X->Y translates to All_X_XtoY<Y> • Now substitute forall Z.Z for Y • We get forall X.(X->forall Z.Z) which translates to All_X_XtoAll_Z_Z • Solution (Läufer & Odersky): use a single class to represent a whole family of related polymorphic types

  21. Properties of translation • Fully type-preserving:A* = B* iff A=B • Translation preserves types of terms: If M:A translates to e then e:A* • Translation preserves convergence behaviour of closed terms (i.e. the translation is “adequate”): If M:A translates to e then M converges iff e converges

  22. Observations • Strictly speaking the translation is not compositional (global generation of names for forall types) • The translation is harnessing the power of polymorphic virtual methods • Generic C#, GJ, NextGen permit polymorphic methods to be virtual • Eiffel, C++ do not • Distinctiveness of polymorphic virtual methods shows up in (type-preserving) implementations • requires execution-time type application

  23. Future work • Proof of semantic correctness (adequacy) of translation • use method of logical relations • Harper, Morrisett, Minamide use similar technique to prove correctness of typed closure conversion • But: we have impredicative polymorphism and recursion; this makes things tricky • Compositional, partial type-preserving translations • Fω • Dynamic types • Power of nominal vs structural equivalence of types

  24. Questions?

More Related