defense
Document Sample


Interface-Oriented Programming
and Representation Inference
L. Robert Varney
June 1, 2005
Agenda
Introduction
Motivating Problems
Solution
Implementation
Experiments
Related Work
Conclusion
Introduction
The Concept
B C
new
A B new new
extends
A B
A
OO software decomposition and recomposition
mechanisms are plagued by four problems that impair
design quality, reusability, safety, and evolvability
Implementation Bias (Concrete Dependencies)
Feature Combinatorics (Inability to Factor Designs)
Representation Assembly (Unsafe Assembly of Factored
Design Elements)
Representation Selection (Performance/Reusability
Tradeoff in Choosing an Implementation of an Interface)
The Concept
B C
new
A B new new
extends
A B
A
The first three problems can be solved by pervasive use
of interfaces together with automatic assembly guided
by locally specified, interface-oriented constraints.
The last problem is harder, but we can avoid embedding
the tradeoff in the code, and separately employ an
automatic mechanism for good, context-dependent
representation choices.
Contributions
IOP as a programming paradigm that
solves the four important problems
PJ, an example of an IOP language
Representation Inference, a novel
mechanism for software assembly
A pervasive, language-integrated
factory mechanism based on
representation instantiation graphs, a
means for context-dependent evolution
of representation choices
Since Last Time We Met …
Name change and other revisions to the
IOP language
Was “ARC”, now “PJ”
Formalized the safety objectives the IOP
system should guarantee
Finished a prototype implementation of
the compiler, representation inference
engine, and run-time system that
together support the development of
representative examples.
Motivating
Problems
Motivating Problems
Implementation Bias
Feature Combinatorics
Representation Assembly
Representation Selection
Problem 1: Implementation Bias
Dependence on concrete
implementations where abstractions
should suffice
Caused in OOP by use of class names at
instantiation dependencies (new object
creation, implementation inheritance)
Forms of Implementation Bias
class C extends ClassName { … }
X obj = new ClassName();
Both are commitments to concrete
implementations at locations of
instantiation dependency
Why Do We Want to Avoid This?
Client CCName CCImpl CCImpl+
Lacking an explicit interface, we may assume
that the client depends on some uknown subset
of the concrete class implementation’s closure
This is unstable and out of the client’s control …
A bad situation – some middle ground should be sought
and made explicit
Problem 2: Feature Combinatorics
Difficulty of factoring the design of a
library of components
Goals:
No code replication
Span feature space
Low coupling between components
Separate client-relevant from implementation
details
Example: A Feature Space for Sets and Bags
Implementation Framework:
Particular set of private abstract methods to be
implemented according to some core data structure
Choice of core data structure:
Trees, lists, hashtables, etc.
Null Values Allowed?
Yes, No
Element Equality?
By value, by identity
Element Storage?:
Story copy, store reference
Element Uniqueness?
Unique, non-unique
Single Inheritance Implementation
Choice of
Choice of framework
core data
structure
Other
feature
choices
Not all combinations are legal,
and ordering matters …
Multiple Inheritance or Mixin-Class
Implementation
No more code replication …
But someone needs to compose the bottom row correctly …
And ordering still matters …
Problem 3: Representation Assembly
Difficulty of assembling partial
implementations into correct, robust, complete
representations
Goals:
No knowledge of encapsulated information
Get exactly what you want, and it stays that way
Special cases of this problem:
Multiple Inheritance Conflicts
Fragile Base Class Problem
Intrusiveness of Aspect-Weaving, Fragile Pointcuts
The Fragile Base Class Problem
class Stack {
void push(int x)
{ … }
void pushAll(int[] x) {
… push(e); …
}
…
}
class CountedStack
extends Stack {
int count;
void push(int x) {
super.push(x);
count++;
}
…
}
The Fragile Base Class Problem
class Stack { class Stack {
void push(int x) void push(int x)
{ … } { … }
void pushAll(int[] x) { void pushAll(int[] x) {
… push(e); … …
} }
… …
} }
class CountedStack
extends Stack {
int count;
void push(int x) {
super.push(x);
count++;
}
…
}
The Fragile Base Class Problem
class Stack {
void push(int x)
{ … }
void pushAll(int[] x) {
…
}
…
}
class CountedStack class CountedStack
extends Stack { extends Stack {
int count; int count;
void push(int x) { void push(int x) {
super.push(x); super.push(x);
count++; count++;
} }
… void pushAll(int[] x) {
} super.pushAll(x);
count += x.length;
}
…
}
The Fragile Base Class Problem
class Stack { class Stack {
void push(int x) void push(int x)
{ … } { … }
void pushAll(int[] x) { void pushAll(int[] x) {
… push(e); … …
} }
… …
} }
class CountedStack
extends Stack {
int count;
void push(int x) {
super.push(x);
count++;
}
void pushAll(int[] x) {
super.pushAll(x);
count += x.length;
}
…
}
Problem 4: Representation Selection
Selecting the best implementation of an
interface
Goals:
Maximal reusability for the component using the
interface
Good performance in all execution contexts
But:
Interface abstraction breaks link between context and
implementation
No Free Lunch – impossible to pick one
implementation that outperforms all others in all
contexts.
Example:
A Map-based
Application
Information only
available here … Example:
A Map-based
What kind
of Map? Application
What kind
of Array?
What kind May determine
of ArrayPolicy? the best answers
here …
Solution
Solution Elements
Interfaces Everywhere
Particles: Unbiased Partial Classes
Representation Inference
Method and Type Constraints
Pervasive Factory Mechanism
Representation Instantiation Graph (RIG)
Solution Elements:
Interfaces and Particles
Interfaces
interface B1 { interface B2 {
B1(int b); // constructor B2(double x); // constructor
void b1(); // method void b2(); // method
} }
interface I extends B1, B2 {
I(); // constructor
void i(); // method
}
B1 b1 = new B1(0); // instantiation
B2 b2 = new B2(9.9); // instantiation
I i = new I(); // instantiation
Particles: Unbiased Partial Classes
particle P represents I assumes B1, B2 {
int v; // instance variable
I() assumes B1(0), B2(9.9) {
… // constructor impl.
}
void i() {
… b1(); … b2(); … // method impl.
}
void b1() {
… super.b1(); … // refined method impl.
}
}
Interface-Oriented Relationships
T
Interface Lattice
extends
I x = new I();
I1 Im B1 Bn
Client …. ….
a() b()
represents assumes
particle P
void a(){ .. b(); .. }
Example: A Stack
interface Collection<T> {
…
boolean empty();
…
}
interface Stack<T> interface List<T>
extends Collection<T> { extends Collection<T> {
Stack<T>(); List<T>();
void push(T x); void addRear(T x);
T pop(); T removeRear();
T top(); T rear();
void pushAll(Collection<T> c); …
} }
Multiple Stack Particles
particle PSi represents Stack<T> assumes Stack<T>, List<T> {
Stack<T> assumes Stack<T>(), List<T>() { }
void push(T x) { addRear(x); }
Collection<T>
T pop() { return removeRear(); }
T top() { return rear(); }
S
void pushAll(Collection<T> c) uses push(T);
}
Stack<T> List<T>
particle PSall represents Stack<T> assumes Stack<T> {
void pushAll(Collection<T> c) {
for (T x in c.elements()) A
push(x);
}
}
Another Stack Particle
particle PSiArr
represents Stack<T> assumes Stack<T>, Array<T> {
Stack<T>() assumes Stack<T>(), Array<T>() { }
void push(T x) { add(x); }
T pop() { return remove(size()-1); }
T top() { return get(size()-1); }
void pushAll(Collection<T> c) uses push(T);
}
Solution Element:
Representation Inference
Representation Inference
Assembles particles into complete representations of
interfaces
Generates multiple alternative representations for each
interface instantiation site in a program
Resolves inheritance dependencies
A candidate representation for some interface is:
A sequence of particles
… with an associated constructor sequence
… and inter-particle method and constructor bindings
A candidate representation is a representation if it:
Completes the interface
Completes all of the particles in the sequence
Method bindings and type attributes satisfy all particle
constraints
Dependencies of the Stack Example
extends
Collection<T> ? represents
.
. represents,
assumes
.
assumes
PS
Stack<T> PSi List<T> ? Array<T> ?
. .
PSall
. .
PSiArr . .
.
.
.
Looking at PSi another way …
Collection<T>()
Collection<T> Iter<T> elements()
boolean contains(T) Interface-oriented
boolean empty() inheritance provides
int size()
implicit virtual
base types.
extends extends extends
Multiple base
abstractions are
Stack<T> Stack<T> List<T> merged before
Stack<T>() pushAll(Collection<T>) List<T>() representations
push(T) addRear(T)
T pop() T removeRear()
are inferred,
T top() T rear() avoiding
inheritance
represents assumes
anomalies but
assumes
allowing multiple
inheritance of
PSi
state.
PSall …
Collection<T>()
Collection<T> Iter<T> elements()
boolean contains(T)
boolean empty()
int size()
extends extends extends
Stack<T> Stack<T>
pushAll(Collection<T>) Stack<T>()
push(T)
T pop()
T top()
represents assumes
PSall
Unifying: PSi + PSall + rep(List<T>())
Collection<T>()
Iter<T> elements()
rep(List<T>())?
Collection<T>
boolean contains(T)
boolean empty()
int size()
extends extends
Only consistent
representations
Stack<T> List<T> are combined
Stack<T>()
push(T) List<T>() (automatically),
addRear(T)
T pop()
T removeRear()
avoiding the
T top()
T rear() fragile base class
pushAll(Collection<T>)
problem, but
represents
without violating
assumes
encapsulation.
PSi
PSall
Inferred Stack Representations
List-based family of representations:
rep(Stack<T>()) PSi + PSall + rep(List<T>())
Array-based family of representations:
rep(Stack<T>()) PSiArr + PSall + rep(Array<T>())
Solution Elements:
Methods and Type Constraints
Encapsulated Method Constraints
Constraints encapsulated in particles, not in interfaces
Used by representation inference to determine inclusion of a particle
(or not) and what method bindings are legal
Method overriding constraints:
initial methods – may always be replaced
essential methods – a group of methods in one particle that may not be
individually replaced unless the replacing method refines it using
super.meth()
final methods – may not be replaced
Method implementations are essential by default
Method use constraints:
void foo() uses bar();
void super.foo() !uses bar();
“Use” is based on transitive closure of method bindings across a
representation (not control flow)
Type Attribute Constraints
Positional type parameters, named role types, and named
properties
Upper bound constraints may be expressed in both
interfaces and particles
The constraints expressed in interfaces and particles for
the same type attribute may be different
Interfaces may refine the attribute constraints of their
ancestors or introduce new attributes
Particles may only refine the attribute constraints of the
interfaces they represent and assume, they may not
introduce new attributes
A representation is required to have a solution to these
constraints
Example: Properties for Set/Bag Behaviors
interface Collection {
…
property equality = {value, identity};
property nullsAllowed = {yes, no}
property store = {copy, reference};
property membership = {unique, nonunique};
}
interface Set extends Collection {
…
property Collection::membership = {unique};
}
Example: Properties for Set/Bag Behaviors
particle PBagAddNA
represents Collection
assumes Collection, Collection as Insert
…
property Collection::membership = {nonunique};
property Collection::nullsAllowed = {yes};
void add(T x) {
insert(x);
}
}
particle PSetAdd
represents Collection
assumes Collection, Collection as Insert
…
property Collection::membership = {unique};
property Collection::nullsAllowed = {no};
void add(T x) {
if (x != null && !contains(x)) {
insert(x);
}
}
}
Solution Elements:
Pervasive Factory Mechanism
A Pervasive, Language-Integrated
Factory Mechanism
Implements instantiation of interface X:
obj = new X();
using a RepNode and an Instantiator:
// Get client particle’s RepNode
rn = getRepNode();
// Get instantiator for this instantiation site
instantiator = rn.getInstantiator(seg,siteK);
// Construct the object
obj = instantiator.construct_X();
Implementation
PJ System Architecture
PJ Compiler Front-End
Representation Inference Engine
Representation Inference:
A Naïve Algorithm
inferRepsNaive( C ) {
RSet = empty;
for (PPerm in Permutations(AllParticles)) {
for (CtorPerm in CtorPermutations(PPerm)) {
for (MethodBindings in Bindings(PPerm,CtorPerm)) {
R = makeRep(PPerm,CtorPerm,MethodBindings);
if (representsType(C) && complete(R) && satisfied(R)) {
RSet = RSet + R;
}
}
}
}
return RSet;
}
Representation Inference:
Revisions to the Naïve Algorithm
Iteratively construct a particle sequence,
constructors, and method bindings
Work bottom up, inferring descendants before
ancestors, selecting undefined methods for a
minimal interface at each step
Check constraints at each iteration, use
backtracking search
Also use constraints to direct the search, e.g.:
Attempt to split downcalls that violate constraints
Representation Instantiation Graph
I0 I1 I2 I3
Defaults
I0=RX I2=RA I2=RB
Back edge to a I3=RM
Default I0,2
Context
Dependent
RepNodes
I0=RY I0=RY
Context-Dependent, Evolvable
Representation Selection via RIGs
RepNode:
A data structure governing the choice of representation for some instantiation site (or
sites) in a program. RepNodes are grouped into “trees” with occasional back-edges to the
root of other RepNode trees.
A Default RepNode:
A root of a RepNode “tree”
RIG (Representation Instantiation Graph):
A forest of RepNode “trees”
Context dependence:
The path to a particular RepNode provides a form of context. The same lexical instantiation
site may correspond to multiple RepNodes corresponding to different dynamic instantiation
contexts.
The RepNode “trees” in a RIG are bounded in depth but can be arbitrarily deep (user
control)
Evolvability:
RIGs can be manipulated without changing source code
Still, manipulated RIGs always chooses representations safely
RepNodes RepNode
__ci__Foo__0 Refers to instantiator
for constructor __ci__Foo
Each RepNode is
responsible for 0 1 2 Segments
managing an
0 1 2 0 1 0 1 Instantiation sites
instantiator for some
representation, and
knows about the
obj = new X();
instantiation sites in
that representation
For each instantiation RepNode
site, it may refer to a
child repNode or to a __ci__X__0
default repNode in
the program’s 0 1
genotype 0 1 2 3 0 1 2
RepNodes and Instantiators
MetaInstantiator
__ci__Foo__0
0 1 2
0 1 2 0 1 0 1 Instantiator Rep__Foo__K
MetaInstantiator
__ci__X__0
0 1 Rep__X__J
Instantiator
0 1 2 3 0 1 2
Object Instantiation Protocol
:Client :Client :Rep :Site :Meta
:Instantiator
Particle RepNode Genotype RepNode Instantiator
new X
getInstantiator(seg,k)
New RepNode?
new
getInstantiator(ciName) Meta
Inst.
Protocol
instantiator
siteRepNode
siteRepNode
getInstantiator()
instantiator
instantiator
construct_X()
object
Instantiator Meta-Instantiation Protocol
:Meta :Context :Instantiator :Instantiator
Instantiator Database Options Binding
getInstantiator(ciName)
lookupOptions(ciName)
instantiatorOptions
chooseInstantiatorBinding()
instantiatorBinding
getInstantiator()
Load Rep Class
Create Instantiator
instantiator
Experiments
Experiment 1:
A Stack
Visualizing Representation Inference
Start with all methods of interface undefined …
Defined Methods
Collection(0)::empty()
Stack(0)::push(int);
Undefined Methods Stack(0)::pushAll(int[])
Stack(0)::pop()
Stack(0)::top()
Result of merging PXList …
Stack(0)::top() = seg(PXList,0)
Stack(0)::pop() = seg(PXList,0) Stack(0)::push(int)
Stack(0):push() = seg(PXList,0)
= PXList
Collection::empty()
Stack::pushAll(int[]) Chosen undefined method
List(0/1)::addTail() and chosen particle that defines it
List(0/1)::removeTail()
List(0/1)::getTail()
Inference results
For Stack
Inference results
For Stack
Experiment 2:
Another Stack (FBC version)
The Fragile Base Class Problem, Again
class Stack { class Stack {
void push(int x) void push(int x)
{ … } { … }
void pushAll(int[] x) { void pushAll(int[] x) {
… push(e); … …
} }
… …
} }
class CountedStack class CountedStack
extends Stack { extends Stack {
int count; int count;
void push(int x) { void push(int x) {
super.push(x); super.push(x);
count++; count++;
} }
… void pushAll(int[] x) {
} super.pushAll(x);
count += x.length;
}
…
}
Robust Stack Particles
particle PX particle PY
represents Stack represents Stack
assumes Stack, … { assumes Stack {
void push(int x) { … } void pushAll(int[] x) {
int pop() { … } … push(e); …
int top() { … } }
… …
} }
particle PZ
represents Stack {
void push(int x) { … }
void pushAll(int[] x) { … }
int pop() { … }
int top() { … }
}
Robust Counted Stack Particles
particle CS2 particle CS1
represents Stack represents Stack
assumes Stack { assumes Stack {
int count; int count;
void push(int x) { void push(int x) {
super.push(x); super.push(x);
count++; count++;
} }
void pushAll(int[]) void pushAll(int[] x) {
uses push(int); super.pushAll(x);
count += x.length;
… }
}
void super.pushAll(int[])
!uses push(int);
…
}
Split downcall
Experiment 3:
A Set Library Design
Inference results
For Set with just one
Core data structure option (X1)
Inference results
For Set with four
Core data structure options,
Each with different constraints
Experiment 4:
A Map
particle PMain represents Main {
…
void main(String[]) {
Map m1 = new Map();
// … use m1 one way
Map m2 = new Map();
// … use m2 some other way
Map m3 = new Map();
// … use m3 yet another way
}
…
}
An Evolved RIG for
the Map application
Experiment 5:
An Artificial Ant
An Artificial Ant
Problem:
Evolve an artificial ant
behavior that can
navigate a path
through a grid,
consuming all the
food
A benchmark
problem of Genetic
Programming [Koza]
Traditional GP
Non-Terminals and Terminals
if (foodAhead()) left()
then else right()
progn2 progn3
move()
1 2 1 2 3
Artificial Ant Interfaces in PJ
interface Ant {
void init(Grid g);
void lookForFood();
boolean foodAhead();
void move();
void left();
void right();
boolean alive();
}
interface Behavior {
void behave();
}
interface DecideBehavior interface Action
extends Behavior { extends Behavior {
} }
Artificial Ant Particle
particle PBasicAnt represents Ant {
Behavior behavior;
…
Ant() {
behavior = new Behavior();
}
…
void lookForFood() {
while (alive()) {
behavior.behave();
}
}
}
Ant Behavior Particles
particle PDecideBehavior particle PLeftAction
represents Behavior { represents Action {
… …
void init( Ant a ) { void init( Ant a ) {
ant = a; ant = a;
foodAction = new Action(); nextBehavior = new Behavior();
noFoodAction = new Action(); }
} void behave() {
void behave() { ant.left();
if (foodAhead()) { nextBehavior.behave();
ant.move(); }
foodAction.behave(); }
}
else {
noFoodAction.behave();
} particle PRightAction
} represents Action {
} …
}
particle PEmptyAction particle PMoveAction
represents Action { represents Action {
… …
} }
An Evolved RIG for
the Artificial Ant
Related Work
Related Work
Explicit recomposition methods
AOP
Explicit (Manual) Recomposition
Approaches:
Generative programming [Czarnecki], feature-oriented
programming [Batory], Mixin-Layers [Smaragdakis]
Single inheritance, Multiple inheritance
Mixin-classes [Bracha90][Flatt98], Traits [Scharle03]
The class-composer must know:
The class dependency graph
The internals of each class
The mapping from client interface requirements to
implementing classes
How to assemble classes together
The class-composer must hope:
That things don’t change after composition
Explicit (Manual) Recomposition
vs. Representation Inference
Explicit recomposition:
Human guesses a solution (a partial order on class
increments) using knowledge of an explicit class
dependency graph and implicit class
interdependencies
Representation Inference:
There is no class graph; constraints are explicit in
particles
Provides multiple alternative solutions (particle
sequences) automatically
“No Assembly Required”
AOP vs. IOP
AOP and Aspect-Weaving [Kiczales97]:
Targets cross-cutting concerns, not base component decomposition
Requires fragile knowledge of implementation class naming conventions and
internal statement patterns (implementation bias)
Invasively modifies bodies of methods that are oblivious to the invasion
Both compile-time and run-time modifications
Requires global reasoning to determine correctness
IOP and Representation Inference:
Targets concern decomposition in general, though not currently able to factor
some concerns that AOP can handle
Requires knowledge of interface names and semantics
Affects only explicit method call sites and inheritance dependencies
Not a run-time mechanism
Local reasoning
Recent trends in AOP are towards the use of interfaces to control
and reason about the effect of aspects !
[Kiczales05], [Aldrich05]
Conclusion
Summary
IOP enables improvements to the design
quality, reusability, safety, and evolvability of
software artifacts when compared to OOP.
IOP accomplishes this via:
Ubiquitous interfaces that remove implementation
bias
Partial class particles that enable fine-grained design
decomposition and avoid code replication
Automatic, safe, and robust inference of complete
representations
A pervasive factory mechanism that dynamically binds
interfaces to inferred representations, enabling
context-dependent evolvability of representation
choices without source code modifications
A Spectrum of Paradigms
IOP
GP OOP
Underbiased Overbiased
Expected behavior Expected behavior
of an interface of an interface
is ill-defined (IKIWISI) is well-understood
Generic interfaces Specific interfaces
admitting MANY admitting FEW
representations representations
Future Work
Finish implementation of key features:
Type parameters and parametric representation inference
Roles, anchors, and collaboration inference
Investigate possibility that IOP can subsume most of AOP
via addition of abstract method types?
Improve representation inference algorithm (e.g.,
incremental use of previously inferred representations)
Improve evolvability through better RIG search algorithms,
evolvable representation constants, etc.
Industrial strength implementation, Eclipse tool support,
etc., needed to attempt larger experiments
END
BACKUP SLIDES
A Java Example: List
interface List {
void add(int x);
int remove();
int remove(int pos);
int get();
int size();
…
}
public class ArrayList implements List {
…
ArrayList() { … }
void add(int x) { … }
int remove() { … }
int remove(int pos) { … }
int get() { … }
int size() { … }
…
}
A Java Example: Stack
interface Stack {
void push(int x);
int pop();
int top();
boolean empty();
}
public class StackImpl extends ArrayList implements Stack {
StackImpl() { … }
void push(int x) { … }
int pop() { … }
int top() { … }
boolean empty() { … }
}
A Java Example: A Client of
StackImpl
public class Client {
…
Client() {
…
}
void method() {
…
StackImpl s = new StackImpl();
…
s.remove(0);
…
}
…
}
A Java Example: A Client of
Stack
public class Client {
…
Client() {
…
}
void method() {
…
Stack s = new StackImpl();
…
s.remove(0); // ERROR
…
}
…
}
Another Stack
Implementation
public class StackImpl2 implements Stack {
List list;
StackImpl( List l ) { list = l; }
void push(int x) { … }
int pop() { … }
int top() { … }
boolean empty() { … }
}
Another Client of Stack
public class Client {
…
Client() {
…
}
void method() {
…
Stack s = new StackImpl2(new ArrayList());
…
s.remove(0); // ERROR
…
}
…
}
Categories Problems Causes
Problems Causes Solutions
OLD SLIDES
Problem
Philosophical Assumption:
Large-scale software development requires types, static
type checking, incremental programming support (e.g.,
inheritance), and dynamic binding. This is provided to
some degree by current object-oriented programming
languages.
Problem:
Existing programming languages force early commitment
to particular implementations, which impairs our ability
to reuse code and evolve systems noninvasively.
Problems
Implementation Selection Problem:
Occurs when the best choice of implementation for some interface used by a client
may depend on the context of the client, which varies.
Example: Client must select StackImpl
Trapped Implementation Bias Problem:
Occurs when the choice of implementation for an interface is statically embedded
within the client.
Example: Cannot change choice of StackImpl without changing
Client
Implementation Composition Problem:
Occurs when the client must select multiple mutually compatible implementations
and compose them together.
Example: Client needs to compose StackImpl2 with
ArrayList
Solution
Solution
Interface-Oriented Programming Approach
Interface-Oriented Programming in ARC
Benefits and Costs of ARC
Representation Inference
Interface-Oriented Collaborations in ARC
Basic Idea
In true object-oriented programming, everything
is an object.
In interface-oriented programming, everything is
an interface. What this really means is that:
All non-local dependencies between program
fragments are expressed in terms of abstract
interfaces.
Interface-Oriented Programming
Approach
Identify interface semantics with abstract interface names by convention.
Clients depend only on the abstract interfaces which provide the
semantics they require.
Allow multiple implementations of any abstract interface.
When implementing an abstract interface, the representation should
depend only on abstract interfaces for lower-level services required
(whether inherited, contained, or used).
When implementing an abstract interface, separate the abstraction being
represented for clients from the abstractions needed internally due to the
local design of the representation.
Define representations incrementally – separate partial representations
which make different local assumptions.
When a composition of implementations is needed, clients should specify
a composition of interfaces.
Interface-Oriented Programming
in ARC:
- Abstractions
abs A extends A1,…,An {
// Constructor Interfaces
A();
…
// Method Interfaces
…
}
Interface-Oriented Programming
in ARC:
- Representations assumes SA1,…,SAm {
rep R represents A1,…,An
// Instance Variables
VA1 v1;
…
VAk vk;
// Constructors
A1() { … }
…
An() { … }
// Methods
…
}
Interface-Oriented Programming
in ARC:
- Non-Deterministic
Instantiation assumes X {
rep R …
…
void method() {
…
A object = new A();
…
}
…
}
An ARC Example: Stack
abs Stack { abs List {
Stack(); List();
void push(int x); void add(int x);
int pop(); int remove();
int top(); int remove(int pos);
boolean empty(); int get();
} int size();
…
}
rep StackImpl represents Stack assumes List {
Stack() assumes List() { ; }
void push(int x) { add(x); }
int pop() { return remove(); }
int top() { return get(); }
boolean empty() { return size() == 0; }
}
An ARC Example: A Client of
Stack
rep ClientRep represents Client … {
…
Client() {
…
}
void method() {
…
Stack s = new Stack();
…
s.remove(0); // ERROR
…
}
…
}
Summary of ARC Concepts
Separation of interfaces from implementations:
abs vs. rep
Multiple implementations per interface:
rep R1 represents A …
rep R2 represents A …
Interface-oriented multiple inheritance for client interfaces:
abs A extends A1,…,An …
Interface-oriented multiple inheritance for specialization:
rep R … assumes SA1,…,SAm …
Interface-oriented variable references:
All variables typed with abstraction names
Non-deterministic Instantiation:
A object = new A();
The Benefits of ARC
Encourages programming style where representations make
minimal assumptions. This minimizes trapped
implementation bias and maximizes reusability of
representations.
Enables incremental programming style where
representations may provide partial implementations based
on hidden assumptions. Separately defined partial
representations can be mixed and matched provided their
respective local assumptions are satisfied consistently.
Provides interface-oriented composition of
implementations.
The Costs of ARC
System must correctly link together partial
representation fragments to provide a complete
representation for an abstract interface. This
requires a new kind of “linker”.
Complete representation solution not necessarily
unique. This implies:
The need to provide some way to select from available candidate
representations.
The opportunity to tune representations automatically by
selecting different representations from the space of solutions.
Representation Inference
A new kind of program linking …
Inference of a complete representation for an
abstract interface by assembling multiple partial
representations automatically.
The external and internal interface dependencies of
a partial representation are constraints which must
be satisfied by the complete representation.
Generates one or more solutions, one of which
must be selected.
Representation Inference
Example abs C {
void c1();
void c2();
}
abs A extends C { abs B extends C {
void a1(); void b1();
void a2(); void b2();
void a3(); void b3();
} }
abs D extends A, B {
void d1();
}
Representations of A, B, C, and D
rep RC represents C { rep RD represents D assumes A, B {
… …
void c1() { … } void c1() { … }
void c2() { … } void c2() { … }
} }
rep RB1 represents B {
rep RA1 represents A assumes C { …
… void c1() { … }
void a1() { … } void c2() { … }
void a2() { … } void b1() { … }
void a3() { … } void b2() { … }
} void b3() { … }
}
rep RA2 represents A {
…
void c1() { … } rep RB2 represents B assumes C {
void c2() { … } …
void a1() { … } void b1() { … }
void a2() { … } void b2() { … }
void a3() { … } void b3() { … }
} }
Representation Constraints
RC { d(C) = e(RC) }
RA1 { d(A) = e(RA1) }
RA2 { d(A) = e(RA2), d(C) = e(RA2) }
RB1 { d(B) = e(RB1), d(C) = e(RB1) }
RB2 { d(B) = e(RB2) }
RD { d(D) = e(RD) }
Representation Solutions
Solution 1 Solution 2 Solution 3
d(C) e(RC) e(RA2) e(RB1)
d(A) e(RA1) e(RA2) e(RA1)
d(B) e(RB2) e(RB2) e(RB1)
d(D) e(RD) e(RD) e(RD)
Interface-Oriented Programming
in ARC:
- Collaboration Abstractions {
abs C extends A1,…,An { abs R1 within C
…
collaboration { void foo( role Rr );
role R1; }
…
role Rr; …
}
abs Rr within C {
// Constructors and Methods …
… void bar( role R1 );
void meth1( role R1 ); }
void meth2( role R2 );
…
}
Interface-Oriented Programming
in ARC:
- Representations within
rep R1 represents A1,…,An within C
Collaborations assumes SA1,…,SAm
with role R2 as SA2
…
with role Rr as SAr {
// Instance Variables, Constructors, Methods
…
}
Graph Collaboration: Client Abstractions
abs Graph {
collaboration {
role Vertex;
role Edge;
}
…
control Iterator<role Edge> edges();
control Iterator<role Vertex> vertices();
}
abs Vertex within Graph {
…
int getValue();
control Iterator<role Edge> edges();
}
abs Edge within Graph {
…
role Vertex getVertex1();
role Vertex getVertex2();
}
Graph Collaboration: Specialization
Abstractions
abs IndexedVertex extends Vertex within Graph {
…
int setIndex(int index);
int getIndex()
}
abs IndexedVertexGraph extends Graph {
…
int getMaxVertices();
role Vertex getVertex(int index);
int getIndex(role Vertex);
}
Partial Graph Representation:
Vertices Indexed
rep RIndexedVertexGraph represents IndexedVertexGraph assumes Graph
with role Vertex as IndexedVertex {
// representation of vertex collection as array of vertices
Array<role Vertex> va;
…
role Vertex getVertex( int index ) {
return va[index];
}
int getIndex( role Vertex v ) {
return v.getIndex();
}
…
}
Partial Graph Representation:
Connectivity as Adjacency Matrix
rep RMatrixGraph represents Graph assumes IndexedVertexGraph
with role Vertex as IndexedVertex {
// representation of connectivity as adjacency matrix
Matrix<boolean> matrix;
…
control Iterator<role Edge> edges() {
…
}
control Iterator<role Vertex> adjacentVertices() {
…
}
…
}
Partial Graph Representation:
Connectivity as Array of Adjacency
Lists
rep RListGraph represents Graph assumes IndexedVertexGraph
with role Vertex as IndexedVertex {
// representation of connectivity as array of adjacency lists
Array<MutableSeq<role Vertex>> lists;
…
control Iterator<role Edge> edges() {
…
}
control Iterator<role Vertex> adjacentVertices() {
…
}
…
}
Graph Collaboration: Coloring
Abstractions
abs ColoredVertex extends Vertex within Graph {
…
int setColor(int c);
int getColor()
}
abs ColoredGraph extends Graph {
…
void computeMinColoring();
int getColor(role Vertex);
}
Partial Graph Representation:
Graph Coloring
rep RColoredGraph represents ColoredGraph assumes Graph
with role Vertex as ColoredVertex {
…
void computeMinColoring() {
…
for (role Vertex v in vertices()) {
…
v.setColor( c );
…
}
}
int getColor( role Vertex v ) {
return v.getColor();
}
…
}
Graph Collaboration: Matching
Abstractions
abs MatchedEdge extends Edge within Graph {
…
void setMatched(boolean matched);
boolean isMatched()
}
abs MatchedGraph extends Graph {
…
void computeMinMatching();
int isMatched(role Edge);
}
Partial Graph Representation:
Graph Matching
rep RMatchedGraph represents ColoredGraph assumes Graph
with role Vertex as ColoredVertex {
…
void computeMinMatching() {
…
for (role Edge e in edges()) {
…
e.setMatched( true );
…
}
}
boolean isMatched( role Edge e ) {
return e.isMatched();
}
…
}
Interface-Oriented Composition of
Colored and Matched Graph
Abstractions
final [ColoredGraph,MatchedGraph] cmg = new [ColoredGraph(),MatchedGraph()];
…
cmg.computeMinColoring();
cmg.computeMinMatching();
…
for (cmg.Vertex v in cmg.vertices()) {
…
int color = cmg.getColor(v);
…
}
…
for (cmg.Edge e in cmg.edges()) {
…
boolean matched = cmg.isMatched(e);
…
}
…
Inferred Representations
[ColoredGraph, MatchedGraph] role Vertex role Edge
RIndexedVertexGraph RVertex REdge
RListGraph or RMatrixGraph RIndexedVertex RMatchedEdge
RColoredGraph RColoredVertex
RMatchedGraph
Contributions
Main Contributions
Invention of the concept of Interface-Oriented
Programming (IOP).
Design of ARC, a novel class-based language which
supports IOP and includes advanced support for
object-oriented and collaboration-based design.
Non-deterministic instantiation and representation
inference.
Other Contributions
(Planned, or Not Discussed Here)
Constructor assumptions and constructor blending.
New approaches for control abstraction and
exception handling needed for IOP.
Segmented object model for IOP which provides
constant-time dynamic dispatch for interfaces.
Plan: Assessment of development environment
support needed for IOP.
Plan: Assessment of different techniques for
representation selection.
Related Work
Related Work
(Selected Topics – See Prospectus for More
Details)
Interfaces, Implementations, and Inheritance
Factories
Mixins
Traits and Habitats
Feature-oriented programming
Dual-ported component approaches
Extending IOP to handle Aspects
Method types:
particle PModelUpdate
represents ModelUpdate
assumes ModelUpdate, DisplayUpdate {
…
void modelUpdate() {
super.modelUpdate();
displayUpdate();
}
super.modelUpdate() !uses displayUpdate();
}
Interfaces, Implementations, and
Inheritance
[Sny86b], [AvdL90], [CCHO89], [CP89], [CHC90], [KL92],
Interface inheritance (subtyping) distinct from
[Coo92], [Lam93]
implementation inheritance (subclassing) and
should be kept separate.
Specializers (implementation reusers) need a
different interface than clients (implementation
users).
No known language allows clients or specializers
requiring instantiation to be independent of a
particular implementation.
Factories
[Cop92], [LS94], [Rie95], [GHJV95]
Programming idioms for creating objects which
hides the name of the class being instantiated from
the requestor of the object.
As a programming technique, requires anticipation
of the need for factories.
Works only on whole representations.
Works only for clients, no equivalent support for
specializers.
Requestor must know the name of the factory
implementation (or parameterize it).
Mixins
[BC90], [FKF98]
Abstract subclasses – classes which inherit from
undefined base classes.
MIXEDJAVA:
Provides interface-oriented inheritance to define mixin
implementations (the undefined bases are interfaces).
Only supports no-arg constructors.
Clients must compose mixins by hand and reference
mixin implementations by name.
Cannot use mixins with unbound base interfaces to
create objects.
Traits and Habitats
[RL89], [SDNB03]
Similar to mixins, but for class internals only.
Macro-like techniques for defining the internals of a
class in terms of method fragments. Thus, supports
low-level description of partial representations.
Programmer must compose method fragments
together, and supply dependencies (e.g., accessor
methods for instance variables).
Feature-Oriented Programming
[Pre97]
A “feature” is like a class but:
Implements an interface (not part of the feature; defined
separately).
Defines core functionality separate from its interaction with other
features
Features represent implementations. To use a feature, must
refer to the feature (implementation) by name.
Client must compose feature implementations manually.
Feature composition exposes entire interface to each
feature, which violates encapsulation of the composition in
certain cases.
Interactions limited to two features at a time
Dual-Ported Component
Approaches
[CL01], [CP02], [MO02]
Component model based on two interface ports to a
component:
Provided interface – the interface to main component
functionality
Required interface – the interface the component expects of its
environment
Typically separates implementation from interfaces.
Client must compose implementations of both provided
and required interfaces together.
Exposure of required interface exposes implementation
bias.
Nested class approach suffers from feature combinatorics
problem.
Status and Plan
Status
Key ideas formulated and tested “on paper”:
Interface-oriented programming
Non-deterministic instantiation
Representation inference and selection
Segmented object model
ARC language design substantially complete, but need to
integrate contexts and parametric types.
ARC compiler under development.
Completed initial assessment of approach relative to other
work.
Plan
Establish baseline formal definition of ARC (including support for
contexts and parametric types), and design complete algorithm for
representation inference.
Implement for a subset of ARC:
Representation Inference Mechanism (RIM) in Java
Representation Selection Mechanism (RSM) in Java
Segmented Object Model (SOM) in Java
Compilation of ARC into Java
Interactive development environment in Java
Apply ARC subset to example software design problems.
Explore alternatives for improving representation selection.
Complete overall evaluation of interface-oriented programming relative to
established approaches to software development, and assess requirements
for an IOP development environment.
Schedule
Complete ARC Language Baseline Definition
ARC Subset Implementation
ARC Application, Representation Selection Work, IOP Tool Requirements
Complete Dissertation
Summer 03 Fall 03 Winter 04 Spring 04 Summer 04 Fall 04
The End
BACKUP SLIDES
Implementation-Oriented Programming
Program Construction
Compile
Compiled
Modules
Link/Load
Loaded
Program
Run
Interface-Oriented Programming
Program Construction
Compile
Compiled
Partial Reps
Represent *
Infer
Complete
Link/Load Reps
Select
Run
Implementation-Oriented vs. Interface-
Oriented
Program Construction
Implementation-Oriented:
Client ImplName MachineAddress
Interface-Oriented:
INFER SELECT
Client InterfaceName {Impl1,…,ImplN} ImplK MachineAddress
Segmented Object Model
SegmentIndex MethodPtr
Instance
SegmentIndex MethodPtr
InstanceTable
… …
Segment 0
SegmentIndex MethodPtr
Segment 1
…
Segment K
Segment 0
SegmentIndex MethodPtr
SegmentTable
SegmentIndex MethodPtr
InstanceVar
… …
…
SegmentIndex MethodPtr
InstanceVar
…
Segment K
SegmentIndex MethodPtr
SegmentTable
SegmentIndex MethodPtr
InstanceVar
… …
…
SegmentIndex MethodPtr
InstanceVar
Dynamic Dispatch: External
Casefunc() {
void
…
x.foo();
…
}
void func( Instance self, Segment seg) {
…
Instance xself = self.var[X];
int k = xself.table.segment[SEG_FOO];
Segment xseg = xself.seg[k];
xself.table.method[METH_FOO].call( xself, xseg );
…
}
Dynamic Dispatch: Internal
Casefunc() {
void
…
bar();
…
}
void func( Instance self, Segment seg) {
…
int k = seg.table.segment[SEG_BAR];
Segment barseg = self.seg[k];
seg.table.method[METH_M1].call( self, barseg );
…
}
Representation Instantiation Graph
I0 I1 I2 I3
Defaults
I0=RX I2=RA I2=RB
Back edge to a I3=RM
Default I0,2
Context
Dependent
RepNodes
I0=RY I0=RY
Context-Dependent, Evolvable
Representation Selection via RIGs
RepNode:
A data structure governing the choice of representation for some instantiation site (or
sites) in a program. RepNodes are grouped into “trees” with occasional back-edges to the
root of other RepNode trees.
A Default RepNode:
A root of a RepNode “tree”
RIG (Representation Instantiation Graph):
A forest of RepNode “trees”
Context dependence:
The path to a particular RepNode provides a form of context. The same lexical instantiation
site may correspond to multiple RepNodes corresponding to different dynamic instantiation
contexts.
The RepNode “trees” in a RIG are bounded in depth but can be arbitrarily deep (user
control)
Evolvability:
RIGs can be manipulated without changing source code
Still, manipulated RIGs always chooses representations safely
MetaInstantiator
__ci__Foo__0
0 1 2
0 1 2 0 1 0 1 Instantiator Rep__Foo__K
Get documents about "