Keep a running total in the mixin, and use only the inherited public operations. Updating after a successful superclass operation leaves the total unchanged if an empty For this trait mixed directly into
pop fails its precondition.trait SumPlugin extends Stack {
private var total = 0
override def push(x: Int): Unit = {
super.push(x)
total += x
}
override def pop(): Int = {
val x = super.pop()
total -= x
x
}
def sum: Int = total
}Stack, its representation invariant is , where is the abstract stack sequence. A successful push adds exactly its argument and a successful pop removes exactly its returned value. ThusThe mixin reads no private Stack fields. Its operations add constant time complexity. As in the paper's Int interface, an unbounded mathematical total requires excluding integer overflow, or replacing the accumulator with BigInt if the interface is allowed to change. A later mixin can transform a pushed value at a different point in the Scala trait linearization; the effect is analyzed in the next part.The doubling mixin delegates with a transformed argument.For
trait DoublePlugin extends Stack {
override def push(x: Int): Unit = super.push(2 * x)
}A, the relevant Scala trait linearization is A, DoublePlugin, SumPlugin, Stack. A push first doubles the argument, then SumPlugin sees and sums that doubled value. The physical values are and the total is .For Equivalently,This is stackable trait modification: the order determines which arguments an outer mixin observes. In particular, the running-total specification from part (b) holds for
B, the Scala trait linearization is B, SumPlugin, DoublePlugin, Stack. SumPlugin sums the original argument, while its super.push doubles the stored value. The physical values are still , but the counter is . Hence the two lines printed are12
6A but does not survive the transforming inner mixin in B. With the given implementation, popping from B would subtract the doubled stored value, demonstrating why mixing individually sensible traits can break a combined representation invariant.Ignoring predefined ancestors as permitted, the Scala trait linearizations areA
super call in a trait continues to the next applicable implementation in the receiver's Scala trait linearization. It is not permanently bound to the parent named when that trait was declared.For The shared ancestor
d1.h, enter B2.h. Its super.g reaches B1.g, and that method's super.f reaches A.f. For d2.h, enter B1.h; its super.g reaches B2.g, whose super.f again reaches A.f. Therefore the two output lines areh in B2, g in B1, f in A
h in B1, g in B2, f in AA occurs once in each Scala trait linearization, after the two specialized traits. This accounts for the final f in A in both lines.In Scala trait linearization, the rightmost mixed-in trait takes priority. Merge inherited linearizations while retaining a shared ancestor only at its final position; For example,
super advances to the next implementation in the resulting chain. Ignoring predefined bases, the lookup orders areLife1, Exercise, Work, Life
Life2, Dance, Exercise, Program, Study, Work, Fun, Life
Life3, Program, Study, Work, Dance, Fun, Exercise, LifeDance alone has order Dance, Fun, Exercise, Life. In Life2, the additional Program branch already contains Fun before Life; keeping that shared ancestry in its later position moves Fun after Work, while Dance still takes priority. Reversing the mixins yields the third chain above.Each The
describe first delegates and then prints, so the lines appear in the reverse of the method lookup order. The exact output, including the two blank lines, isI live.
I work.
I exercise.
I live.
I have fun.
I work.
I study.
I program.
I exercise.
I dance.
I live.
I exercise.
I have fun.
I dance.
I work.
I study.
I program.super inside a trait therefore need not call that trait's isolated textual parent; the receiving object's full Scala trait linearization controls the next body.Write each Scala trait linearization from most specific to least specific. Merge the parents' Scala trait linearizations right-to-left, removing a duplicate from the earlier portion when it also appears in the later portion. This retains every base once while respecting its required position after derived traits.[ref]For example, combining the final
CityMan, City, Earth, Man, Mammal, Animal, Describe
BirdMan, City, Earth, Man, Mammal, Bird, Animal, Describe
BatMan, Man, Gotham, City, US, Earth, Bat, Mammal, Bird, Animal, DescribeCity with Gotham does not put City before its derived trait Gotham; duplicate removal places it after Gotham. Similarly Mammal and Bird remain after Bat, despite their occurrence through other parents.Each Each line has a final comma and space, because every printed string fragment includes them. The essential result is
describe calls super.describe before appending its own label. Thus execution enters implementations along the displayed Scala trait linearization, and appends labels in reverse order when calls return. The three printed lines areDescribe, Animal, Mammal, Man, Earth, City,
Describe, Animal, Bird, Mammal, Man, Earth, City,
Describe, Animal, Bird, Mammal, Bat, Earth, US, City, Gotham, Man, List each Scala trait linearization from the most specific software class toward the common base, omitting predefined types:For example,
Life1, Party, Study, Life
Life2, Sport, Exercise, Research, Party, Study, Life
Life3, Research, Study, Sport, Party, Exercise, LifeSport alone linearizes as Sport, Party, Exercise, Life, while Research alone gives Research, Party, Study, Life. In combining parents, take the right parent's linearization first, deleting from it entries also present in the remaining left-parent linearization, then append the latter. This places a common base after all of its derived traits.[ref]An unqualified The first three lines belong to
super.describe() continues after the current trait in the actual receiver's Scala trait linearization. Each override prints after its super call, so the output follows the displayed order backwards, starting at Life. The complete output is:I live.
More on study.
More on party.
I live.
More on study.
More on party.
More on research.
More on exercise.
More on sport.
I live.
More on exercise.
More on party.
More on sport.
More on study.
More on research.l1, the next six to l2, and the final six to l3.For the implementations above, the output isIn
0
1A, the Scala trait linearization sends insert('a',0) first to CapitalisePlugIn, then to CharTrackPlugIn, then to DocImpl. The counter trait receives 'A', so it does not increment the lowercase 'a' count. The stored document is A.In
B, the counter trait receives 'a', delegates through CapitalisePlugIn, and increments its original argument's counter on return. The stored document is again A, but the counter records one lowercase insertion:This demonstrates a composition that violates the count-of-current-document representation invariant for B: an earlier counter cannot assume a later transforming trait inserts its argument unchanged. Subsequent deletion also receives uppercase 'A' and would leave that stale lowercase count. A different valid design could inspect read(pos) after insertion to count the actual stored character; that design would produce 0,0. The stated 0,1 follows the explicit implementation in part (b), whose correctness assumption has now failed in the second composition.Read each Scala trait linearization from the most specific implementation toward the base. For a declaration with parents , concatenate their linearizations from right to left, deleting a duplicate from a left operand when it already occurs in the right operand. This can move a repeated ancestor later than a superficial “rightmost trait first” scan suggests. Omitting the common For example,
AnyRef, Any suffix, the answers areC, Y, H, N, L, I, E
B, Y, H, N, L, D, I, E
A, N, O, Y, S, H, T, L, D, I, ET contributes T,L,D,I,E, while O contributes O,Y,S,H,E. In A, the separately mentioned Y also appears in the later merge with O, and N's ancestors also occur in T; resolving these duplicates yields the final order displayed above. The inherited trait orders are the displayed lists after removing their leading class names. [ref]Each implementation first evaluates There is a trailing space before each newline. The third and fourth lines are identical: the declared type
super.meth and then appends its own name. Consequently the contributions appear in reverse order of the defining traits in the Scala trait linearization. The four printed lines areE I L N H Y
E I D L N H Y
E I D L T H S Y O N
E I D L T H S Y O N D of o4 permits the call but does not change the actual A object's dynamic dispatch or its super chain. Within N, for example, super.meth in an A follows the next implementation in A's full linearization rather than simply using L as its isolated textual parent.The output is
ABC, without a newline. The relevant Scala trait linearization is D,C,B,A. Calling d.m starts with C.m, whose super.m continues to B.m, then to A.m. The latter prints A; returning through the two method bodies then prints B followed by C. The braces keep each final print inside that object method.The output becomes
BCA, without a newline. Here each method body consists only of super.m; the semicolon ends that definition. The following print("B") or print("C") is a separate statement in the trait body, so it executes during construction.Creating
new D initializes the shared base and then the B and C mixins, printing BC. The subsequent d.m still forwards along C,B,A, but only A.m now prints anything, appending A. Thus it is the placement of the construction-time statements, not a changed Scala trait linearization, that changes the output. [ref]Ignoring the predefined universal bases, the Scala trait linearizations are
D1, B2, B1, A and D2, B1, B2, A. The rightmost mixed-in trait is searched first, while the shared base occurs only once.The output is:
h in B2, g in B1, f in A
h in B1, g in B2, f in AFor
D1, h is the B2 implementation. Its super.g continues after B2 to B1.g; that method's super.f continues after B1 to A.f. The second line follows the reversed mixin order. In particular, a trait's unqualified super is interpreted in the concrete receiver's linearization, not by assuming the trait always sees only its textual parent.[ref]Scala trait linearization merges parent linearizations from right to left, deleting an earlier occurrence when that same ancestor occurs in a later appended parent linearization. Each class precedes its ancestors, and an ancestor appears once.[ref]
Scala 2.13 classes and objects specification. Class Linearization: concatenation replaces identical elements of its left operand with their occurrences in the right operand; the same rule is used for the Scala 2 code here
Ignoring the predefined roots gives| Type | Linearization, most specific first |
|---|---|
K | K |
I | I, K |
J | J, K |
G | G, I, K |
F | F, J, I, K |
H | H, J, K |
D | D, F, J, G, I, K |
E | E, H, J, K |
A | A, D, F, J, G, I, K |
B | B, E, H, D, F, J, G, I, K |
C | C, E, H, J, K |
Each override appends its letter after
super.m returns. Reading the method chain in reverse from K therefore yields the three output lines:KIGJFDA
KIGJFDHEB
KJHECIn particular, mixing
H into C again does not create another H or move it before its subclass E. Also, J lies between F and G in D's chain; super follows the final linearization, not simply one statically named parent. Scala 2.11 classes and objects specification 2026-10-31
Defines Scala trait linearization, inherited member lookup, and evaluation of trait initialization statements.