0% found this document useful (0 votes)
42 views44 pages

Software Composition Paradigms: Sommersemester 2015

This document discusses software composition paradigms and introduces mixins and traits. It provides the following key points: - Mixins are uniform extensions of classes that provide reusable functionality without instantiation. They allow multiple inheritance. - Traits in Scala are similar to mixins but can have default implementations of methods. Traits allow stacking modifications onto classes through multiple inheritance. - The order of mixed in traits is significant as it determines method resolution through linearization. Linearization provides a consistent dispatch order to resolve conflicts from multiple inheritance.

Uploaded by

Anu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
42 views44 pages

Software Composition Paradigms: Sommersemester 2015

This document discusses software composition paradigms and introduces mixins and traits. It provides the following key points: - Mixins are uniform extensions of classes that provide reusable functionality without instantiation. They allow multiple inheritance. - Traits in Scala are similar to mixins but can have default implementations of methods. Traits allow stacking modifications onto classes through multiple inheritance. - The order of mixed in traits is significant as it determines method resolution through linearization. Linearization provides a consistent dispatch order to resolve conflicts from multiple inheritance.

Uploaded by

Anu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 44

Software Composition Paradigms

Sommersemester 2015
Radu Muschevici
Software Engineering Group, Department of Computer Science

2015-05-05

1 / 44

Beyond Inheritance:
Mixins & Traits

2 / 44

Mixins

3 / 44

Mixins
A mixin is a uniform extension of many different parent classes with
the same set of elds and methods.
[Ancona, Lagorio, and Zucca 2000]

Offers functionality to be inherited, but it cannot be instantiated


subclass without superclass or with many superclasses

Mixin can be reused with different superclasses.

A class may inherit some or all if its functionality from one or more
mixins via multiple inheritance.

Mixins require certain functionality and provide additional


functionality.

Flexible and lots of opportunities for reuse.


[Bracha and Cook 1990]
4 / 44

Traits Mixins in Scala


trait Singer {
def sing { println( "singing ..." ) }
}

Trait dening some


behaviour

class Insect
class Cicada extends Insect with Singer
class Bird extends Singer with Flyer
// given Flyer as another trait

Static trait
inheritance (builds
new class)

class Person {
def tell {
println("here's a little story ...")
Dynamic trait
}
inheritance
}
(anonymous class)
val singingPerson = new Person with Singer
singingPerson.sing
5 / 44

Mixins in Scala

Scala calls its mixins traits.

Scala traits are used to dene object types by specifying the


signature of the supported methods.

Scala traits can be partially implemented, i.e. it is possible to


dene default implementations for some methods (unlike Java
interfaces).

Scala traits may not have constructors.

Common uses: enriching thin interfaces and dening stackable


modications
[Odersky, Spoon, and Venners 2011]
6 / 44

Mixin with Requirements


abstract class WhoAmI {
//Abstract class defines the required methods
def whoami () : String
}
trait Singer extends WhoAmI {
//Trait uses required method
def sing { println("I am a singing " + whoami ) }
}
class Insect extends WhoAmI {
// Required method implementation
def whoami = "insect"
}
class Cicada extends Insect with Singer
class Person extends WhoAmI {
def whoami = "person"
def tell { println("here's a little story about a "
+ whoami ) }
}
7 / 44

Simple Example

trait Similarity {
def isSimilar(x: Any): Boolean
def isNotSimilar(x: Any): Boolean = !isSimilar(x)
}

isSimilar is abstract

isNotSimilar is concrete but written in terms of isSimilar

Classes that integrate this trait only have to provide a concrete


implementation for isSimilar. isNotSimilar gets inherited
directly from the trait.

8 / 44

Simple Example (cont.)


class Point(x: Int, y: Int) extends Similarity {
def isSimilar(obj: Any) =
obj.isInstanceOf[Point] &&
obj.asInstanceOf[Point].x == x
}
object TraitsTest extends Application {
val p1 = new Point(2, 3)
val p2 = new Point(2, 4)
val p3 = new Point(3, 3)
println(p1.isNotSimilar(p2)) // False
println(p1.isNotSimilar(p3)) // True
println(p1.isNotSimilar(2)) // True
}

9 / 44

The Ordered Trait


Thin vs. Rich Interfaces

Rich interface: many methods (easier in theory for client)

Thin interface: fewer methods easier for implementer

trait Ordered[A] {
def compare(that: A): Int
def < (that: A): Boolean = (this compare that) > 0
def > (that: A): Boolean = (this compare that) < 0
def <= (that: A): Boolean = (this compare that) <= 0
def >= (that: A): Boolean = (this compare that) >= 0
def compareTo(that: A): Int = compare(that)
}

An Ordered interface should be rich (for convenience), i.e. supply all


comparison operators: <, >, <=, >=.
10 / 44

The Ordered Trait (cont.)


An Ordered interface in Java would require that we implement all
methods:
class Rational implements Ordered {
boolean isLessThan(Rational that) {...}
boolean isGreaterThan(Rational that) {...}
boolean isLessOrEqualThan(Rational that) {...}
boolean isGreaterOrEqualThan(Rational that) {...}
}

In Scala, implement only one method, compare, and get a rich


interface:
class Rational (n : Int, d : Int) extends Ordered[Rational] {
def compare (that: Rational) =
(this.numer * that.denom) - (that.numer * this.denom)
}

11 / 44

Mixins as Stackable Modications

Use Scala traits to modify the methods of a class

Stack these modications onto each other

Consider the IntQueue class


abstract class IntQueue {
def get() : Int
def put(x : Int)
}

Now well build a concrete class atop of it

12 / 44

An Implementation

import scala.collection.mutable.ArrayBuffer
class BasicQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x : Int) { buf += x }
}

13 / 44

A Modication Trait

trait Doubling extends IntQueue {


abstract override def put (x : Int) {
super.put(2*x)
}
}
val queue = new BasicQueue with Doubling
queue.put(10)
queue.get() // Result: 20

Trait can only be mixed into IntQueues

super refers to the class that actually uses the trait

14 / 44

Two additional, stackable traits


trait Incrementing extends IntQueue {
abstract override def put (x : Int) { super.put(x+1) }
}
trait Filtering extends IntQueue {
abstract override def put (x : Int) {
if (x >= 0) super.put(x)
}
}
val queue = new BasicQueue with Doubling with Filtering with Incrementing
queue.put(-1)
queue.get // Result: 0
val queue = new BasicQueue with Doubling with Incrementing with Filtering
queue.put(-1)
queue.get // Result: Exception (queue is empty)

The mixin order is signicant!


Different mixin orders different behaviours.
15 / 44

Linearisation (Method Resolution Order, MRO)

Linearisation means that when a class is instantiated (new...),


a linear order of its superclasses, traits and itself is determined
(from most specic to least specic).

When several methods are applicable for a given call, the one
dened on the most specic class or trait, according to the
linearisation, is selected.

Linearisation resolves conicts in method dispatch due to


ambiguity.

Linearisation is needed to provide a consistent dispatch order of


a multiple inheritance hierarchy.

16 / 44

Desirable Properties of Linearisations

Acceptable: linearisation depends only on shape of inheritance


hierarchy

Observes local precedence order: linearisation of a class is


consistent with linearisation of superclasses. If A precedes B for
class C, then A will precede B for all subclasses of C.

Monotonicity: every property inherited by a class is dene in or


inherited by one of the direct super classes.

[Barrett et al. 1996]

17 / 44

Scalas Linearisation

1. Put the actual type of the instance as the rst element.

2. Starting with the rightmost parent type and working left, compute
the linearisation of each type, appending its linearisation to the
cumulative linearisation. (Ignore AnyRef and Any for now.)
3. Working from left to right, remove any type if it appears again to
the right of the current position.

4. Append AnyRef and Any.

18 / 44

Scala Linearisation
Scalas Linearisation: Example

classAnimal
class Animal
traitFurryextendsAnimal
trait Furry extends Animal
traitHasLegsextendsAnimal
trait HasLegs extends Animal
traitFourLeggedextendsHasLegs
trait FourLegged extends HasLegs
classCatextendsAnimalwithFurrywithFourLegged

class Cat extends Animal with Furry with FourLegged


Any
Inheritance
Linearisation

AnyRef

Animal

HasLegs

Furry

FourLegged

Cat
19 / 44

Scalas Linearisation: Example (cont.)


Apply rules 14 (slide 18):
1. Cat
2. Cat FourLegged HasLegs Animal Furry
Animal Animal
3. Cat FourLegged HasLegs Animal Furry
classAnimal
Animal
Animal
traitFurryextendsAnimal
traitHasLegsextendsAnimal
traitFourLeggedextendsHasLegs
4. Cat FourLegged HasLegs Furry Animal
classCatextendsAnimalwithFurrywithFourLegged
AnyRef Any

Scala Linearisation
Any
Inheritance
Linearisation

AnyRef

Animal

HasLegs

Furry

FourLegged

Cat

20 / 44

Scala Linearisation Exercise


abstract class IntQueue {...}
class BasicQueue extends IntQueue {...}
trait Doubling extends IntQueue {...}
trait Filtering extends IntQueue {...}
trait Incrementing extends IntQueue {...}
class MyQueue extends BasicQueue with Doubling with Filtering with Incrementing

Any

Inheritance
Linearisation

AnyRef

21 / 44

Mixins vs. Multiple Inheritance

Mixins offer late-binding of super calls:

With multiple inheritance the method called by super is


determined statically.

With mixins, the method is determined by linearisation,


possibly at runtime.

22 / 44

Mixins vs. Multiple Inheritance: Example


Multiple inheritance (not Scala):
class A {
read() : String {...}
write(s : String) {...}
}
class SyncReadWrite {
read() : String { ... super.read() ... }
write(s : String) { ... super.write(s) ... }
}
class SyncA extends A, SyncReadWrite {...}

Diamond problem: Which read and write methods will SyncA


inherit?

super calls refer to methods of the superclass of SyncReadWrite.


23 / 44

Mixins vs. Multiple Inheritance: Example (cont.)


Mixins:
class A {
read() : String {...}
write(s : String) {...}
}
trait SyncReadWrite {
read() : String { ... super.read() ... }
write(s : String) { ... super.write(s) ... }
}
class SyncA extends A with SyncReadWrite {...}

Linearisation: SyncA SyncReadWrite A

super calls refer to methods declared in A.

24 / 44

Problems with Mixins


Problem: little control over composition a linearisation rule selects
order of method calls.

A suitable total ordering on features must be found.

Glue code exploiting or adapting linear composition may be


dispersed throughout the class hierarchy.

Resulting class hierarchies are often fragile w.r.t. change


conceptually simple changes impact multiple parts of the
hierarchy.

25 / 44

Traits

26 / 44

Traits: Motivation

Inheritance: granularity too coarse difcult to decompose


application into an optimal class hierarchy, to maximises reuse.

Mixins pose numerous problems for reuse, e.g. little control over
composition.

27 / 44

Traits
A trait is a set of methods divorced from any class hierarchy.
[Ducasse et al. 2006]

Traits are purely units of reuse (only methods). Classes are


generators of instances.

Traits are simple software components that provide and require


methods.

Traits specify no state, so the only conict when combining traits is


a method conict. Conict resolution mechanisms are provided.

28 / 44

Traits
A trait is a set of methods divorced from any class hierarchy.
[Ducasse et al. 2006]

Traits are purely units of reuse (only methods). Classes are


generators of instances.

Traits are simple software components that provide and require


methods.

Traits specify no state, so the only conict when combining traits is


a method conict. Conict resolution mechanisms are provided.

Newer versions do specify state!

29 / 44

A Trait O. Nierstrasz, N. Scharli,


R. Wuyts, A.P. Black
Ducasse,

ovided
ethods
Provided
methods

Fig. 10.

TReadStream
on:
collection
atEnd
collection:
atStart
position
setToEnd
position:
setToStart
nextPosition
maxPosition
minPosition
next

Required
methods
Required
methods

[Ducasse
et methods
al. 2006]
The trait TReadStream with provided and
required

Stphane Ducasse, Oscar Nierstrasz, Nathanael Schrli, Roel Wuyts, Andrew P. Black: Tra
mechanism for fine-grained reuse. ACM Trans. Program. Lang. Syst. 28(2): 331-388 (200

81

30 / 44

Trait Composition

Classes are composed from traits: Incremental extensions to the


single superclass are specied using one or more traits.

Traits can be composed from other traits.

Traits can be composed in arbitrary order.

Principle: Composite retains complete control over composition.

Glue methods connect traits together, adapting provided trait


methods, and resolving method conicts.

Adaptation required only when conicts are present or changes


are required.

31 / 44

Trait Operations

Composite traits are constructed using trait sum, overriding,


exclusion and aliasing.

Trait sum takes the union of the non-conicting methods. Identical


names that map to different method bodies are marked as a
conict.

Overriding resolves conicts by providing methods in the


composite that replace trait methods with the same name.

Exclusion avoid conicts by excluding methods from composition.

Aliasing renames methods, thus avoiding method name clashes.

32 / 44

Composition with Traits

Composition with Traits


Traits: A Mechanism for Fine-grained Reuse

Class
Class

Trait
Trait

Fig. 11.

21

ReadStream
initialize
collection
collection:
position
position:
TReadStream
on:
collection
atEnd
collection:
atStart
position
setToEnd
position:
setToStart
nextPosition
maxPosition
minPosition
next

fulfilled by

The class ReadStream composed from the trait TReadStream


84

Saturday 29 September 12

Running Example. Suppose that we want to implement a library that provides streams
which may be readable, writeable, both readable and writeable, or synchronized. For clarity, trait names start with the letter T, and class names do not. We italicize required meth-33 / 44

84

Trait Composition Rule

Methods dened in a class override methods provided by a trait.


Methods in class implement gluing to resolve conicts.

Flattening property. A (non-overridden) method in a trait has the


same semantics as if it were implemented directly in the class.

Composition order is irrelevant. Disambiguation is explicit.

34 / 44

Flattening

In any class dened using traits, the traits can be inlined to obtain an
equivalent class denition that does not use traits.
Traits can be compiled away.

Methods dened in the class take precedence over methods


provided by a trait.

Trait methods take precedence over superclass methods.

The keyword super has no special semantics for traits.

35 / 44

Flattening

Flattening: Example

aturday 29 September 12

Traits: A Mechanism for Fine-grained Reuse

fulfilled by

23

fulfilled by

TReadStream
atEnd
collection
position

TWriteStream
nextPut:
atEnd
on:
collection
position

TPositionableStream
atEnd
collection
atStart
collection:
setToEnd
position
setToStart
position:
nextPosition
maxPosition
minPosition

TPositionableStream
atEnd
collection
atStart
collection:
setToEnd
position
setToStart
position:
nextPosition
maxPosition
minPosition

next
on:

flattened to
TReadStream
on:
collection
atEnd
collection:
atStart
position
setToEnd
position:
setToStart
nextPosition
maxPosition
minPosition
next

TWriteStream
on:
collection
atEnd
collection:
atStart
position
setToEnd
position:
setToStart
nextPosition
maxPosition
minPosition
nextPut:

86

Fig. 13. TReadStream and TWriteStream as composite traits

36 / 44

Conict Resolution

Conicts
A conict arises when composing two traits that provide identically
named methods with different bodies.

Method conicts are resolved explicitly by

Dening an overriding method in the composite class/trait,

Exclusion in the composition clause,

Aliasing, i.e. making a trait method available under another name.

37 / 44

Traits: A Mechanism for Fine-grained Reuse


Conict Resolution: Example

fulfilled by

next

25

fulfilled by
ReadWriteStream
collection:
collection
position
position:

TSyncReadStream
readNext
acquireLock
releaseLock

TSynchronize
acquireLock
semaphore
releaseLock
semaphore:
initialize
TReadStream
on:
collection
atEnd
collection:
atStart
position
setToEnd
position:
setToStart
nextPosition
maxPosition
minPosition
next
readNext

next
on:

conflict

TReadStream
collection
position

TPositionableStream
atEnd
collection
atStart
collection:
setToEnd
position
setToStart
position:
nextPosition
maxPosition
minPosition
TWriteStream
collection
nextPut:
position
on:
TPositionableStream
atEnd
collection
atStart
collection:
setToEnd
position
setToStart
position:
nextPosition
maxPosition
minPosition

87

38 / 44

Exclusion Removes Conicts


Exclude methods
that would otherwise
be provided by a trait
in order to avoid a
conict.

Circle
initialize
=
hash
rgb
rgb:
center
center:
radius
radius:
drawOn:

TColor
red
green
~=
=
hash

rgb
rgb:

TColor

TCircle
=
hash
...
bounds
area

center
center:
radius
radius:

red
green
~=
=
hash

rgb
rgb:

TCircle
TDrawing
draw
bounds
refresh
drawOn:
refreshOn:

=
hash
...
bounds
area

center
center:
radius
radius:

TDrawing
draw
bounds
refresh
drawOn:
refreshOn:

39 / 44

Traits vs. Mixins


Mixins
1. A mixin is a unit of reuse, but (often) also denes a type
2. Composition by inheritance
3. Must be applied incrementally
4. Order of composition given by linearisation algorithm
5. Inheritance has two roles: code reuse and building conceptual
hierarchies.

Traits
1. Purely units of reuse
2. Composition by sum, overriding, aliasing and exclusion: More
ne-grained control
3. Several traits can be applied to a class in a single operation.
4. Composition is unordered & requires explicit resolution of conicts.
5. Inheritance is separate from trait composition and only reects
conceptual hierarchies.

40 / 44

Stateful Traits
(next lesson)

41 / 44

This Weeks Reading Assignment

Bracha, G., and Cook, W. Mixin-based inheritance. In ACM


Conference on Object-Oriented Programming Systems,
Languages, and Applications (New York, NY, USA, 1990),
OOPSLA/ECOOP 90, ACM Press, pp. 303311.

Download link:
http://dl.acm.org/citation.cfm?id=97982

Freely accessible from within the TUD campus network

42 / 44

References I
Ancona, Davide, Giovanni Lagorio, and Elena Zucca (2000). Jam A
Smooth Extension of Java with Mixins. In: European Conference
on Object-Oriented Programming. Vol. 1850. LNCS. Springer,
pp. 154178.
Barrett, Kim, Bob Cassels, Paul Haahr, David A. Moon, Keith Playford,
and P. Tucker Withington (1996). A Monotonic Superclass
Linearization for Dylan. In: ACM Conference on Object-Oriented
Programming Systems, Languages, and Applications. OOPSLA 96.
ACM Press, pp. 6982.
Bracha, Gilad and William Cook (1990). Mixin-based inheritance. In:
ACM Conference on Object-Oriented Programming Systems,
Languages, and Applications. OOPSLA/ECOOP 90. ACM Press,
pp. 303311.

43 / 44

References II
Ducasse, Stphane, Oscar Nierstrasz, Nathanael Schrli, Roel Wuyts,
and Andrew P. Black (2006). Traits: A Mechanism for Fine-grained
Reuse. In: ACM Transactions on Programming Languages and
Systems 28.2, pp. 331388.
Odersky, Martin, Lex Spoon, and Bill Venners (2011). Programming in
Scala: A Comprehensive Step-by-Step Guide. 2nd. USA: Artima
Incorporation.

44 / 44

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy