310 likes | 420 Views
Explore the differences between flattening and direct semantics for inheritance in the context of Featherweight Jigsaw software. Learn about the intuitive flattening approach and the efficient, albeit complex, direct semantics implementation. Discover the pros and cons of each method and their implications on program compilation and dependencies.
E N D
Flattening versus Direct semantics for Featherweight JigsawGiovanni Lagorio, Marco Servetto and Elena Zucca
Plan of the talk • Flattening and Direct semantics • By example: Inheritance • Featherweight Jigsaw (FJig) • (nice and clean) surface syntax • Generalized inheritance • I’ll gloss over its (ugly but equivalent) core syntax • Conclusions
The intuition of Inheritance • Please think how you’d explain inheritance • to a muggle1(1 here, indicates someone who’s never heard of OO) • Odds are you’re thinking of flattening semantics
Flattening Semantics of inheritance class C1 { void m1() {…} } What does this mean? class C2 extends C1 { void m2() {…} } class C2 { void m1() {…} void m2() {…} } It’s as we wrote C2 like this
The general idea • Giving semantics of some feature by translating it away • Programs translated in (equivalent) programs that don’t use the feature class C2 { void m1() {…} void m2() {…} } • Extension flattened into the vanilla language Language • Let’s see pros and cons class C2 extends C1 { void m2() {…} } ExtendedLanguage
Pros: it’s intuitive class C1 { void m1() {…} } class C2 { void m1() {…} void m2() {…} } class C1 { void m1() {…} } class C2 extends C1 { void m2() {…} } class C3 extends C2 { void m3() {…} } class C3 { void m1() {…} void m2() {…} void m3() {…} }
Pros: good old method lookup class C1 { void m1() {…} } class C2 { void m1() {…} void m2() {…} } class C3 { void m1() {…} void m2() {…} void m3() {…} } • Lookup as easy as it can get • No changes in the definition (the program changes, not lookup) • For instance,lookup(C2, …)=(inheritance has been flattened out)
Cons: compiling dependencies class C1 { void m1() {…} } class C2 { void m1() {…} void m2() {…} } class C3 { void m1() {…} void m2() {…} void m3() {…} } class C1 { void m1() {…} } class C2 extends C1 { void m2() {…} } class C3 extends C2 { void m3() {…} } the binarydepends on
Flattening Semantics, recap • Intuitive:good tool for teaching/understanding • Been used to give the semantics of mixins, traits, … but • Poor implementation choice • Lot of compiling dependencies (unlike Java, like C++) • Code bloating
The implementation of Inheritance • Please think how you’d implement inheritance • Odds are you’re thinking of direct semantics
The idea • Classes can be defined in new ways • Since programs are not translated,good old method lookup can’t work anymore class C1 { void m1() {…} } Language lookup(C2, m1)=??? We can compute lookup(C, m)=… For instance, lookup(C1, m1)=… class C2 extends C1 { void m2() {…} } ExtendedLanguage
Extended method lookup class C1 { void m1() {…} } class C2 extends C1 { void m2() {…} } class C3 extends C2 { void m3() {…} } • Needs to know what extends means • For instance, lookup(C3, m2) starts from C3 • Don’t find m2, so proceeds with C2 • And finds m2
Flexible class definitions • class C ClassExpression • ClassExpression::=BasicClass | mergeClassExpression1, ClassExpression2 | rename N to N’ inClassExpression | hide N inClassExpression | …others…BasicClass ::= { …constr/fields/methods…}
Basic class declaration • One constructor; takes any kind of parameters • Internal representation hidden from clients • A series of members: fields and methods • Members can be: • abstract • virtual • frozen • local no definition “someone” is expected to provide it there is a definition,but it can be later replaced there is a definition, that current clients will see forever (even if replaced) there is a definition, that no other object will ever see
FJig • Nice per se • More than that, can encode: • standard inheritance • mixins • traits • … it’s a general framework for sw composition • Wouldn’t be wonderful to have • an intuitive (flattening) semantics and • a (rather complex but) efficient (direct) one? • Actually, no it wouldn’t • … unless they’re equivalent! And they are!
An example • I’m “cheating” using • an extra-sugared syntax • primitive types and void • class A {abstractvoid m();frozen intanswer() { return41; }local intloc() { return1; }} • class B {frozen int answer() { return42; }virtual void m() { loc(); } local intloc() { returnanswer(); } } • class C merge (rename answer towrongAnswerin A),B • …what’s new C().answer()?
Flattening steps (1 of 5) • class C merge (rename answer towrongAnswerin A), B • class C merge (rename answer towrongAnswerin{abstractvoid m();frozen intanswer() { return41; }local intloc() { return1; } } ), B
Flattening steps (2 of 5) • classC merge (rename answer towrongAnswerin{abstractvoid m();frozen intanswer() { return41; }local intloc() { return1; } } ), B • class C merge{abstractvoid m();frozen intwrongAnswer() { return41; }local intloc() { return1; }} , B
Flattening steps (3 of 5) • class C merge{abstractvoid m();frozen intwrongAnswer() { return41; }local intloc() { return1; } } , {frozen intanswer() { return42; }virtual void m() { loc(); }local intloc() { returnanswer(); } } • classC merge{abstractvoid m();frozen intwrongAnswer() { return41; }local intloc() { return1; }} , B
Flattening steps (4 of 5) • class C merge{abstractvoid m();frozen intwrongAnswer() { return41; }local intloc() { return1; } } , {frozen intanswer() { return42; }virtual void m() { loc’(); }local intloc’() { returnanswer(); } }
Flattening steps (5 of 5) • class C { // equivalent flattened definitionfrozen intwrongAnswer() { return41; }local intloc() { return1; }frozen intanswer() { return42; }virtual void m() { loc’(); }local intloc’() { returnanswer(); } } • now we can easily see what new C().answer() is • and, quite obviously , it’s 42
Direct Semantics class B {frozen intanswer() { return 42; }virtual void m() { loc(); } local intloc() { return answer(); } } class A {abstractvoid m();frozen intanswer() { return 41; }local intloc() { return1; }} class C merge (rename answer towrongAnswerin A),B • again, what does new C().answer() means? • what is lookup(C, answer)?
Direct Semantics class B {frozen intanswer() { return 42;}virtual void m() { loc(); } local intloc() { return answer(); } } class A {abstractvoid m();frozen intanswer() { return 41; }local intloc() { return1; }} class C merge (rename answer towrongAnswerin A), B lookup(C, answer) = lookup(merge …, answer) = = lookup((rename…), answer)and non-deterministically = lookup(B, answer)
Lookup on a rename expression lookup(renameFromNtoToNinClassExpression, N)= • failure if N=FromN • lookup(ClassExpression, FromN) if N=ToN • lookup(ClassExpression, N) otherwise In the example: lookup((rename answer towrongAnswerin A), answer) fails, so lookup(C, answer) = … 2 choices … = lookup(B, answer)
Lookup on a name lookup(B, answer) = lookup(ClassExpression, answer) where ClassExpressionis the class expression of B, which is a base (flatten) class. So, we can conclude. Believe it or not, I’ve just scratched the surface: direct semantics is no picnic
Conclusions and further work • Flatten semantics: easy, intuitive • but “performs” poorly • Direct semantics can be (rather) complex • but it’s a good choice for implementation • FJig: general language for software composition • encompasses inheritance, mixin, traits…. • Given both semantics and proved equivalent • Implemented interpreter as master-thesis:http://www.disi.unige.it/person/LagorioG/FJig/ • Exploring the equivalence for feature requiring static types (e.g. overloading and static binding) • Investigating smart implementation techniques