Scalable component abstractions
ACM Sigplan Notices (2005)
- ISSN: 03621340
- ISBN: 1595930310
- DOI: 10.1145/1103845.1094815
Available from portal.acm.org
or
Abstract
Abstract. We identify three programming language abstractions for the construction of re-usable components: abstract type members, explicit selftypes and symmetric mixin composition. Together, these abstractions enable us to transform an arbitrary assembly of static program parts with hard references between them into a system of re-usable components. The transformation maintains the structure of the original system. We demonstrate this approach in two case studies, a subject/observer framework and a compiler front-end. 1
Author-supplied keywords
Available from portal.acm.org
Page 1
Scalable component abstractions
Scalable Component Abstractions
Martin Odersky
EPFL
CH›1015 Lausanne
martin.odersky@ep .ch
Matthias Zenger
Google Switzerland GmbH
Freigutstrasse 12
CH›8002 Z rich
zenger@google.com
ABSTRACT
We identify three programming language abstractions for
the construction of reusable components: abstract type
members, explicit selftypes, and modular mixin composi-
tion. Together, these abstractions enable us to transform
an arbitrary assembly of static program parts with hard ref-
erences between them into a system of reusable components.
The transformation maintains the structure of the original
system. We demonstrate this approach in two case studies,
a subject/observer framework and a compiler front-end.
Categories and Subject Descriptors
D.3.3 [Programming Languages]: Language constructs
and features Classes and objects; inheritance; modules;
packages; polymorphism; recursion.
General Terms
Languages
Keywords
Components, classes, abstract types, mixins, Scala.
1. INTRODUCTION
True component systems have been an elusive goal of
the software industry. Ideally, software should be assem-
bled from libraries of pre-written components, just as hard-
ware is assembled from pre-fabricated chips or pre-defined
integrated circuits. In reality, large parts of software ap-
plications are often written from scratch, so that software
production is still more a craft than an industry.
Components in this sense are simply program parts which
are used in some way by larger parts or whole applications.
Components can take many forms; they can be modules,
classes, libraries, frameworks, processes, or web services.
Their size might range from a couple of lines to hundreds of
thousands of lines. They might be linked with other compo-
nents by a variety of mechanisms, such as aggregation, pa-
rameterization, inheritance, remote invocation, or message
Permission to make digital or hard copies of all or part of this work for
personal or classroom use is granted without fee provided that copies are
not made or distributed for pro t or commercial advantage and that copies
bear this notice and the full citation on the rst page. To copy otherwise, to
republish, to post on servers or to redistribute to lists, requires prior speci c
permission and/or a fee.
OOPSLA’05, October 16 20, 2005, San Diego, California, USA.
Copyright 2005 ACM 1›59593›031›0/05/0010 ...$5.00.
passing.
An important requirement for components is that they are
reusable; that is, that they should be applicable in contexts
other than the one in which they have been developed. Gen-
erally, one requires that component reuse should be possible
without modifiying a component's source code. Such mod-
ifications are undesirable because they have a tendency to
create versioning problems. For instance, a version conflict
might arise between an adaptation of a component in some
client application and a newer version of the original compo-
nent. Often, one goes even further in requiring that compo-
nents are distributed and deployed only in binary form [43].
To enable safe reuse, a component needs to have interfaces
for provided as well as for required services through which
interactions with other components occur. To enable flexible
reuse in new contexts, a component should also minimize
hard links to specific other components which it requires
for its functioning.
We argue that, at least to some extent, the lack of progress
in component software is due to shortcomings in the pro-
gramming languages used to define and integrate compo-
nents. Most existing languages offer only limited support
for component abstraction and composition. This holds in
particular for statically typed languages such as Java [16]
and C# [9] in which much of today's component software
is written. While these languages offer some support for at-
taching interfaces describing the provided services of a com-
ponent, they lack the capability to abstract over the services
that are required. Consequently, most software modules are
written with hard references to required modules. It is then
not possible to reuse a module in a new context that refines
or refactors some of those required modules.
Ideally, it should be possible to lift an arbitrary system of
software components with static data and hard references,
resulting in a system with the same structure, but with nei-
ther static data nor hard references. The result of such a
lifting should create components that are first-class values.
We have identified three programming language abstractions
that enable such liftings.
Abstract type members provide a flexible way to abstract
over concrete types of components. Abstract types
can hide information about internals of a component,
similar to their use in SML signatures. In an object-
oriented framework where classes can be extended
by inheritance, they may also be used as a flexible
means of parameterization (often called family poly-
morphism [11]).
Martin Odersky
EPFL
CH›1015 Lausanne
martin.odersky@ep .ch
Matthias Zenger
Google Switzerland GmbH
Freigutstrasse 12
CH›8002 Z rich
zenger@google.com
ABSTRACT
We identify three programming language abstractions for
the construction of reusable components: abstract type
members, explicit selftypes, and modular mixin composi-
tion. Together, these abstractions enable us to transform
an arbitrary assembly of static program parts with hard ref-
erences between them into a system of reusable components.
The transformation maintains the structure of the original
system. We demonstrate this approach in two case studies,
a subject/observer framework and a compiler front-end.
Categories and Subject Descriptors
D.3.3 [Programming Languages]: Language constructs
and features Classes and objects; inheritance; modules;
packages; polymorphism; recursion.
General Terms
Languages
Keywords
Components, classes, abstract types, mixins, Scala.
1. INTRODUCTION
True component systems have been an elusive goal of
the software industry. Ideally, software should be assem-
bled from libraries of pre-written components, just as hard-
ware is assembled from pre-fabricated chips or pre-defined
integrated circuits. In reality, large parts of software ap-
plications are often written from scratch, so that software
production is still more a craft than an industry.
Components in this sense are simply program parts which
are used in some way by larger parts or whole applications.
Components can take many forms; they can be modules,
classes, libraries, frameworks, processes, or web services.
Their size might range from a couple of lines to hundreds of
thousands of lines. They might be linked with other compo-
nents by a variety of mechanisms, such as aggregation, pa-
rameterization, inheritance, remote invocation, or message
Permission to make digital or hard copies of all or part of this work for
personal or classroom use is granted without fee provided that copies are
not made or distributed for pro t or commercial advantage and that copies
bear this notice and the full citation on the rst page. To copy otherwise, to
republish, to post on servers or to redistribute to lists, requires prior speci c
permission and/or a fee.
OOPSLA’05, October 16 20, 2005, San Diego, California, USA.
Copyright 2005 ACM 1›59593›031›0/05/0010 ...$5.00.
passing.
An important requirement for components is that they are
reusable; that is, that they should be applicable in contexts
other than the one in which they have been developed. Gen-
erally, one requires that component reuse should be possible
without modifiying a component's source code. Such mod-
ifications are undesirable because they have a tendency to
create versioning problems. For instance, a version conflict
might arise between an adaptation of a component in some
client application and a newer version of the original compo-
nent. Often, one goes even further in requiring that compo-
nents are distributed and deployed only in binary form [43].
To enable safe reuse, a component needs to have interfaces
for provided as well as for required services through which
interactions with other components occur. To enable flexible
reuse in new contexts, a component should also minimize
hard links to specific other components which it requires
for its functioning.
We argue that, at least to some extent, the lack of progress
in component software is due to shortcomings in the pro-
gramming languages used to define and integrate compo-
nents. Most existing languages offer only limited support
for component abstraction and composition. This holds in
particular for statically typed languages such as Java [16]
and C# [9] in which much of today's component software
is written. While these languages offer some support for at-
taching interfaces describing the provided services of a com-
ponent, they lack the capability to abstract over the services
that are required. Consequently, most software modules are
written with hard references to required modules. It is then
not possible to reuse a module in a new context that refines
or refactors some of those required modules.
Ideally, it should be possible to lift an arbitrary system of
software components with static data and hard references,
resulting in a system with the same structure, but with nei-
ther static data nor hard references. The result of such a
lifting should create components that are first-class values.
We have identified three programming language abstractions
that enable such liftings.
Abstract type members provide a flexible way to abstract
over concrete types of components. Abstract types
can hide information about internals of a component,
similar to their use in SML signatures. In an object-
oriented framework where classes can be extended
by inheritance, they may also be used as a flexible
means of parameterization (often called family poly-
morphism [11]).
Page 2
Selftype annotations allow one to attach a programmer-
defined type to this. This turns out to be a convenient
way to express required services of a component at the
level where it connects with other components.
Modular mixin composition provides a flexible way to com-
pose components and component types. Unlike func-
tor applications, mixin compositions can establish re-
cursive references between cooperating components.
No explicit wiring between provided and required ser-
vices is needed. Services are modelled as component
members. Provided and required services are matched
by name and therefore do not have to be associated
explicitly by hand.
All three abstractions have their theoretical foundation in
the νObj calculus [35]. They have been defined and im-
plemented in the programming language Scala. We have
used them extensively in a component-oriented rewrite of
the Scala compiler, with encouraging results.
The three abstractions are scalable, in the sense that they
can describe very small as well as very large components.
Scalability is ensured by the principle that the result of a
composition should have the same fundamental properties
as its constituents. In our case, components correspond to
classes, and the result of a component composition is always
a class again, which might have abstract members and a self-
type annotation, and which might be composed with other
classes using mixin composition. Classes on every level can
create objects (also called runtime components) which are
first-class values, and therefore are freely configurable.
Related work
The concept of functor [27, 17, 24] in the module systems
of SML [17] and OCaml [24], provides a way to abstract
over required services in a statically type-checked setting. It
represents an important step towards true component soft-
ware. However, functors still pose severe restrictions when it
comes to structuring components. Recursive references be-
tween separately compiled components are not allowed and
inheritance with dynamic binding is not available.
ML modules, as well as other component formalisms [1,
30, 42, 51] introduce separate layers that distinguish be-
tween components and their constituents. This approach
might have some advantages in that each formalism can be
tailored to its specific needs, and that programmers receive
good syntactic guidance. But it limits scalability of compo-
nent systems. After all, what is a complicated system on one
level might be a simple element on the next level of scale.
For instance, the Scala compiler itself is certainly a non-
trivial system, but it is treated simply as an object when
used as a plugin for the Eclipse [33] programming environ-
ment. Furthermore, different instantiations of the compiler
might exist simultaneously at runtime. For example, one
instantiation might do a project rebuild, while another one
might do a syntax check of a currently edited source file.
Those instantiations of the compiler should have no shared
state, except for the Eclipse runtime environment and the
global file system. In a system where the results of a compo-
sition are not objects or classes, this is very hard to achieve.
Scala's aim to provide advanced constructs for the ab-
straction and composition of components is shared by sev-
eral other research efforts. From Beta [28] comes the idea
that everything should be nestable, including classes. To
address the problem of expressing nested structures that
span several source files, Beta provides a fragment sys-
tem as a mechanism for weaving programs, which is out-
side the language proper. This is similar to what is done in
aspect-oriented programming (indeed, the fragment system
has been used to emulate AOP [23]).
Abstract types in Scala have close resemblances to ab-
stract types of signatures in the module systems of SML and
OCaml, generalizing them to a context of first-class compo-
nents. Abstract types are also very similar to the virtual
types [29] of the Beta and gbeta languages. In fact, virtual
types in Beta can be modelled precisely in Scala by a com-
bination of abstract types and selftype annotations. Virtual
types as found in gbeta are more powerful than either Scala's
or Beta's constructions, since they can be inherited as su-
perclasses. This opens up possibilities for advanced forms of
class hierarchy reuse [12], but it makes it very hard to check
for accidental and incompatible overrides. Closely related
are also the delegation layers of Caesar [38, 31], FamilyJ's
virtual classes [48] and the work on nested inheritance for
Java [32].
Scala's design of mixins comes from object-oriented linear
mixins [3], but defines mixin composition in a symmetric
way, similar to what is found in mixin modules [8, 18] or
traits [41]. Jiazzi [30] is an extension of Java that adds a
module mechanism based on units [15], a powerful form of
parametrized modules. Jiazzi supports extensibility idioms
similar to Scala, such as the ability to implement mixins.
Jiazzi is built on top of Java, but its module language is
not integrated with Java and therefore is used more like a
separate language for linking Java code.
OCaml [25] and Moby [13] are both languages that
combine functional and object-oriented programming using
static typing. Unlike Scala, these two languages start with
a rich functional language including a sophisticated module
system and then build on these a comparatively lightweight
mechanism for classes.
The only close analogue to selftype annotations in Scala
is found in OCaml, where the type of self is an extensible
record type which is explicitly given or inferred. This gives
OCaml considerable flexibility in modelling examples that
are otherwise hard to express in statically typed languages.
But the context in which selftypes are used is different in
both languages. Instead of subtyping, OCaml uses a system
of parametric polymorhism with extensible records. The ob-
ject system and module systems in OCaml are kept separate.
Since selftypes are found only in the object system, they play
a lesser role in component abstraction than in Scala.
The rest of this paper is structured as follows. Section 2
introduces Scala's programming constructs for component
abstraction and composition. Section 3 shows how these
constructs are applied in a type-safe subject/observer frame-
work. Section 4 discusses a larger case study where the Scala
compiler itself was transformed into a system with reusable
components. Section 5 discusses lessons learned from the
case studies. Section 6 concludes.
2. CONSTRUCTS FOR COMPONENT AB›
STRACTION AND COMPOSITION
This section introduces the language constructs of Scala
insofar as they are necessary to understand the cases stud-
defined type to this. This turns out to be a convenient
way to express required services of a component at the
level where it connects with other components.
Modular mixin composition provides a flexible way to com-
pose components and component types. Unlike func-
tor applications, mixin compositions can establish re-
cursive references between cooperating components.
No explicit wiring between provided and required ser-
vices is needed. Services are modelled as component
members. Provided and required services are matched
by name and therefore do not have to be associated
explicitly by hand.
All three abstractions have their theoretical foundation in
the νObj calculus [35]. They have been defined and im-
plemented in the programming language Scala. We have
used them extensively in a component-oriented rewrite of
the Scala compiler, with encouraging results.
The three abstractions are scalable, in the sense that they
can describe very small as well as very large components.
Scalability is ensured by the principle that the result of a
composition should have the same fundamental properties
as its constituents. In our case, components correspond to
classes, and the result of a component composition is always
a class again, which might have abstract members and a self-
type annotation, and which might be composed with other
classes using mixin composition. Classes on every level can
create objects (also called runtime components) which are
first-class values, and therefore are freely configurable.
Related work
The concept of functor [27, 17, 24] in the module systems
of SML [17] and OCaml [24], provides a way to abstract
over required services in a statically type-checked setting. It
represents an important step towards true component soft-
ware. However, functors still pose severe restrictions when it
comes to structuring components. Recursive references be-
tween separately compiled components are not allowed and
inheritance with dynamic binding is not available.
ML modules, as well as other component formalisms [1,
30, 42, 51] introduce separate layers that distinguish be-
tween components and their constituents. This approach
might have some advantages in that each formalism can be
tailored to its specific needs, and that programmers receive
good syntactic guidance. But it limits scalability of compo-
nent systems. After all, what is a complicated system on one
level might be a simple element on the next level of scale.
For instance, the Scala compiler itself is certainly a non-
trivial system, but it is treated simply as an object when
used as a plugin for the Eclipse [33] programming environ-
ment. Furthermore, different instantiations of the compiler
might exist simultaneously at runtime. For example, one
instantiation might do a project rebuild, while another one
might do a syntax check of a currently edited source file.
Those instantiations of the compiler should have no shared
state, except for the Eclipse runtime environment and the
global file system. In a system where the results of a compo-
sition are not objects or classes, this is very hard to achieve.
Scala's aim to provide advanced constructs for the ab-
straction and composition of components is shared by sev-
eral other research efforts. From Beta [28] comes the idea
that everything should be nestable, including classes. To
address the problem of expressing nested structures that
span several source files, Beta provides a fragment sys-
tem as a mechanism for weaving programs, which is out-
side the language proper. This is similar to what is done in
aspect-oriented programming (indeed, the fragment system
has been used to emulate AOP [23]).
Abstract types in Scala have close resemblances to ab-
stract types of signatures in the module systems of SML and
OCaml, generalizing them to a context of first-class compo-
nents. Abstract types are also very similar to the virtual
types [29] of the Beta and gbeta languages. In fact, virtual
types in Beta can be modelled precisely in Scala by a com-
bination of abstract types and selftype annotations. Virtual
types as found in gbeta are more powerful than either Scala's
or Beta's constructions, since they can be inherited as su-
perclasses. This opens up possibilities for advanced forms of
class hierarchy reuse [12], but it makes it very hard to check
for accidental and incompatible overrides. Closely related
are also the delegation layers of Caesar [38, 31], FamilyJ's
virtual classes [48] and the work on nested inheritance for
Java [32].
Scala's design of mixins comes from object-oriented linear
mixins [3], but defines mixin composition in a symmetric
way, similar to what is found in mixin modules [8, 18] or
traits [41]. Jiazzi [30] is an extension of Java that adds a
module mechanism based on units [15], a powerful form of
parametrized modules. Jiazzi supports extensibility idioms
similar to Scala, such as the ability to implement mixins.
Jiazzi is built on top of Java, but its module language is
not integrated with Java and therefore is used more like a
separate language for linking Java code.
OCaml [25] and Moby [13] are both languages that
combine functional and object-oriented programming using
static typing. Unlike Scala, these two languages start with
a rich functional language including a sophisticated module
system and then build on these a comparatively lightweight
mechanism for classes.
The only close analogue to selftype annotations in Scala
is found in OCaml, where the type of self is an extensible
record type which is explicitly given or inferred. This gives
OCaml considerable flexibility in modelling examples that
are otherwise hard to express in statically typed languages.
But the context in which selftypes are used is different in
both languages. Instead of subtyping, OCaml uses a system
of parametric polymorhism with extensible records. The ob-
ject system and module systems in OCaml are kept separate.
Since selftypes are found only in the object system, they play
a lesser role in component abstraction than in Scala.
The rest of this paper is structured as follows. Section 2
introduces Scala's programming constructs for component
abstraction and composition. Section 3 shows how these
constructs are applied in a type-safe subject/observer frame-
work. Section 4 discusses a larger case study where the Scala
compiler itself was transformed into a system with reusable
components. Section 5 discusses lessons learned from the
case studies. Section 6 concludes.
2. CONSTRUCTS FOR COMPONENT AB›
STRACTION AND COMPOSITION
This section introduces the language constructs of Scala
insofar as they are necessary to understand the cases stud-
Sign up today - FREE
Mendeley saves you time finding and organizing research. Learn more
- All your research in one place
- Add and import papers easily
- Access it anywhere, anytime
Start using Mendeley in seconds!
Readership Statistics
54 Readers on Mendeley
by Discipline
4% Mathematics
2% Engineering
by Academic Status
43% Ph.D. Student
19% Other Professional
11% Student (Master)
by Country
22% United States
17% France
9% United Kingdom



