270 likes | 440 Views
Jam. Designing a Java Extension with Mixins. http://www.disi.unige.it/person/LagorioG/jam/. Presented by: Yoav Gur. Definition. A mixin is a parametric heir class, that can be instantiated on different classes to create different heirs, that extends those classes
E N D
Jam Designing a Java Extension with Mixins http://www.disi.unige.it/person/LagorioG/jam/ Presented by: Yoav Gur
Definition A mixin is a parametric heir class, that can be instantiated on different classes to create different heirs, that extends those classes using the features defined in the mixin. Every mixin is applicable only on a predefined family of parent classes, which satisfy certain requirements defined by the mixin.
“Designing a Java Extension…” Must correspond to Java principles: • Object oriented. • Strongly typed. • Allows overriding, overloading, and hiding. • Dynamic binding of non-static methods. • Static binding of fields and static methods.
“…with Mixins” A new notation must be added: • A way to declare a mixin. • A way to state requirements from the parent class to be extended. • Presents two new preserved words: “mixin” and “inherited”.
Use the new preserved word “mixin” to declare a new mixin. Use the new preserved word “inherited” to state requirements from the parent class. A legal parent class must supply the stated fields, and the exact stated methods. No constructors. can you think on a different logical requirements in regards to methods? mixinUndo{ inheritedString getText(); inheritedvoid setText(String s); String lastText; void setText(String s){ lastText = getText(); super.setText(s); } void undo(){ setText(lastText); } } A simple example – mixin declaration
We will call “mixin instance” a class obtained by instantiating a mixin (not to be confused with an instance of a class). Constructors of the new mixin instance are to be defined in the curly brackets. class Textbox extends Component{ String text; … String getText() { … } void setText(String s) { … } } class TextboxWithUndo = Undo extends Textbox { } A simple example – Mixin instantiation
“A class obtained by instansiating a mixin M on a parent class P should have the same behavior as a usual heir of P whose body contains a copy of all components defined in M” Intuition: It is possible to get the same effect of mixin instantiation in pure Java by defining many “hand-made” subclasses, which extend different parent classes and have the exact same body. Semantics – “The copy principle”
A mixin is a type • So far, the presented extension allows avoiding code duplication by using mixins. • But that’s not all: a mixin can be used as a type, and a mixin instance is a subtype of both the mixin and the parent class on which it has been instantiated.
About mixin as a type • A mixin type M can be used in any place that a interface type can be used. • It is possible to access inherited fields and invoke inherited methods on expressions of mixin types. • A mixin can implement interfaces in exactly the same way a class does. • An heir class, H = M extends P, is a subtype of both M and P, and therefore is implicitly convertible to both. • As in interface types, a mixin type cannot be used for creating objects.
A semantic question rises: should we count the number of inc() invoked from all mixin instances of M, or keep a separate counter for each mixin instance? “The copy principle” dictates the latter. Therefore, static members are not a part of the mixin type. (i.e. H.inc() is legal, but M.inc() is not legal). mixin M { staticvoid inc() { counter++ ;} staticint counter = 0; } class H = M extends Object { } Static members
Declaring an inherited abstract method is a request for the parent class to declare, but not necessarily to implement, the method. The parent class can implement the method. It is also possible to redefine a method as abstract: mixin M { inheritedabstractvoid f(); } Abstract methods mixin M { inherited void f(); abstract void f(); }
Which f will be 7? The copy principle doesn’t help here Arbitrary solution that corresponds the most to “the spirit of Java”: The field which is selected is determined by the dynamic type of m. Still: Jam compiler will produce a warning to make the user aware that field accesses through a reference of the mixin type have nonobvious semantics. mixin M { int f; } class Once = M extends Base{} class Twice = M extends Once{} class Test { void test(){ Once o = new Twice(); foo(o); } void foo(M m) { m.f = 7; } } Ambiguity in field accesses in case of double mixin instantiation class Twice has two fields named f, one is hidden by the other.
Translation into Java – Jam’s implementation • inherited declarations in mixinM are translated to an interfaceParent$M. • A mixinM is translated to an interfaceM that extendsParent$M, with getters & setters for M’s fields. • Access to M’s fields is translated to a call to the appropriate getter/setter. • Corresponds to dynamic binding of M’s fields in case of double instantiation. • “class H = M extends P {}” is translated to “class H extends P implements M {…}” • M’s body is copied to H’s body, according to the copy principle. • Code duplication is only “delayed” until the translation to Java.
Implicit Constraints • All constraints we saw so far were positive: • What must exist (abstract is an exception) • There are also, implicit, negative specification • What must not be in the base class • Three implicit such constraints: • Unexpected overriding: overriding a method which was not declared as inherited • Illegal overriding: not a full signature match • Ambiguous overloading: (roughly) a method in the mixin overloads a method in the base, which may cause surprises to the user • Like positive constraints, depend only on mixin type (independent of implementation). • A modification of a method body does not affect instantiation correctness.
Unexpected Overriding • Mixin M, method mM, class C, method mC • The names of mM and mC are identical. • Signatures are identical • However, M does not declare mC as inherited. • Example: mixin Undo on a class with a void undo() • A Correct Java class, not what you expect. • JAM produces an error message at • instantiation time • Note: Unexpected overriding of fields and static methods is allowed!
Illegal Overriding • Mixin M, method mM, class C, method mC • The names of mM and mC are identical. • Signatures are almost identical (same argument types), but: • Different return type • Different classification (static/instance) • Different throws clause • JAM produces an error message at • instantiation time
class A {} class B extends A{} class C { void m(B b) {}void m(A a) {} } (new C()).m(new B()) class C { void m(Integer i) {}void m(String s) {} } (new C()).m(null) m(B b) will be chosen No Solution Overloading and Ambiguity(for now, forget about mixins) • Overloading: class C has several implementations of method m, all with different signature. • Ambiguity: more than one signature matches a call site. • Which version should the compiler use for • (new C()).m(…) • Java has built-in rules for ambiguity resolution. • There are cases when these rules fail.
Java Ambiguity Resolution • Tournament • <C,P1,…Pn> Characterizes a candidate • C: The declaring class • Pi: The ith parameter type • <C1,P11,…Pn1> vs. <C2,P12,,…Pn2> • Signs: • C1 ≥ C2 ≡ “C1 is equally or more specific then C2” • Pi1 ≥ Pi2 ≡ “P1 is equally or more specific then P2” • <C1,P11,P21,…Pn1> will win iff: • C1 ≥ C2AND P11 ≥ P12 AND … AND Pn1 ≥ Pn2 • If there is more then one champion: • No Solution
Ambiguous Overloading with Mixins – Motivation (1) • Mixin M, method mM, class C, method mC, mixin instance H • mMmay be an inherited method • An overloading relation between mM and mC • M’s author, do not know that it will be attached to C, and therefore is unaware of the overloading conflict. • Jam’s compiler can not know M’s dynamic type at compilation time. • Also unaware of overloading between mM and mC
Ambiguous Overloading with Mixins - Motivation (2) • Assume: • Overloading relation between mM and mC • Ambiguous call to mM() • either from inside M (this.mM()) • or from outside M (H.mM()) • Without the third constraint:
Ambiguous Overloading with Mixins • Solution - Third implicit constraint: • mC is not inherited • The names of mM and mC are identical • Signatures are interfering • Difference is only in parameter type of reference types • JAM produces an error message at • instantiation time • Assuming {boolean,int} are the only primitive types, avoids ambiguities entirely. • Otherwise, interfering should be redefined.
What’s the Type of this? mixin M mixin M {void g() {int i = A.f(this); }} class H = M extends Object{} However, by the copy principle (which is also the way JAM is implemented)… class H class H { void g() { int i = A.f(this); } } Problem: the type of “this” dictates ambiguity resolution rules Solution: “this” has type M, but it cannot be passed as an argument! It can be assigned to other variables, of explicit type M.
class P {} class H extends P { public H m() {return this;} } class P {} class H extends P { public P m() {return this;} } Limitations • No parametric notation for the parent and heir. • Some code can’t be generated by mixins: ≠ class P {} mixin M { public M m() {return this;} } class H = M extends P{} • Adding such a notation would prevent using mixin as a type.
Translation into Java – Jam’s implementation (reminder) • inherited declarations in mixinM are translated to an interfaceParent$M. • A mixinM is translated to an interfaceM that extendsParent$M, with getters & setters for M’s fields. • Access to M’s fields is translated to a call to the appropriate getter/setter. • Corresponds to dynamic binding of M’s fields in case of double instantiation. • “class H = M extends P {}” is translated to “class H extends P implements M {…}” • M’s body is copied to H’s body, according to the copy principle. • Code duplication is only “delayed” until the translation to Java.
Alternative translation into Java homogeneous implementation • A mixin M is translated the same way as in Jam’s implementation. • M’s methods which are not getters/setters are translated to static methods of a new class “BodyM”, with an additional parameter “M This”. • Those methods in H are implemented by calls to the corresponding methods in BodyM, with “this” as the additional parameter. • Some code duplication is avoided. • The downside: what about private/protected members in M? Getters & setters must become public for BodyM to be able to use them. Other methods must become public in BodyM for H to be able to use them.
Conclusion (1) • The two design principles of Jam: • The copy principle • A mixin name can be used as a type. • A mixin instance is a subtype of both the mixin and the parent class. • Static members are not a part of the mixin type. • In case of double mixin instantiation, field accesses through a reference of the mixin type use dynamic binding.
Conclusion (2) • Overloading resolution corresponds to Java’s “more specific” rule. • Instantiation is illegal in cases of unexpected overriding, illegal overriding, or ambiguous overloading. • Some legal instantiations may still generate an erroneous Java class. • use of “this” as argument inside a mixin is forbidden. • No parametric notation for the parent and the heir inside the mixin. • There is a tradeoff in the translation to java: code duplication vs. loosing encapsulation.