|
Poppy is a compact and expressive object-oriented programming
language, exemplifying the theory of classification. The type system
for Poppy is based on the second-order theory of F-bounds
[1],
rather than the first-order theory of subtyping
[2].
Classes are treated as parametric polymorphic families of types,
possessing at least a given interface. While the formal treatment
of polymorphism would normally require the use of type parameters,
Poppy deliberately hides this in its surface syntax, using systematic
type substitutions
[3]
on class identifiers to describe the propagation of new types into
variables, when these are bound to more specific objects. Class
parameters are rebound, especially during inheritance, where the
self-type evolves automatically
[4].
The type system checks that each
method invocation is correctly typed in the calling context, by
propagating specific types into general variables. As a result,
Poppy naturally supports second-order classes, whose methods are
recursively closed over each new class, avoiding the problems
associated with covariant argument restriction in type systems based
on first-order subtyping
[5].
This is a new project, currently seeking to attract EPSRC funding
for an RA and a PhD student. It is based on formal results from the
Theory of Classification and
experimental work on parametric language design in the
Brunel 2.0 programming language.
Poppy is the first practical object-oriented programming language to
treat classes correctly as second-order polymorphic families of types
(c.f. the type classes in Haskell), unifying the notion of
class with the usual parametric treatment of generic types.
Poppy also aims to be a practical language, hiding the complexity
of polymorphic types and type propagation into parameters from the
programmer, where this can be handled in more effective ways.
As an example of how elegant the syntax of the Poppy language might look,
imagine a Pair class representing an ordered pair of values:
class (Pair self) extends (Object super) {
class First renames Object; /* First class */
class Second renames Object; /* Second class */
First first; /* first projection */
Second second; /* second projection */
Pair copy(First left, Second right) { /* copy constructor */
first := left;
second := right;
self
}
}
This illustrates a number of syntactic aspects central to the design of
the language (as well as a few other things explained below):
- The default encapsulation rule granting public read-only access obviates
the need for keywords; the most common cases requires no visibility, scope
or abstract annotations;
- Explicit introduction of self and super variables,
treated just like other variables - supports explicit method combination
on multiple parent classes;
- Construction allocates a blank object, whose state is copy-initialised
under the object's control; supports object streaming with many fewer
constructors;
- Methods only need parentheses where arguments are supplied; supports
the fusion of access methods and attributes; both return a result.
Now, imagine a more restricted Entry class, representing an entry
in a phone book, which specialises the Pair class, so that the
first projection is a String, and the second projection is an
Integer:
class (Entry self) extends (Pair pair, TotalOrder order) {
class First renames String; /* First rebound */
class Second renames Integer; /* Second rebound */
class Other renames Entry; /* Other binary class */
Boolean lessThan (Other other) {
first.lessThan(other.first) /* Compare on names */
}
Boolean equal (Other other) {
first.equal(other.first) /* Compare on names */
}
}
This illustrates a number of other features, principally how easy it is
to specialise the types of fields and methods, but also the use of
multiple classification:
- Multiple classification of concepts: an Entry is both a kind
of Pair and a kind of TotalOrder;
- Type substitution: the First and Second parameters are
rebound to more constrained classes;
- Adaptation of inherited methods: methods obtained from TotalOrder
adapt to the Entry class.
- Binary methods: arguments can be the same class as self, or
a subclass of self's class;
- Simple method syntax: all methods return a result, the last
expression in a sequence.
For further examples of how Poppy might look in practice, browse some of
the example classes in the Poppy
source code directory. Please note that this is a work in progress
and ongoing changes may mean that styles may not yet be consistent.
[1] P Canning, W Cook, W Hill, W Olthoff and
J Mitchell,
F-bounded polymorphism for object-oriented programming,
Proc 4th Int. Conf. Func. Progr. Lang. and Arch., September,
(London: Imperial College, 1989), 273-280.
[2] L Cardelli and P Wegner,
On understanding types, data abstraction and polymorphism,
ACM Computing Surveys, 17 (4), (ACM Press, 1985), 471-521.
[3] J Palsberg and M Schwartzbach,
Object-Oriented Type Systems, (London: John Wiley, 1994).
[4] W Cook, W Hill and P Canning,
Inheritance is not subtyping,
Proc 17th ACM Symp. Principles of Progr. Lang.,
(ACM Press, 1990), 125-135.
[5] W Cook, A proposal for making Eiffel
type-safe, Proc 3rd European Conf. Object-Oriented Progr.,
57-70; reprinted in Computer Journal, 32 (4),
(Nottingham: BCS/Pitman, 1989), 305-311.
The main working document about Poppy is the
Poppy Language
Manifesto. Please note that this is a work in progress, and is
still being developed. We put this version out to stimulate some
early discussion!
Nothing ese has appeared in print that is directly related to Poppy.
Earlier related publications in object-oriented type theory and
language design may be found on:
|