10
Type-Theoretic Design Patterns Ondˇ rej Ryp´ cek Roland Backhouse Henrik Nilsson School of Computer Science and Information Technology The University of Nottingham, UK oxr,rcb,[email protected] Abstract The development of design patterns in object-oriented program- ming aims at capturing good software design in a re-usable generic form. However, design patterns are not expressible in conventional object-oriented programming languages. To address this shortcom- ing, we need to model and understand design patterns precisely. We achieve this by identifying operators characterising the most fun- damental design patterns in a way that enables the construction of object-oriented programs with provable structural and behavioural properties. We use dependent-type theory to define a simplified, functional model of object-oriented programming. Design patterns are modelled in this setting as operators on object signatures and implementations. We present examples of several basic design op- erators and design patterns modelled in this setting and show how properties of their composition can be proven. Categories and Subject Descriptors F.3.1 [Logics and Mean- ing of Programs]: Specifying and Verifying and Reasoning about Programs—Logics of programs; F.3.3 [Logics and Meaning of Programs]: Studies of Program Constructs; D.3.3 [Programming Languages]: Language Constructs and Features—Patterns, classes and objects General Terms Languages, Theory Keywords Design patterns, formal method, dependent types, object-oriented programming, functional programming, functional objects, visitor pattern, program correctness, reasoning, language features 1. Introduction The development of “design patterns” [18, 12] in object-oriented programming aims at capturing good software design in a reusable generic form. A design pattern represents a prepared solution to a common design problem that is readily available to program- mers. Besides being helpful in the coding phase, design patterns also serve an important role in software-design description, docu- mentation and communication, and, as a result, have become well established in the practice of developing object-oriented software. Despite their proven utility, due to lack of suitable abstraction mechanisms, design patterns cannot be expressed directly in con- ventional object-oriented programming languages. Consequently, 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 profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. WGP’06 September 16, 2006, Portland, Oregon, USA. Copyright c 2006 ACM 1-59593-492-6/06/0009. . . $5.00 in textbooks and articles, such as [18], design patterns are described in an informal combination of natural language, UML diagrams and snippets of example code. This makes them hard for program- mers to use, because they have to be implemented by hand. More- over, there is no way to check that pattern instances are correct with respect to their intended meaning, and they don’t evolve with the rest of the program. This practice also impairs the documentation and communication roles, as patterns, once implemented, disperse into the target low-level language and most evidence of them is lost. As design patterns represent the distillation of many recurring design problems in the software development practice, we consider this fact a major failure of current object-oriented programming languages. There is an ongoing debate about the way and extent to which design patterns should be supported by programming languages. Gil and Lorenz suggest that some specific patterns should directly become language features [19]. We adhere to a different approach, advocated also by Chambers [11]. We believe that design patterns should be studied in order to provide guidance for the development of novel abstraction mechanisms that directly support implemen- tation of and reasoning about design patterns. Instead of adding a fixed set of ready-made patterns, the right abstraction and construc- tion mechanisms should be described that support development of the patterns in all their variants and guises by the programmers in the target language. In order to study design patterns, we model objects in dependent- type theory in a way that makes it possible to write programs by induction on the structure of objects and to prove various properties of the programs. This model forms the basis of our study of the abstractions conveyed by design patterns. We define a collection of fundamental refinement operators on objects with formally provable computational properties. By us- ing these operators to build large object structures incremen- tally, we make it possible to establish properties of the struc- tures. Some of the operators correspond closely to constructions found in design patterns. We show how the elementary op- erators can be combined to form larger operators that model design patterns of the “gang-of-four” kind introduced in [18]. We show how this approach leads to a precise formalisation of design patterns in code as first-class entities. Moreover, it makes it feasible to formally prove properties of object-oriented programs, as the operators used in their construction witness the properties. We demonstrate this with several examples. In Section 2, we start by formalizing a simple object-based lan- guage in a dependent type theory. We use the Calculus of Inductive Constructions of Coquand and Huet [13] but other dependent-type theories would be equally suitable. This formalism allows us not

Type-theoretic design patterns

Embed Size (px)

Citation preview

Type-Theoretic Design Patterns

Ondrej Rypacek Roland Backhouse Henrik NilssonSchool of Computer Science and Information Technology

The University of Nottingham, UKoxr,rcb,[email protected]

AbstractThe development of design patterns in object-oriented program-ming aims at capturing good software design in a re-usable genericform. However, design patterns are not expressible in conventionalobject-oriented programming languages. To address this shortcom-ing, we need to model and understand design patterns precisely. Weachieve this by identifying operators characterising the most fun-damental design patterns in a way that enables the construction ofobject-oriented programs with provable structural and behaviouralproperties. We use dependent-type theory to define a simplified,functional model of object-oriented programming. Design patternsare modelled in this setting as operators on object signatures andimplementations. We present examples of several basic design op-erators and design patterns modelled in this setting and show howproperties of their composition can be proven.

Categories and Subject Descriptors F.3.1 [Logics and Mean-ing of Programs]: Specifying and Verifying and Reasoning aboutPrograms—Logics of programs; F.3.3 [Logics and Meaning ofPrograms]: Studies of Program Constructs; D.3.3 [ProgrammingLanguages]: Language Constructs and Features—Patterns,classes and objects

General Terms Languages, Theory

Keywords Design patterns, formal method, dependent types,object-oriented programming, functional programming, functionalobjects, visitor pattern, program correctness, reasoning, languagefeatures

1. IntroductionThe development of “design patterns” [18, 12] in object-orientedprogramming aims at capturing good software design in a reusablegeneric form. A design pattern represents a prepared solution toa common design problem that is readily available to program-mers. Besides being helpful in the coding phase, design patternsalso serve an important role in software-design description, docu-mentation and communication, and, as a result, have become wellestablished in the practice of developing object-oriented software.

Despite their proven utility, due to lack of suitable abstractionmechanisms, design patterns cannot be expressed directly in con-ventional object-oriented programming languages. Consequently,

Permission to make digital or hard copies of all or part of this work for personal orclassroom use is granted without fee provided that copies are not made or distributedfor profit or commercial advantage and that copies bear this notice and the full citationon the first page. To copy otherwise, to republish, to post on servers or to redistributeto lists, requires prior specific permission and/or a fee.WGP’06 September 16, 2006, Portland, Oregon, USA.Copyright c© 2006 ACM 1-59593-492-6/06/0009. . . $5.00

in textbooks and articles, such as [18], design patterns are describedin an informal combination of natural language, UML diagramsand snippets of example code. This makes them hard for program-mers to use, because they have to be implemented by hand. More-over, there is no way to check that pattern instances are correct withrespect to their intended meaning, and they don’t evolve with therest of the program. This practice also impairs the documentationand communication roles, as patterns, once implemented, disperseinto the target low-level language and most evidence of them islost. As design patterns represent the distillation of many recurringdesign problems in the software development practice, we considerthis fact a major failure of current object-oriented programminglanguages.

There is an ongoing debate about the way and extent to whichdesign patterns should be supported by programming languages.Gil and Lorenz suggest that some specific patterns should directlybecome language features [19]. We adhere to a different approach,advocated also by Chambers [11]. We believe that design patternsshould be studied in order to provide guidance for the developmentof novel abstraction mechanisms that directly support implemen-tation of and reasoning about design patterns. Instead of adding afixed set of ready-made patterns, the right abstraction and construc-tion mechanisms should be described that support development ofthe patterns in all their variants and guises by the programmers inthe target language.

• In order to study design patterns, we model objects in dependent-type theory in a way that makes it possible to write programsby induction on the structure of objects and to prove variousproperties of the programs. This model forms the basis of ourstudy of the abstractions conveyed by design patterns.

• We define a collection of fundamental refinement operators onobjects with formally provable computational properties. By us-ing these operators to build large object structures incremen-tally, we make it possible to establish properties of the struc-tures.

• Some of the operators correspond closely to constructionsfound in design patterns. We show how the elementary op-erators can be combined to form larger operators that modeldesign patterns of the “gang-of-four” kind introduced in [18].

• We show how this approach leads to a precise formalisationof design patterns in code as first-class entities. Moreover, itmakes it feasible to formally prove properties of object-orientedprograms, as the operators used in their construction witness theproperties. We demonstrate this with several examples.

In Section 2, we start by formalizing a simple object-based lan-guage in a dependent type theory. We use the Calculus of InductiveConstructions of Coquand and Huet [13] but other dependent-typetheories would be equally suitable. This formalism allows us not

only to express the syntax and typing of objects precisely but alsoto express fundamental properties of objects.

In Section 3, we introduce a handful of operators on object sig-natures and implementations. These operators model elementarysteps in object-oriented design and provide a structured and soundalternative to general inheritance. We show that programs con-structed by these well-defined refinement steps from well-definedcomponents exhibit provable properties in contrast to unstructuredextension and overriding.

In Section 4, we define several specific operators derived fromconstructions found in design patterns. We use them, together withthe elementary ones, to model several design patterns: decorator,flyweight, composite and visitor.

We demonstrate how our approach enables expression of bothstructural and behavioural properties of the patterns. In Section4.3, the composite operator is defined, which composes objects aswell as their behaviour. We combine it with the visitor operator inSection 4.4, to define the composite visitor.

Although we introduce all necessary notation as it is used inthe text, basic familiarity with dependent-type theory will helpunderstand the text. Some standard references are e.g. [6, 25].Familiarity with conventional object-oriented programming anddesign patterns is expected.

All the presented material has been implemented and compiled,up to minor syntactical differences, in the Coq proof assistant andits underlying theory, the Calculus of Inductive Constructions [6].

2. ObjectsIn this section we present a simplified purely functional modelof objects. Our development of objects follows the simple-typetheoretic approach of Pierce and Turner [26]. In the model, mutablestate is represented by a value threaded through all methods ofan object. Each method takes a value of the internal state as anadditional parameter and produces a new, possibly different, valueof the state as its output together with any regular return value.

For example, a method plus taking two natural numbers as aparameter and producing their sum, with a state (side-effect) of typebool is represented by the function

plus : (bool × (nat × nat)) → (bool × nat)

Here, bool and nat are types and × stands for the binary product oftwo types. Objects are modelled as records of functions represent-ing methods together with a value of the internal state. The inter-nal state (its type and value) is hidden by existential quantification.The implementation is thus not accessible from the outside and itis possible to mix objects with the same interface and different im-plementation that have the same existential type.

Dependent-type theory gives us the ability to specify object in-terfaces precisely and to manipulate them as values. This is essen-tial to our constructive approach to object-oriented design. In therest of this section we introduce the definitions of object signature,signature implementation and object.

Definition 1. Given a set of method identifiers – a namespace N ,a signature is a function that assigns a pair of types, the source andtarget, to any name in the namespace, N :

Sig(N : Set) := N → Set × Set (1)

Here, Set is the universe of all small types – types whose modelsare proper sets. The namespace, N , is written on the left-hand sideas a parameter to the definition. This is to make the definition morereadable. The above definition (1) could have equivalently beenwritten as follows:

Sig := 〈λ(N : Set) . N → Set × Set 〉

The λ quantification defines a function. It binds a variable N oftype Set in the right-hand side. We usually use the form of (1) forformal parameters, like types, in order to let the right-hand sidereveal the core of the definition. However, the choice is purelyaesthetic as the two versions are semantically identical.Definition 2. For every signature, s : Sig N , src and tgt arefunctions that assign the source and target types, respectively, toany name n : N :

src(s : Sig N) : N → Set := fst ◦ s

tgt (s : Sig N) : N → Set := snd ◦ s

Thus, for a signature s and an identifier n in the namespace ofthe signature, (src s n) and (tgt s n) denote the source and targettype, respectively, assigned by s to n. This enables manipulation ofall source and target types of a signature as a whole in the point-free style. We adhere to the convention of leaving out parametersthat can be inferred from other parameters. In the definition above,the namespace, N , is inferable from the type, Sig N , of s. The typeof Sig also implies that N is of type Set. We use juxtaposition forfunction application.

A signature describes the public interface of an object. An im-plementation of a given signature is a product of methods indexedby N . For each name n : N , it consists of a function that takes avalue of an internal state, or representation for short, and a value ofthe source type specified by the signature, src s n, and produces anew representation together with a value of the target type specifiedby the signature, tgt s n.Definition 3. An implementation of a given signature on N with arepresentation type, R, is defined as follows.

Impl (s : Sig N)(R : Set) :=〈Π(n : N) . (R × (src s n)) → (R × (tgt s n)) 〉

Definition 4. An object of a given signature is a pair of an imple-mentation and a value of its representation type. The representationtype is hidden by existential quantification, introduced by Σ:

Object(s : Sig N) := 〈Σ(R : Set) . R × (Impl s R) 〉

Object values are introduced as follows:

[(r, i)]

where r : R is an initial value of the internal representation typeand i : (Impl s R) is an implementation for some s : Sig N .

Although objects cannot be directly invoked, they can be sentmessages without breaking the encapsulation. We define a functionsend, that for any name in the namespace of the signature of anobject and an input parameter of the correct type opens the exis-tential object package, invokes the corresponding function of theimplementation and closes the object again.Definition 5. For every namespace N , the function send is definedas follows.

send(s : Sig N)(n : N) :Object s → src s n → ((Object s) × (tgt s n))

send s n :=〈λ o x .

let (q , i) := oin let (q′, x′) := i n (q, x)in ([(q′, i)], x′) 〉

Here, we use the standard let syntax to unpack the existentialpackage as a pair of the current state and an implementation andto bind the pair of a new state and a return value returned by theimplementation. Finally, we repack the object with the new stateand return the return value.

Example 1. Consider a class of simple counters defined informallyas follows.class Counter {

get: Unit -> natadd: nat -> Unit

}

The class has two methods, get and add. Their intended meaningis that the get method is an attribute – it takes an argument oftype Unit, which is equivalent to taking no arguments and returnsthe current state of the counter. The second method, add, takes anatural number and adds it to the current state.

The namespace of the signature of counter, Ncntr , is the set{ get, add }. We then have the following definition for the signa-ture of counter.

Scntr : Sig Ncntr := 〈λ(n : Ncntr ) . case n of

| get 7→ Unit × nat

| add 7→ nat × Unit

end 〉

Given a signature, Scntr , of counters, we define the type of objectswith that signature as follows.

Counter := Object Scntr

A possible implementation of Counter with a representation typenat is defined as follows.

Icntr : Impl Scntr nat

:= 〈λ(n : Ncntr ) . case n of| get 7→ 〈λ(x : nat × Unit) . (fst x, fst x) 〉| add 7→ 〈λ(x : nat × nat) .

((fst x) + (snd x), unit) 〉〉

Now, given an initial state for the counter we can construct aCounter object as follows.mkCounter : nat → Counter := 〈λ(r : nat) . [(r, Icntr )] 〉

3. Elementary OperatorsEquipped with a functional model of objects, we will in this sectiondefine some elementary building blocks and operators for combin-ing objects. We present their actions on signatures first and laterextend them to implementations.

3.1 Signatures

3.1.1 Join

Informally, the join of two signatures, s with methods{m1, . . . , mk} and t with methods {m′

1, . . . , m′

l}, is a signatures ⊕ t with methods {m1, . . . , mk, m′

1, . . . , m′

l}. In order to for-malize join on signatures, we introduce a more general operator:the junc. It takes a pair of functions with the same domain to afunction on the sum of their ranges.

O : 〈Π(f : A → C)(g : B → C) . (A + B) → C 〉

O := 〈λ(f : A → C)(g : B → C)(x : A + B) .

case x of (2)| inl a 7→ f a

| inr b 7→ g b

end 〉

For any types A, B, A + B denotes the disjoint union of A and Bwith injections inl and inr to the left and right component of thesum, respectively.

We can now specify the join on signatures, denoted as ⊕, to bethe junc of the signatures regarded as functions from a namespaceto the type Set × Set (see Definition 1).Definition 6.

⊕ : Sig N → Sig M → Sig (N + M)⊕ := (O)

(3)

The following holds immediately by the familiar properties ofjunc (see e.g. [7, 23]) for any s : Sig N and t : Sig M .

(s ⊕ t) ◦ inl = s(s ⊕ t) ◦ inr = t

(4)

That is, (s ⊕ t) agrees with s on the left component of the names-pace and with t on the right component of the namespace for anyname in the namespace. The following equations characterize joinin terms of src and tgt .

src (s ⊕ t) = (src s)O (src t)tgt (s ⊕ t) = (tgt s)O (tgt t)

(5)

Example 2. Suppose we want to extend our counter (Example 1)with a multiplication method. Let the namespace of the multiplica-tion signature, Nmlt , be the set {mult}. The multiplication inter-face is defined as follows.

Smlt : Sig Nmlt := 〈λ(n : N) . case n of| mult 7→ nat × Unit

end 〉(6)

That is, method mult takes a natural number and returns nothing.We may join the multiplication signature, Smlt , with the originalcounter signature, Scntr .

SCMult := Scntr ⊕ Smlt

The namespace of the joined signature, SCMult , is the set{inl get, inl add, inr mult}. It corresponds to the followinginterface.class Counter {

get: Unit -> natadd: nat -> Unitmult: nat -> Unit

}

3.1.2 Coercion

By the rule of subsumption, a subtype can be regarded as an ob-ject of a supertype and used in any context where the supertype isacceptable. In our model, this corresponds to an object of a com-posite signature, say s⊕ t, being coerced to an object of any of thesub-signatures, either s or t.

The coercion is contravariantly determined by the left or rightinjection, inl or inr , on namespaces.Definition 7. For any s : Sig (N + M), there is a function, exl ,defined as follows.

exl : Sig (N + M) → Sig Nexl s := s ◦ inl

(7)

It is immediate, by (4) and (7), thatexl (t ⊕ u) = t

By exactly the same construction for inr , we get the other projec-tion, exr .Definition 8.

exr : Sig (N + M) → Sig Nexr s := s ◦ inr

exr (t ⊕ u) = u

The above construction can be easily generalised for any twonamespaces, M and N , and a function, f : M → N , betweenthem.Definition 9. We define function coerce between signatures asfollows.

coerce (f : M → N) : Sig N → Sig Mcoerce f s := s ◦ f

(8)

By taking T ⊕U for N , inl for f and writing exl for coerce inlin (8), we get definition (7). And similarly for inr and exr .

exl = coerce inl

exr = coerce inr

3.1.3 Product

The product operator combines two signatures, s : Sig N andt : Sig M , to a signature s ⊗ t : Sig (N × M). Names in s ⊗ tare pairs of names from the source signatures, source types in s⊗ tare pairs of source types of s and t and target types in s ⊗ t arepairs of target types of s and t. Intuitively, a method in the productsignature corresponds to a pair of methods from the two signatures.

Formally, let M be the split operator [23] that takes a pair offunctions with the same domain to a function returning a pair ofresults.

M : 〈Π(f : A → B) .〈Π(g : A → C) . A → B × C 〉 〉

f M g := 〈λ(a : A) . (f a, g a) 〉

Operator ⊗ is defined in terms of split as follows.Definition 10.

⊗ : Sig N → Sig M → Sig (N × M)s ⊗ t := ((fst × fst) M (snd × snd)) ◦ (s × t)

(9)

Operator × in fst × fst and snd × snd is the standard productof functions. The following holds immediately by the properties ofsplit and products for any s : Sig N and t : Sig M .

src (s ⊗ t) = (src s) × (src t)tgt (s ⊗ t) = (tgt s) × (tgt t)

(10)

In the following definition, we introduce a useful refinement of theproduct.Definition 11. Let delta be the diagonal function idMid. Then thediagonal product is defined as follows:

∆ : Sig N → Sig N → Sig Ns ∆ t := coerce delta (s ⊗ t)

(11)

Example 3. The product Scntr ⊗ Scntr is the following interface.class Counter_Counter {

get_get: Unit x Unit -> nat x natget_add: Unit x nat -> nat x Unitadd_get: nat x Unit -> Unit x natadd_add: nat x nat -> Unit x Unit

}

Its coercion along the diagonal of the namespace is a signature oftwo Counters running in parallel. Formally,

Scntr ∆ Scntr

corresponds to the following informal interface which essentiallyconsists of pairs of functions of the original counter.class Counter {

get: Unit x Unit -> nat x natadd: nat x nat -> Unit x Unit

}

3.1.4 Composition

As signatures are essentially products of function types, we maycompose them, given the precondition that the source and targettypes of all methods match. We formalize this precondition by thefollowing definition of composability.Definition 12. Signatures s : Sig N and t : Sig N are compos-able, iff the target type assigned by t to any n : N is the same asthe source type assigned to n by s. Formally,

s / t := (src s = tgt t). (12)Definition 13. Operator � has the following type.

� : 〈Π(s : Sig N)(t : Sig N) . (s / t) → Sig N 〉

The implementation is characterised by the following two equa-tions:

src (s � t) = src t

tgt (s � t) = tgt s

We use � as an infix operator, writing s � t instead of � s t.We take the liberty of leaving the proof of composability out,merely assuming its existence. Where not obvious, we justify ourassumption in text without cluttering the syntax unnecessarily.

By associativity of function composition, the following holdsfor all composable signatures s, t and u.

(s � t) � u = s � (t � u)

We may thus omit parentheses in chained compositions.

3.1.5 Nose and Tail

Definition 14. Signatures nose and tail represent the source andtarget sides of the signature.

nose(s : Sig N) : Sig N := (src s) M (src s)tail(s : Sig N) : Sig N := (tgt s) M (tgt s)

Thus nose s is formed by pairs of source types of s and tail sis formed by pairs of target types of s.Example 4. In practice, noses and tails are signatures of objectsthat transform parameters and results of objects, which are essentialfor a formal definition of decorators. According to [18],

. . . The decorator conforms to the interface of the compo-nent it decorates so that its presence is transparent to thecomponent’s clients. The decorator forwards requests to thecomponent and may perform additional actions (such asdrawing a border) before or after forwarding.

Suppose we want to define a doubling decorator for counters,that is, an object that doubles any numerical argument of anymethod of Counter (Example 1). It may be implemented as follows.

Idbl : Impl (nose Scntr ) nat

Idbl := 〈λ(n : Ncntr ) . case n of

| get 7→ 〈λ(x : nat × Unit) . x 〉

| add 7→ 〈λ(x : nat × nat) . (fst x, 2 ∗ snd x) 〉

This implementation, Idbl , with signature (nose Scntr ) is ready tobe pre-composed with the implementation of counters, Icntr , toyield a counter that adds everything twice. The signature of thecomposite is Scntr �Sdbl . We must, however, establish the fact thatthe signatures are composable, that is that the results of Idbl canbe used as inputs to Icntr . Fortunately, we know this immediatelyas noses are always pre-composable and tails are always post-composable.

Formally, the following holds for all signatures s.s / (nose s)(tail s) / s

(13)

Note also that noses and tails are the right and left units ofcomposition, respectively.

s � (nose s) = s(tail s) � s = s

(14)

This formally establishes that Idbl defines a decorator: an objectwith the same interface that modifies the behaviour of anotherobject.

3.2 Implementations

In this section, we extend the operators on signatures to imple-mentations – stateful computations. We deliberately avoid the term“class” as a class comprises an implementation together with a typewhile our approach is object-based. Implementations don’t standfor types. Object-types are defined by signatures and there may bemany unrelated implementations with the same signature.

3.2.1 Join

Informally, the join of two implementations is an implementationobtained by putting the implementations next to each other in sucha way that each acts on its part of the signature.

Definition 15. For two implementations, i and j, with the samerepresentation types and signatures s and t, respectively, the joinedimplementation i ⊕ j with the joined signature s ⊕ t is defined asfollows:

⊕ : Impl s R → Impl t R → Impl (s ⊕ t) R

(i ⊕ j) ◦ inl = i(i ⊕ j) ◦ inr = j

Remember that implementations are products of functions in-dexed by a set (Definition 3). By the definition of ⊕ for signatures(Definition 6), the namespace of the joined implementation is a sumof the namespaces of the operands. The above equations state thatthe functions in the joined implementation are those of the operandsinjected to the left and right component of the sum, respectively.

Example 5. In Example 2, we defined a signature, Smlt , withone method, mult. Here is its straightforward implementation withstate of type nat.

Imlt : Impl Smlt nat := 〈λ(n : Nmlt ) . case n of| mult 7→

〈λ(x : nat × nat) .(fst x ∗ snd x, unit) 〉

By joining this implementation with the implementation of coun-ters, Icntr (Example 1), we obtain an implementation that supportsboth addition and multiplication. The two parts of the implementa-tion share their state of type nat.

ICMult := Icntr ⊕ Imlt

3.2.2 Coercion

Coercion of implementations is defined analogously to coercion ofsignatures (8).

Definition 16.

coerce (f : M → N) : Impl s R → Impl (coerce f s)Rcoerce f i := i ◦ f

(15)

3.2.3 Product

The product of two implementations is an implementation obtainedby putting the implementations together in such a way that eachimplementation processes a different (disjoint) part of the inputparameters and produces the corresponding parts of the output.Definition 17. For signatures s : Sig N and t : Sig M and forrepresentation types, R and S, the product of two implementationis defined as follows:

⊗ : Impl s R → Impl t S → Impl (s ⊗ t) (R × S)i ⊗ j := 〈λn . 〈λ(pq, xy) .

let (p′, x ′) := i (fst n) (fst pq , fst xy)in let (q ′, y ′) := j (snd n) (snd pq , snd xy)in ((p′, q ′), (x ′, y ′)) 〉 〉

The symbol, ⊗, is used as an infix operator as before. Thedefinition of the diagonal product of implementations is analogicalto the one for signatures.Definition 18. For all implementations i and j with the samenamespace, the following defines their diagonal product:

i ∆ j := coerce delta (i ⊗ j)

3.2.4 Composition

Definition 19. Given two implementations, i : Impl s R andj : Impl t R, such that their signatures, s and t, are composable,their composition is a composition of the corresponding functionsin the implementations:

� : 〈Π(i : Impl s R)(j : Impl t R) . Impl (t � s) R 〉

〈Π(n : N) . (i � j) n = (i n) ◦ (j n) 〉(16)

By associativity of function and signature composition, the fol-lowing holds for all composable implementations i,j,k.

(i � j) � k = i � (j � k)

We may thus omit parentheses in chained compositions.Example 6. We now formally finish Example 4, where an imple-mentation, Idbl , of a doubling transformer of inputs to counter wasintroduced. We formally define:

DblCounter : Impl (Scntr � Sdbl ) nat

DblCounter := Icntr � Idbl

As discussed before, the composition of signatures is valid and dueto (14), we may even write

DblCounter : Impl Scntr nat

3.2.5 Weakening

In Example 6, we defined the composition Icntr � Idbl . The com-position was valid only because the two implementations were de-fined with the same representation type, nat. However, at the pointof definition of Idbl , in Example 4, there was no obvious reasonfor choosing this particular representation type and, in general, wecannot expect programmers to have the kind of foresight we haveexhibited by our choice. Moreover, Idbl doesn’t make any use of therepresentation. It has no side-effect at all, it transforms parameters.It should therefore be possible to combine such a component withimplementations of any representation type as long as they have thecorrect signature.

In this section we define the weakening operator that adjoins adummy variable to an implementation with any representation type.The dummy variable is threaded intact through all methods of theimplementation. This enables definition of implementations withthe minimal necessary representations and reshaping them later tothe required form.

Definition 20. Let i : Impl s R be an implementation withsignature s : Sig N and representation type R. Let S be a type inSet. Then the following defines an implementation.

i/S : Impl s (R × S)i/S := 〈λ(n : N) .

〈λ(((p, q), x ) : (R × S) × (src s n)) .let (p′, x′) := i n (p, x)in ((p′, q), x′) 〉 〉

(17)

Adjoining to the left is defined similarly:

S\i : Impl s (S × R)

Using weakening, important parallel variants of join and com-position can be defined.

3.2.6 Parallel operators

The operator ⊕ requires the two arguments to have the same repre-sentation type. This is necessary to allow the state of the two com-ponents to be threaded through. In effect, both components sharethe same state and their side-effects interleave. For instance, in Ex-ample 2, the multiplication part, Imult operates on the same stateas the addition part, Icntr . We call operators with this property co-alesced.

Sometimes, however, a different operation is required, thatkeeps the states, i.e. side-effects, separate. We define a paralleljoin operator, in terms of join and weakening.Definition 21.

⊕ : Impl s R → Impl t S → Impl (s ⊕ t) (R × S)i⊕ j := (i/S) ⊕ (R\j)

In exactly the same way, a parallel version of composition isdefined.Definition 22.

� : Impl s R → Impl t S → Impl (s � t) (R × S)i � j := (i/S) � (R\j)

Example 7. With weakening, and parallel composition in particu-lar, we may redefine Idbl (Example 4) properly with the unit state.Idbl′ : Impl (nose Scntr ) Unit

Idbl′ := 〈λ(n : Ncntr ) . case n of

| get 7→ 〈λ(x : Unit × Unit) . x 〉

| add 7→ 〈λ(x : Unit × nat) . (unit, 2 ∗ snd x) 〉

Using parallel composition, we obtain the same implementation asbefore.

DblCounter′ : Impl (Scntr ) nat

DblCounter′ := Icntr � Idbl′

Note that Idbl′ can be composed with any implementation of Scntr ,regardless of its internal state. The identity behaviour on the stateof Icntr , which was previously hardcoded into Idbl is now providedby the operator.

3.3 Delegation

All operators in Section 3.2 were defined to act on implemen-tations. That is, they are refinements of programs where bothstates and implementations are accessible and free to be modi-fied. They produce stateful programs from stateful programs. Thiscorresponds to the scenario where programmers are working withclasses while making use of other classes by the means of inher-itance, general modifications to source code, cutting and pastingpieces of code together. We have started to replace this approachwith structured operators with well-defined effect.

Until now, we have not covered delegation or any operatorson objects. We remedy this by defining a single operator that for-malises delegation and makes it possible to use the operators eitherwith open implementations or with closed objects.Definition 23. For any signature s : Sig N , the following imple-mentation:

[ s ] : Impl s (Object s) (18)is the implementation that delegates every method n ∈ N to an

object with signature s in its state via send (Definition 5).

Example 8. Suppose we want to create a doubling decorator of acounter object, that doubles all arguments and then delegates toan object given in a constructor. As we have the doubling pre-decorator Idbl′ ready, we must just combine it in the right way.

We define the implementation first.Idbl∗ : Impl Scntr (Object Scntr )Idbl∗ := [ Scntr ] � Idbl′

An object with this implementation is constructed by the fol-lowing constructor.

mkDblCounter := 〈λ(o : Object Scntr ) . [(o, Idbl∗)] 〉

4. Design PatternsIn the previous section, we have shown several design combinators,which enable development of structured object-oriented programs.Moreover, our running example of “doubling counter” suggests aclose connection between the Decorator design pattern [18] andfunction composition. This is no surprise as we believe that manydesign patterns of [18] are instances of elementary mathematicalphenomena. We further elaborate on the decorators in Section 4.1by abstracting away from the examples and formulating an abstractdefinition of our decorator operator.

In a similar fashion, we analyse several other design patternsof the “gang-of-four” book [18] and define operators that capturethe essential core of the patterns. The informal patterns can bereconstructed as the set of all implementations constructed by theoperators.

4.1 Decorator

Based on the examples, we define an abstract decorator operatoras follows.

decorator : 〈Π(s : Sig N)(R : Set)(pre : Impl (nose s) R)(post : Impl (tail s) R). Impl s R → Impl s R 〉

decorator pre post i := post � i � pre

The operator takes a pre-decorator pre (a stateful transformerof inputs) a post-decorator post (a stateful transformer of outputs)and an existing implementation, i, and composes them to form animplementation with the same signature. As shown before, all theother operators provide the necessary freedom of implementation.Example 9. It is now possible to formalize the doubling decoratorof Example 8 abstractly using the decorator operator.

DblCntrDec(o : Object Scntr ) :=decorator (Idbl′/Object Scntr ) id (Unit\[ o ])

Here, id stands for the identity implementation with signature(tail Scntr ) and representation type (Unit × Object Scntr ). Thetwo weakening steps are necessary in order to match the represen-tation types of the implementations.

4.2 Flyweight

The Flyweight pattern [18], describes means to share instances ofobjects. The key idea of the pattern is an isomorphism betweenobjects with data stored in their state and objects with data passedin parameters with each method call. By moving data out of objectstate, the possibility of sharing object instances increases. In theextreme case, a state-less object is just a collection of functions.Such objects are often called static in object-oriented languages.

In this sense, the Flyweight pattern captures the connectionbetween stateful and stateless computations.

In object-oriented design, there is always the choice betweenthe stateful and the stateless approach. A parameter can either bepassed to a method or set as a property of the object. A result of anoperation can be either returned by the method directly or stored inthe object state and retrieved later by one of the methods. Despitethe choice, which is a matter of personal taste, convenience of usageand performance, the behaviour of the object remains the same.That is, for the same input, provided in whatever form, the sameresult should be produced.

The following is a type of the flyweight operator, which shifts avariable form the state to the parameters of all methods. We leavethe implementation as an exercise.

flyweight : 〈Π(s : Sig N) .Impl s (R × S) → Impl (distr S s) R 〉

Here, distr is a an auxiliary function on signatures that dis-tributes its first argument, a Set, into every source and target typeof the second argument, a signature.

distr : Set → Sig N → Sig N

src (distr S s) n = S × (src s n)

To express the isomorphism between stateful and stateless ob-jects, we define an inverse operation, called heavyweight. It is for-mally provable in our setting that they are inverse to each other.

heavyweight : 〈Π(s : Sig N)(S : Set)(t : Sig N) .(t = distr S s) →Impl t (R) → Impl s (R × S) 〉

(19)

heavyweight ◦ flyweight = id

flyweight ◦ heavyweight = id

Of course, the heavyweight is not applicable to all implementa-tions but only to those with a sufficiently uniform signature. Thiscondition is traceable in the type of heavyweight in definition (19),second line.

4.3 Composite

The essence of the Composite pattern [18] is an object that standsfor a collection of objects. That is,• its state is the collective state of all its components• its behaviour is a collective behaviour of all its components• it has the same signature as its components

Note that the above properties characterize exactly the collectiveaspect of the object called “Composite” in the Composite pattern.In practice, “Composites” (in the vague sense) often fulfil moreroles than just those enumerated above. In particular, they are oftenalso collections of their components, with methods to manipulatethem. Or they might have other roles, which aren’t directly relatedwith the composite role. We strictly separate these object rolesand provide other means to combine objects with other roles, joininterfaces, etc.

What follows is a definition of a composite operator that giventwo implementations of a given interface and a collection of func-tions to combine the results of their methods produces a new im-plementation.

composite(s : Sig N) :〈Π(fork : Impl (src s M (src s M src s)) (R × S))(fold : Impl ((tgt s M tgt s) M tgt s) (R × S)) .Impl s R → Impl s S → Impl s (R × S) 〉

composite fork fold i j := fold � (i∆j) � fork

(20)

The parameter fork forks input parameters of the signature. Itduplicates and possibly amends them. On each copy of the inputs,the two components, i and j, operate in parallel in (i∆j). Finally,the results are combined by fold to produce the output of thecomposite. fold defines the way results of the components aremerged (folded) together.

The version presented here is a composite where the containerof components is just a pair. Nevertheless, composites of any fi-nite arity can be produced by iteration.This simplified model issufficient for our presentation, although, in practice, more sophis-ticated ways of storing components would be required. The defini-tion would then be parameterized by a general container with a foldfunction to combine the results.

4.4 Visitor

The Visitor pattern [18] is a smart way of using dynamic dispatchin object-oriented programming to implement type-safe case anal-ysis on a fixed set of objects. Although it is often associated withtraversal of hierarchical object structures, we claim that traversingis a common behaviour of operations on hierarchical object struc-tures, not a specific property of Visitors. Nevertheless, we are ableto reconstruct this behaviour with a combination of visitor and com-posite operators.

There is a close correspondence between the Visitor pattern andfunctional encoding of the binary sum type [9]. In the polymorphiclambda calculus, System F [21], binary sum is usually defined asfollows.

A + B := 〈 ∀X . ((A → X) × (B → X)) → X 〉 (21)

For any a : A and b : B, the values inl a : A + B andinr b : A + B are defined as follows.

inl a := 〈ΛX .〈λ((f, g) : (A → X) × (B → X)) .

f a 〉 〉inr b := 〈ΛX .

〈λ((f, g) : (A → X) × (B → X)) .g b 〉 〉

(22)

Our definition of visitor proceeds in close analogy with theabove definition. With our encoding of objects, it then becomesobvious that a visitor is a collection of methods (c.f. (f, g) in(22)) that are passed to the visited objects. Each object invokesone method of the collection and returns the result of the visit(c.f. the applications f a and g b in (22)). An implementation ofVisitor for a concrete collection of object types is thus a methoddefined by case analysis on the collection of objects. We present asimplified stateless version of the visitor. This corresponds ratherclosely to the above example. In the presence of side-effects, thetypes become more involved.

Formally, the visitor operator is parameterized by a product ofsignatures. It produces a signature of the visitor, which correspondsto the pair of types (A → X) × (B → X) in (21). For eachcomponent in the product, there is also an operator that produces animplementation of method accept that invokes the correct method

in the visitor. These correspond to the inl and inr constructors in(c.f. 22).

In the rest of this section we assume the following definitions.N : Set

Ns : 〈Π(n : N) . Set 〉Sigs : 〈Π(n : N) . Sig (Ns n) 〉

(23)

Here, N is the namespace of the visitor. It is the index of theproduct of signatures, Sigs that define the visitor. Ns is a productof namespaces of the signatures.

The signature of the visitor is defined as follows. Here, X is thereturn type of the method.

vis(X : Set) : Sig N:= 〈λ(n : N) . Object (Sigs n) × X 〉

The n-th visit method takes an object of the n-th signature, andreturns a value of X . The n accept methods are defined as follows.

accepting (X : Set) : Sig Unit :=〈λn .Object (vis X) × X 〉

accept (n : N)(i : Impl (Sigs n) R) :〈Π(X : Set) . Impl (accepting X) R 〉

accept n i :=〈λ(X : Set) .〈λ( : Unit)((r, v) : R × Object (vis X )) .

(r, snd (send v n [(r, i)])) 〉 〉

The n-th method takes an implementation of the n-th signature,a return type, a visitor together with the current state of the object(c.f. (f, g) in (22) ) and applies the n-th visit method to an object ofthe n-th signature and implementation i. It returns the return valueof the visit method.

We can now put the two pieces together to form a formal visitoroperator as follows.

visitor (X : Set)(reps : N → Set)(impls : 〈Π(n : N) . Impl (Sigs n) (reps n) 〉) :(Sig N)×〈Π(n : N) . Impl ((Sigs n) ⊕ (accepting X)) (reps n) 〉

visitor X reps impls :=(vis X ,〈λn . (impls n) ⊕ (accept n (impls n) X) 〉)

For a given return type X and a product of implementations,impls, the visitor operator produces a signature of the visitortogether with a product of implementations, each with an acceptmethod.

The careful reader will have noticed that our definition of thevisitor operator differs from the description of the visitor pattern in[18] in that our visitor and accepting participants are not mutuallyrecursive. We claim, however, that this kind of recursion is notessential to the visitor pattern, as exposed by the above separationof concerns.

4.4.1 Composite Visitors and Visitors of Composites

Visitors are often used for traversing composite data structuresrepresented by object hierarchies. However, there are visitors thatdon’t perform any traversal at all despite the object being compositeor not. An example is the following:

class TypeOfVisitor {visitA(A a) : String := "A"visitB(B b) : String := "B"

}

It would therefore be incorrect to build traversals into the visitoroperators.

There are three kinds of situation comprising visitors and com-posite structures.1. A visitor of an object that has some “subcomponents”, but is

not a formal composite in the sense of Section 4.3.2. A visitor of a formal composite object.3. A formal composite of objects accepting a visitor.

The first case corresponds to the situation in informal object-oriented design. It is obviously up to the visitor to perform traversalof the “subcomponents”, as there is not enough information todistinguish between them and other attributes.

In case (2), by definition of the composite operator (20), alloperations of the composite object are defined correctly in terms ofoperations of the subcomponents. Therefore, operations performedby the visitor via the interface of the visited object are necessarilydefined in terms of the whole hierarchy.

Formally, for any set X , any signature s and two its implemen-tations i and j with representation types Ri and Rj respectively,and for any forking and folding implementations f and g, the visi-tor of the hierarchy comprising i, j and their composite is definedas follows:

visitor X (Ri, Rj , Ri × Rj) (i, j, (composite f g i j))

The last case, (3), corresponds to what is called an internal visi-tor in [9], where recursive invocation of accept on subcomponentsof a composite is within the accept method of the composite. Thecomposite is formed after the accept methods of the subcompo-nents are specified. Therefore, the accept method of the compositeis defined in terms of accept methods of the subcomponents. This isautomatically guaranteed by our construction. Formally, for someindices i and j

W := composite f g(snd (visitor X reps impls) i)(snd (visitor X reps impls) j)

(24)

We prove formally that W , a composite visitor, indeed vis-its the subcomponents. We compute as follows with the constantacc standing for the name of the accept method implementationsproduced by visitor. Formally, acc ≡ inr unit. We write v forsnd (visitor X reps impls).

W acc

= {Equation 24}

(composite f g (v i) (v j)) acc

= {by Equation 20}

(g � ((v i)∆(v j)) � f) acc

= {by Equation 16}

(g acc) ◦ (((v i)∆(v j)) acc) ◦ (f acc)

The last equation states clearly that any input to (W acc) is firstforked by f , then passed through the accept method of the parallelproduct of the components v i and v j, which, by definition, runsthe two accept methods in parallel. The results are then combinedtogether by the accept method of g.

5. Related WorkThere is a large body of work on formalizing design patterns byvarious means. We summarise just the most relevant approacheshere and compare them to our work.

The interest in applications of dependent-type theory to variousfields of the theory of programming is recently on the increase.In [27], the author models objects in dependent-type theory. Theapproach is based on the concept of active objects [30], which we

consider too different from the conventional model to provide muchinsight into conventional design patterns.

There are several approaches to support design patterns directlyby the programming language. In [10, 17], the authors presentso called pattern-oriented programming, an extension of object-oriented programming with a new kind of structuring mechanism,called superposition, which allows instantiation and compositionof design patterns to be expressed. The approach, however, cap-tures only the structural aspect of design patterns, and is unable tospecify and compose behaviour.

Other researchers [8, 5, 3, 4] introduce specialised models ofobject-oriented programming to support the specification of soft-ware components and design patterns. We, on the contrary, try tostay as close to the conventional object-oriented model as possible.We simplify the model in order to reveal the structure of object-oriented programs and patterns.

Many researchers concentrate on developing formalisms forreasoning about and tool support for design patterns without ex-tending the base language [5, 16, 15, 14, 2, 22, 24, 28]. These ap-proaches are however external in that they support specification ofpatterns outside of the target language.

There exist a few recent accounts on formalizing design patternsby analysing the more primitive particles they are combined from.In [29], several simple visitor combinators are introduced whichare used to construct tree-traversal algorithms. In [28], several el-emental design-patterns are introduced and formalized in a simpleextension of the ς-calculus of Abadi and Cardelli [1]. The workis aimed at program analysis rather than program construction. In[20], the authors report on an empirical analysis of recurrent con-structs in a large corpus existing Java code. The analysis is basedon purely syntactical criteria and it is very specific to the Java lan-guage.

6. ConclusionWe have introduced a model of functional objects in dependent-type theory, which is conceptually very close to intuition and otherformal models of functional objects. However, it is much moreprecise. We have demonstrated how such a precise model can beexploited to structure object-oriented design.

We have formalized several fundamental operators on objectsand have demonstrated how they enable the construction of object-oriented programs with provable structural and behavioural prop-erties.

Moreover, we have identified several design patterns as describ-ing fundamental design constructs. We have formalized these con-structs in our model and reconstructed the design patterns formallyas first-class citizens in the model. We have shown how the formaldesign patterns can be combined to recover the full spectrum ofobject-oriented programs described by the informal patterns.

There are certain issues that will have to be tackled in the future.In particular, the strong-normalisation property of the Calculus ofConstructions doesn’t allow us to provide a truly faithful model ofobjects. In object-oriented languages with unconstrained recursion,object definitions are often mutually recursive. In a strongly nor-malising model, definitions of mutually recursive objects have tobe much more involved as they must be provided with a proof oftermination of all methods. So far, this hasn’t hindered our devel-opment as the main goal was to identify and formalize abstractionspresent in the informal design patterns, which are not essentiallymutually recursive. However, to bring our work closer to conven-tional object-oriented languages and to apply it to substantial exam-ples, we would either have to move to a computationally strongertheory or to a more complicated model.

AcknowledgmentsThe authors would like to thank Ralf Lammel, Pablo Nogueira andthe reviewers for their contribution and feedback. This research hasbeen funded by EPSRC grant EP/D502632/1.

References[1] Martin Abadi and Luca Cardelli. A Theory of Objects. Monographs

in Computer Science. Springer Verlag, 1996. ABA m 96:1 1.Ex.[2] Vasu Alagar and Ralf Lammel. Three-tiered specification of micro-

architectures. citeseer.ist.psu.edu/493152.html.[3] P. Alencar, D. Cowan, K. Lichtner, C. Lucena, and L. Nova. Tool

support for formal design patterns. Technical Report CS-9536,University of Waterloo, Waterloo, Ontario, Canada, 1995.

[4] P. Alencar, D. Cowan, and C. Lucena. A formal approach toarchitectural design patterns. In Proceedings of the 3rd InternationalSymposium of formal Methods Europe, pages 576 – 594, 1996.

[5] P. Alencar, D. Cowan, C. Lucena, and L. Nova. Formal specificationof reusable interface objects. In ACM SIGSOFT Symposium onSoftware Reusability, pages 88–96, 1995.

[6] Yves Bertot and Pierre Casteran. Interactive Theorem Provingand Program Development. Coq’Art: The Calculus of InductiveConstructions. Texts in Theoretical Computer Science. SpringerVerlag, 2004.

[7] Richard Bird and Oege De Moor. Algebra of Programming. PrenticeHall PTR, September 1996.

[8] Jan Bosch. Design patterns as language constructs. Journal ofObject-Oriented Programming, 11(2):18–32, 1998.

[9] Peter Buchlovsky and Hayo Thielecke. A type-theoretic reconstruc-tion of the visitor pattern. In 21st Conference on Mathematical Foun-dations of Programming Semantics. http://www.cl.cam.ac.uk/ pb368/mfps-visitors.pdf.

[10] S. Bunnig, P. Forbrig, R. Lammel, and N. Seemann. A programminglanguage for design patterns. http://citeseer.ist.psu.edu/306047.html.

[11] C. Chambers, B. Harrison, and J. Vlissides. A debate on languageand tool support for design patterns. In Proceedings of the 27thACM SIGPLAN-SIGACT symposium on Principles of programminglanguages, pages 277–289. ACM Press, 2000.

[12] James O. Coplien and Douglas C. Schmidt. Pattern languages ofprogram design. ACM Press/Addison-Wesley Publishing Co., 1995.

[13] Thierry Coquand and Gerard Huet. The calculus of constructions.Technical Report 530, INRIA, 1986.

[14] Jing Dong. A logical framework for design composition. InProceedings of the 22nd international conference on Softwareengineering, pages 698–700. ACM Press, 2000.

[15] A. Eden, J. Gil, and A. Yehudai. A formal language for designpatterns (extended abstract). http://citeseer.ist.psu.edu/171003.html.

[16] A. Eden, J. Gil, and A. Yehudai. Precise specification and automaticapplication of design patterns. In Proceedings of the 12th IEEEInternational Automated Software Engineering Conference – ASE1997, 1997.

[17] Peter Forbrig and Ralf Lammel. Programming with patterns.http://citeseer.ist.psu.edu/ 312457.html.

[18] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns:Elements of Reusable Object-Oriented Software. Addison-Wesley,1995.

[19] Joseph Gil and David H. Lorenz. Design patterns vs. language design.Lecture Notes in Computer Science, 1357, 1998.

[20] Joseph (Yossi) Gil and Itay Maman. Micro patterns in java code.SIGPLAN Not., 40(10):97–116, 2005.

[21] Jean-Yves Girard, Yves Lafont, and Paul Taylor. Proofs and Types,volume 7 of Cambridge Tracts in Theoretical Computer Science.Cambridge University Press, 1989.

[22] K. Lano, J. Bicarregui, and S. Goldsack. Formalising design patterns.In D. Duke and A. Evans, editors, 1st BCS-FACS Northern FormalMethods Workshop, Electronic Workshops in Computing. Ilkley, UK,Springer-Verlag, 1996.

[23] Erik Meijer, Maarten Fokkinga, and Ross Paterson. Functionalprogramming with bananas, lenses, envelopes and barbed wire. InProceedings of the 5th ACM conference on Functional programminglanguages and computer architecture, pages 124–144, New York,NY, USA, 1991. Springer-Verlag New York, Inc.

[24] Tommi Mikkonen. Formalizing design patterns. In Proceedings ofthe 20th international conference on Software engineering, pages115–124. IEEE Computer Society, 1998.

[25] B. Nordstrom, K. Petersson, and J.M. Smith. Programming in Martin-Lof’s Type Theory: An Introduction. Oxford University Press, 1990.

[26] Benjamin C. Pierce and David N. Turner. Simple type-theoreticfoundations for object-oriented programming. Journal of FunctionalProgramming, 4(2):207–247, 1994.

[27] Anton Setzer. Object-oriented programming in dependent type theory.In Seventh Symposium on Trends in Functional Programming, 2006.

[28] J. Smith and D. Stotts. Elemental design patterns: A link betweenarchitecture and object semantics, 2002. citeseer.ist.psu.edu/smith02elemental.html.

[29] Joost Visser. Visitor combination and traversal control. In OOPSLA’01: Proceedings of the 16th ACM SIGPLAN conference on Objectoriented programming, systems, languages, and applications, pages270–282, New York, NY, USA, 2001. ACM Press.

[30] Peter Wegner. Concepts and paradigms of object-oriented program-ming. SIGPLAN OOPS Mess., 1(1):7–87, 1990.