A Functional Pattern System for Object-Oriented Design

346
A Functional Pattern System for Object-Oriented Design Thomas K ¨ uhne

Transcript of A Functional Pattern System for Object-Oriented Design

Page 1: A Functional Pattern System for Object-Oriented Design

A Functional Pattern System forObject-Oriented Design

Thomas Kuhne

Page 2: A Functional Pattern System for Object-Oriented Design

♦A child

does not dis-cover the world by

learning abstract rules.Instead it learns by looking

at concrete examples. An examplecontains the rules as well. In contrast to

rules, the recognition of examples can be basedon tangible reality. The knowledge extracted from

an example serves as a Pattern that is used to rememberfacts and to construct new solutions. When grown-ups

are about to learn something or have to applyunknown tools, they are put into a child’s

position again. They will favor concreteexamples over abstract rules. The

rules will happily be gen-erated automatically,

for this is howthe brain

works.♦

Page 3: A Functional Pattern System for Object-Oriented Design

Thesis iii

Thesis

Design patterns inspired by functional programming concepts can advance object-oriented design.

Problem

The object-oriented paradigm has undoubtfully raised our ability to design andmaintain large complex software systems. However, it does not seem to have meetthe high expectations concerning reuse and ease of evolution which have been pro-moted ever since its commercial success.

There are many potential reasons for the above observation such as unquali-fied staff, immature languages, inadequate methodologies, inappropriate businessprocesses, etc.

The view presented here is that although the object-oriented paradigm is a pow-erful basis, it is incomplete in its inherent concepts and therefore restricts the designspace to inappropriate solutions. It is assumed that both software development andlanguage design are restrained from achieving their full potential when restrictedto a purely object-oriented world view.

Solution

Since the complementary paradigm to object-orientation is represented by func-tional programming, I investigate high-level, well-known to work functional con-cepts and examine their suitability to enhance object-oriented design. I explore thesoftware engineering relevance of each concept and present its intent, applicability,implementation, and consequences in the literate form of a design pattern.

My approach clearly motivates functional techniques for object-oriented designfrom a software engineering point of view. This is different to the usual procedureof designing a new language with an “ad-hoc” conglomeration of functional andobject-oriented features. The latter case requires excellence in language design andmakes it hard to find out and evaluate uses of the new language.

In contrast, design patterns are already widely used to improve design. As func-tional concepts constitute a powerful paradigm by themselves, it is more than sug-gestive to assume that design patterns expressing successful functional conceptswill enhance the object-oriented paradigm with new capabilities.

Page 4: A Functional Pattern System for Object-Oriented Design

iv Thesis

Contribution

Feasibility

I demonstrate the feasibility of using functional techniques in object-oriented de-signs which are to be implemented by ordinary object-oriented programming lan-guages. This is done at the level of a calculus comparison and in concrete designpattern implementation descriptions. I demonstrate synergetic effects caused byconcept integration, which together with the advantages of functional patterns,thus, show the utility of the approach.

Software production

Object-oriented practitioners hopefully will use the names of functional design pat-terns as a vocabulary to discuss solutions in a new design space. I present a systemof patterns which are connected by relations that describe how individual patternsmay interact and collaborate with each other. This system, consisting of state-of-the-art mini-architectures, may allow thinking and designing beyond restrictionsimposed by a dogmatic object-oriented approach. As a result, the quality of soft-ware is hoped to improve.

Language Design

Using functional patterns for object-oriented design can be regarded as dual-paradigm design. In this light, functional design patterns appear as language id-ioms that lift an object-oriented language to a dual paradigm implementation lan-guage.

It is very instructive to verify how well an object-oriented language supports theimplementation of these idioms, since limiting properties are expected to interferein other attempts to produce flexible and maintainable software as well.

Unless one is restricted to use a certain existing language, it is, however, onlynatural to consider direct language support in order to avoid the repetitive im-plementation of these idioms. A holistic language that encompasses both object-oriented and functional paradigms should provide more ease of use, increasedsafety, better initial execution efficiency, and higher optimization potential.

I consider each presented design pattern for its contribution to language con-structs that support a dual paradigm language. The software engineering consid-erations, contained in each design pattern description, help to avoid “featurism” infavor of conceptually founded language principles.

Ultimately, impulses initiated by the functional pattern system lead to a reeval-uation of the role distribution between a programming language and its associatedenvironment. The result allows transcending the limitations of each paradigm byproviding the optimal paradigm view on demand.

Page 5: A Functional Pattern System for Object-Oriented Design

Preface v

Preface

Example is the school of mankind, and they will learn at no other.– Edmund Burke

A number of people directly or indirectly influenced my work and I am gratefulfor their contributions.

Norbert Ihrig made my early years in school a worthwhile experience and with-out him my way would have been much harder.

Gregor Snelting proposed an interesting master thesis to me and, thus, openedup the subsequent opportunity for a research position at the institute for “Praktis-che Informatik” at the Darmstadt University of Technology. I enjoyed his lecturesand am thankful for his continued support.

Thilo Kielman introduced me to the world of being a scientist. I owe him manyhints, paper reviews, and enjoyable hours.

Alberto Pardo was my roommate over a long period and shared many stimulat-ing discussions with me. I would like to express my gratitude for his ever presentwillingness to help and for a lot of advice in the areas of calculi and algebras. Heand Nick Dyson helped to improve the chapter on functional programming.

John Sargeant invested considerable effort to discuss the Void Value patternwith me and provided useful information on UFO.

Dirk Koschorek provided invaluable last minute advice on SMALLTALK.Many scientists, who I had the honor to meet at workshops or conferences, dis-

cussed aspects of my work with me and made me reflect about it.Gillian L. Lovegrove and Jurgen Ebert accepted to be external examiners and

I am indebted to their timely efforts. I would like to thank Jurgen Ebert for hisinvitation to the “Bad Honnef” workshop series and for his personal interest in mywork.

Finally, I would like to express my gratitude to my thesis supervisor WolfgangHenhapl. He gave me all the freedom I could wish for and stimulated my work onseveral occasions. He asked the question to which function objects present an an-swer and challenged me to conceive the Translator design. Numerous discussionswith him created an abundance of insights for me.

I am honestly grateful to the nameless cosmic particles that hit me when I hadthe idea of function objects, to replace Nil with a type specific value, to solve theforces of internal and external iteration with multiple consumable intermediatestreams, to express functional ideas by devising a pattern system, and to rethinkthe roles of languages and their environments based on the notion of tiles.

Page 6: A Functional Pattern System for Object-Oriented Design

vi Preface

Many thanks to Cordon Art for granting permission to use the images by M.C.Escher “Circle Limit IV”, “Space Filling”, and “Day and Night”. 1998, CordonArt B.V., Nieuwstraat 6, P.O.Box 101, 3740 AC Baarn, Holland.

Last but not least, my appreciation goes to to Donald E. Knuth who developedTEX and put it into the public domain, Frank Mittelbach for LATEX 2ε, Linus Torvaldfor LINUX, and all their companions and successors who enabled me to producethis document with copylefted software only.

Page 7: A Functional Pattern System for Object-Oriented Design

Contents vii

Contents

Thesis iii

Preface v

Prologue 1

I Foundation 7

1 Functional programming 91.1 Worldview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91.2 Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

1.2.1 Functional decomposition . . . . . . . . . . . . . . . . . . . . 101.2.2 Reduction Semantics . . . . . . . . . . . . . . . . . . . . . . . 111.2.3 Higher-Order Functions . . . . . . . . . . . . . . . . . . . . . 121.2.4 Lazy evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . 141.2.5 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . 161.2.6 Type inference . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

1.3 Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171.3.1 Pro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

1.3.1.1 Programming Discipline . . . . . . . . . . . . . . . 181.3.1.2 Concise programs . . . . . . . . . . . . . . . . . . . 18

1.3.2 Contra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.3.2.1 Functional decomposition . . . . . . . . . . . . . . . 211.3.2.2 Reduction semantics . . . . . . . . . . . . . . . . . . 211.3.2.3 Deceptive Clearness . . . . . . . . . . . . . . . . . . 26

2 Object-orientation 292.1 Worldview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292.2 Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

2.2.1 Object-oriented decomposition . . . . . . . . . . . . . . . . . 322.2.2 Encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . 322.2.3 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332.2.4 Subtyping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342.2.5 Dynamic Binding . . . . . . . . . . . . . . . . . . . . . . . . . 35

Page 8: A Functional Pattern System for Object-Oriented Design

viii Contents

2.2.6 Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372.3 Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

2.3.1 Pro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382.3.1.1 Real world modeling . . . . . . . . . . . . . . . . . 382.3.1.2 Maintainability . . . . . . . . . . . . . . . . . . . . . 392.3.1.3 Beyond the imperative . . . . . . . . . . . . . . . . 40

2.3.2 Contra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412.3.2.1 Imperative heritage . . . . . . . . . . . . . . . . . . 412.3.2.2 Classes for everything . . . . . . . . . . . . . . . . . 422.3.2.3 Inheritance for everything . . . . . . . . . . . . . . 42

3 Calculus comparison 453.1 Language comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . 453.2 Opulus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463.3 Opulus within λ-calculus . . . . . . . . . . . . . . . . . . . . . . . . . 473.4 λ-calculus within Opulus . . . . . . . . . . . . . . . . . . . . . . . . . 493.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

4 Conflict & Cohabitance 554.1 Conflict . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

4.1.1 Oppositions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554.1.1.1 Semantic model . . . . . . . . . . . . . . . . . . . . 554.1.1.2 Decomposition . . . . . . . . . . . . . . . . . . . . . 564.1.1.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . 564.1.1.4 Encapsulation . . . . . . . . . . . . . . . . . . . . . 56

4.1.2 Redundancy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574.1.2.1 Parameterization . . . . . . . . . . . . . . . . . . . . 574.1.2.2 Dispatch . . . . . . . . . . . . . . . . . . . . . . . . . 57

4.2 Cohabitance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574.2.1 Subsumption . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

4.2.1.1 Pure functions . . . . . . . . . . . . . . . . . . . . . 574.2.1.2 Dispatch . . . . . . . . . . . . . . . . . . . . . . . . . 58

4.2.2 Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594.2.2.1 Evaluation . . . . . . . . . . . . . . . . . . . . . . . 594.2.2.2 Closures . . . . . . . . . . . . . . . . . . . . . . . . . 59

4.2.3 Synergy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594.2.3.1 Parameterization . . . . . . . . . . . . . . . . . . . . 604.2.3.2 Decomposition . . . . . . . . . . . . . . . . . . . . . 64

4.3 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

5 Design Patterns 695.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

5.1.1 A gentle introduction . . . . . . . . . . . . . . . . . . . . . . . 695.1.2 Software Patterns . . . . . . . . . . . . . . . . . . . . . . . . . 72

5.2 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

Page 9: A Functional Pattern System for Object-Oriented Design

Contents ix

5.3 Promise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 765.4 Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 765.5 Diagram notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

5.5.1 Class diagram notation . . . . . . . . . . . . . . . . . . . . . . 805.5.2 Object diagram notation . . . . . . . . . . . . . . . . . . . . . 805.5.3 Interaction diagram notation . . . . . . . . . . . . . . . . . . 81

II Pattern System 83

6 Catalog 856.1 Proto-Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856.2 Pattern System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

6.2.1 Function Object . . . . . . . . . . . . . . . . . . . . . . . . . . 866.2.2 Lazy Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876.2.3 Value Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876.2.4 Transfold . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886.2.5 Void Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886.2.6 Translator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

6.3 Why Eiffel? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

7 Function Object 937.1 Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937.2 Also Known As . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937.3 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

7.3.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937.3.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

7.4 Applicability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 987.5 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1007.6 Participants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1007.7 Collaborations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017.8 Consequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1027.9 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057.10 Function Object variants . . . . . . . . . . . . . . . . . . . . . . . . . . 1077.11 Known Uses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107.12 Related Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

7.12.1 Categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . 1117.12.2 Collaboration . . . . . . . . . . . . . . . . . . . . . . . . . . . 1127.12.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 113

8 Lazy Object 1158.1 Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1158.2 Also Known As . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1158.3 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

8.3.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

Page 10: A Functional Pattern System for Object-Oriented Design

x Contents

8.3.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1168.4 Applicability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1198.5 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218.6 Participants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218.7 Collaborations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1228.8 Consequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1228.9 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1278.10 Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1308.11 Known Uses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1438.12 Related Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

8.12.1 Categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . 1458.12.2 Collaboration . . . . . . . . . . . . . . . . . . . . . . . . . . . 1458.12.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 146

9 Value Object 1499.1 Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1499.2 Also Known As . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1499.3 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

9.3.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1499.3.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

9.4 Applicability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1549.5 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1549.6 Participants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559.7 Collaborations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559.8 Consequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559.9 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1589.10 Known Uses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609.11 Related Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

9.11.1 Categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . 1619.11.2 Collaboration . . . . . . . . . . . . . . . . . . . . . . . . . . . 1619.11.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 162

10 Transfold 16310.1 Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16310.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

10.2.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16310.2.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

10.3 Applicability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17310.4 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17410.5 Participants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17510.6 Collaborations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17510.7 Consequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17610.8 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18010.9 Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18210.10 Known Uses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

Page 11: A Functional Pattern System for Object-Oriented Design

Contents xi

10.11 Related Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18810.11.1 Categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . 18810.11.2 Collaboration . . . . . . . . . . . . . . . . . . . . . . . . . . . 18810.11.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 188

11 Void Value 19111.1 Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19111.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191

11.2.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19111.2.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

11.3 Applicability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19411.4 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19511.5 Participants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19511.6 Collaborations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19611.7 Consequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19611.8 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19711.9 Known Uses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19911.10 Related Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199

11.10.1 Categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . 19911.10.2 Collaboration . . . . . . . . . . . . . . . . . . . . . . . . . . . 19911.10.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 200

12 Translator 20112.1 Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20112.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

12.2.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20212.2.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

12.3 Applicability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20412.4 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20612.5 Participants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20712.6 Collaborations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20812.7 Consequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20912.8 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21312.9 Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21412.10 Related Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217

12.10.1 Categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . 21712.10.2 Collaboration . . . . . . . . . . . . . . . . . . . . . . . . . . . 21812.10.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 219

13 Collaboration 22113.1 Example collaboration . . . . . . . . . . . . . . . . . . . . . . . . . . . 227

Page 12: A Functional Pattern System for Object-Oriented Design

xii Contents

III Language design 231

14 Pattern Driven Language Design 23314.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23314.2 Function Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23314.3 Translator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23814.4 Transfold . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24014.5 Value Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24314.6 Lazy Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24614.7 Void Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24814.8 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252

Epilogue 261

Bibliography 275

Index 305

Page 13: A Functional Pattern System for Object-Oriented Design

List of Figures xiii

List of Figures

1.1 Functional decomposition . . . . . . . . . . . . . . . . . . . . . . . . . 101.2 Comparison between imperative and functional style . . . . . . . . . 191.3 Functional and imperative LIFE . . . . . . . . . . . . . . . . . . . . . 251.4 I/O feedback model . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

2.1 Object-oriented taxonomy . . . . . . . . . . . . . . . . . . . . . . . . . 302.2 Ideal inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312.3 Classes as versions and variants . . . . . . . . . . . . . . . . . . . . . 332.4 Subtyping inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . 342.5 Dynamic method lookup . . . . . . . . . . . . . . . . . . . . . . . . . 36

3.1 Screenshot: OPULUS expression . . . . . . . . . . . . . . . . . . . . . 493.2 Screenshot: OPULUS expression represented in the λ-calculus . . . . 493.3 Screenshot: λ-calculus expression . . . . . . . . . . . . . . . . . . . . 513.4 Screenshot: λ-calculus expression represented in OPULUS . . . . . . 51

4.1 Classification of programming paradigms . . . . . . . . . . . . . . . 564.2 Cooking with object responsibility . . . . . . . . . . . . . . . . . . . . 604.3 Cooking with single inheritance . . . . . . . . . . . . . . . . . . . . . 614.4 Cooking with multiple inheritance . . . . . . . . . . . . . . . . . . . . 614.5 Cooking with repeated inheritance . . . . . . . . . . . . . . . . . . . . 624.6 Cooking with parameterization . . . . . . . . . . . . . . . . . . . . . 634.7 Cooking with synergy . . . . . . . . . . . . . . . . . . . . . . . . . . . 634.8 Restructured dependencies . . . . . . . . . . . . . . . . . . . . . . . . 67

5.1 A composer’s pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . 705.2 Space filling pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735.3 Software system of interwoven patterns. . . . . . . . . . . . . . . . . 775.4 OMT Notation: Class diagram . . . . . . . . . . . . . . . . . . . . . . 805.5 OMT Notation: Object diagram . . . . . . . . . . . . . . . . . . . . . 805.6 OMT Notation: Interaction diagram . . . . . . . . . . . . . . . . . . . 81

7.1 Function Object structure diagram . . . . . . . . . . . . . . . . . . . . 1017.2 Function Object interaction diagram . . . . . . . . . . . . . . . . . . . 102

8.1 Schematic CD-player . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1168.2 Plug-in modules in a bitstream architecture . . . . . . . . . . . . . . . 116

Page 14: A Functional Pattern System for Object-Oriented Design

xiv List of Figures

8.3 Generating hamming numbers . . . . . . . . . . . . . . . . . . . . . . 1188.4 Structure diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218.5 Interaction diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1228.6 Definition of an Anamorphism . . . . . . . . . . . . . . . . . . . . . . 1288.7 Natural number stream . . . . . . . . . . . . . . . . . . . . . . . . . . 1308.8 Using streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1318.9 Suspension of a stream tail . . . . . . . . . . . . . . . . . . . . . . . . 1348.10 Streamfunction unfolding . . . . . . . . . . . . . . . . . . . . . . . . . 1358.11 Streamvalue insertion . . . . . . . . . . . . . . . . . . . . . . . . . . . 1368.12 Stream of primes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1388.13 Unfolded primes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1398.14 Expanding the sieve . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1418.15 Normalized sieve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1428.16 Sample structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1478.17 Stream structure diagram . . . . . . . . . . . . . . . . . . . . . . . . . 148

9.1 Providing a “const” interface . . . . . . . . . . . . . . . . . . . . . . . 1519.2 Structure diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1549.3 Interaction diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559.4 Optional mutators for Value Object . . . . . . . . . . . . . . . . . . . 1589.5 Copy strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

10.1 Code example “Goodbye Mariner” . . . . . . . . . . . . . . . . . . . 16610.2 Iteration topology with lazy streams and Transfold . . . . . . . . . . 16810.3 Iteration of two aggregates in lock-step . . . . . . . . . . . . . . . . . 16910.4 Transposing lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17010.5 Computing a list of products . . . . . . . . . . . . . . . . . . . . . . . 17010.6 Transmapping equality . . . . . . . . . . . . . . . . . . . . . . . . . . 17110.7 Transfolding the inner product of a matrix . . . . . . . . . . . . . . . 17210.8 Minor axis matrix transposition . . . . . . . . . . . . . . . . . . . . . 17310.9 Transformation chain . . . . . . . . . . . . . . . . . . . . . . . . . . . 17310.10 Structure diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17410.11 Interaction diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17610.12 Inner product application . . . . . . . . . . . . . . . . . . . . . . . . . 18210.13 Matrix multiplication with a magic square . . . . . . . . . . . . . . . 186

11.1 Code example: Robust behavior . . . . . . . . . . . . . . . . . . . . . 19211.2 Empty tree as void value . . . . . . . . . . . . . . . . . . . . . . . . . 19211.3 Code example: Null iterator . . . . . . . . . . . . . . . . . . . . . . . 19311.4 Code example: Default Behavior . . . . . . . . . . . . . . . . . . . . . 19311.5 Code example: Base case definition . . . . . . . . . . . . . . . . . . . 19311.6 Code example: Case distribution . . . . . . . . . . . . . . . . . . . . . 19411.7 Void Value structure diagram . . . . . . . . . . . . . . . . . . . . . . 19511.8 Automatic reference initialization . . . . . . . . . . . . . . . . . . . . 198

12.1 Homomorphic translations of trees . . . . . . . . . . . . . . . . . . . 202

Page 15: A Functional Pattern System for Object-Oriented Design

List of Figures xv

12.2 Generic interpretation on an abstract syntax tree . . . . . . . . . . . 20412.3 Sample structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20512.4 Structure diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20612.5 Interaction diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20812.6 Distinct interpretation phases . . . . . . . . . . . . . . . . . . . . . . . 20912.7 Splitting the interpretation . . . . . . . . . . . . . . . . . . . . . . . . 21012.8 Non-interfering semantics . . . . . . . . . . . . . . . . . . . . . . . . . 211

13.1 Pattern categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . 22213.2 Pattern implementation relations . . . . . . . . . . . . . . . . . . . . . 22313.3 Pattern collaborations . . . . . . . . . . . . . . . . . . . . . . . . . . . 22413.4 Pattern system relations . . . . . . . . . . . . . . . . . . . . . . . . . . 22613.5 Functional pattern decomposition of “Samefringe” . . . . . . . . . . 229

14.1 Code example: Initialization. . . . . . . . . . . . . . . . . . . . . . . . 25114.2 Hierarchical tiles editing . . . . . . . . . . . . . . . . . . . . . . . . . . 258

E.1 Pattern Cathedral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265E.2 Paradigm integration with patterns . . . . . . . . . . . . . . . . . . . 269

Page 16: A Functional Pattern System for Object-Oriented Design

xvi List of Figures

Page 17: A Functional Pattern System for Object-Oriented Design

List of Tables xvii

List of Tables

3.1 Opulus syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463.2 Opulus BNF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473.3 Translation of Opulus to λ-calculus . . . . . . . . . . . . . . . . . . . 483.4 Translation of λ-calculus to Opulus . . . . . . . . . . . . . . . . . . . 503.5 Complexity of Opulus in the λ-calculus . . . . . . . . . . . . . . . . . 523.6 Complexity of λ-calculus in Opulus . . . . . . . . . . . . . . . . . . . 52

4.1 Criteria to evaluate cook and recipe combination . . . . . . . . . . . 604.2 Decomposition matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . 654.3 Sensitivity of decomposition types . . . . . . . . . . . . . . . . . . . . 65

5.1 Facets of “pattern” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715.2 Promise of design patterns . . . . . . . . . . . . . . . . . . . . . . . . 76

6.1 Functional pattern spectrum . . . . . . . . . . . . . . . . . . . . . . . 906.2 Language levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916.3 Functional pattern system . . . . . . . . . . . . . . . . . . . . . . . . . 92

10.1 External versus internal iteration . . . . . . . . . . . . . . . . . . . . . 16710.2 Transfold’s arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . 171

12.1 Simplifications possible while translating . . . . . . . . . . . . . . . . 211

13.1 Benefits of the functional pattern design example . . . . . . . . . . . 228

14.1 Pattern implications on language design . . . . . . . . . . . . . . . . 25214.2 Tiles with object-oriented and functional dimensions . . . . . . . . . 25614.3 Concrete types with implementation and interface views . . . . . . . 257

Page 18: A Functional Pattern System for Object-Oriented Design

xviii List of Tables

Page 19: A Functional Pattern System for Object-Oriented Design

Prologue 1

Prologue

A paradigm is not a dogma.– me

Paradigms define ways to perceive the world. Is a cannery a hierarchi-cal structure of functions defined on passive data, or a set of interactingactive objects, or a web of concurrent processes? In analogy to theories,

the value of a paradigm is determined by the usefulness of creating models accord-ing to it. A paradigm, therefore, is a means to an end. In software developmentthe goal is to avoid semantic gaps inbetween analysis, design, and implementationwhile achieving reusable components, extendable applications, and maintainablesoftware.

Motivation

During its now three decades spanning history, the object-oriented paradigm hasproved to be tremendously useful as a basis for development methodologies and asan implementation strategy. Object-oriented languages are truly general purposelanguages since they may conquer any domain by defining the appropriate abstrac-tions. Domain specific libraries or frameworks enable designers to define and useapplication languages, specifically tailored to model particular domains [Hudak96].Application languages avoid an impedance mismatch between analysis and imple-mentation. Object-oriented languages to a large extent allow a natural definition ofapplication languages with the help of class abstractions. They, hence, enable aso-called middle-out development [Ward94], where one team implements the appli-cation language while another builds the application on top of it. It appears to bealmost certain that object-orientation will not cease as a dead end in the evolutionof programming and design paradigms.

However, there are signs of a disintegrating kingdom. The fact that thenext Kuhnian paradigm shift [Kuhn70] is overdue [Quibeldey-Cirkel94] is nota strong indication, but in 1994 an issue of BYTE magazine devoted to compo-nent software shook the object-oriented community by stating that object-orientedtechnology failed to keep its promises and that VISUAL BASIC-like, componentoriented languages offer higher productivity [Byte94]. Subsequently, the func-tional paradigm scored higher in analysis suitability [Harrison et al.94] and thesupremacy of the object-oriented paradigm was found to be “an open research

Page 20: A Functional Pattern System for Object-Oriented Design

2 Prologue

issue” [Menzies & Haynes95]. Further competition appeared promoting “Subject-oriented programming” [Harrison & Ossher93, ObjectExpert97].

It is not my intent to refute alternative approaches to be futile, but this thesismakes a definite statement that first, object-orientation is still well in the game andsecond, should be the basis for an evolution rather than a revolution. But why didobject-orientation not live up to its promises? A possible answer is given the theAugust 1995 issue of IEEE COMPUTER:

“Poor design is a major culprit in the software crisis.” – Bruce W. Weide

Or, more elaborate:

“But simply using an object-oriented programming language or environ-ment does not, in itself, guarantee miraculous results. Like any other humanendeavor, software design is an art: discipline, hard work, inspiration, andsound technique all play their parts. Object-oriented technology has much tooffer, certainly. But how may it best be exploited? [Wirfs-Brock et al.90]”

– Rebecca Wirfs-Brock et al.

This question is still unanswered as evidenced by the 1997 call for papers of theOOPSLA ’97 workshop “Object-Oriented Design Quality”:

“Despite the burst in the availability of OO analysis and design methodolo-gies, languages, database management systems, and tools, relatively little workhas been done in the area of OO design quality assurance, assessment, andimprovement. We badly need a better understanding of the properties of OOsystem design, in the small and in the large, and their effect on quality factorssuch as maintainability, evolvability, and reusability.” – Rudolf K. Keller

Design patterns [Gamma et al.94], i.e., the documentation of successful mini-architectures, are hoped to be part of the solution to the persisting design problem.They can be used to hand down design excellence from experts to novices. Indeed,pattern books may contribute to make software engineering a true engineering sci-ence by representing engineering handbooks containing known to work solutions.In fact, patterns can make software more reusable, allow reusing of design, aid indesigner communication, and can be used in teaching good design.

Paradigm integration

While object-orientation and design patterns — the buzzwords of the eighties andnineties respectively — receive a great deal of attention, it got rather quiet arounda field which has mostly been of academic interest only: Functional programmingis known for its elegance but also has the reputation of being an academic toy only.Quite often the desirable features of functional programming are believed to beinaccessible from other programming language types.

Page 21: A Functional Pattern System for Object-Oriented Design

Prologue 3

“Functional programming can not, apart from modest attempts, be appliedwith algorithmic programming languages in common use1 [Schneider91].”

– H. J. Hoffmann

Especially higher-order functions, i.e., the ability to support closures, are a desir-able feature, believed to be absent in programming languages like C++, EIFFEL,and so on [Baker93, Kofler93, Gamma et al.94].

Continuing attempts to explain potential benefits of functional program-ming [Hughes87], this thesis uses the literate form of design patterns to capturethe benefits of functional concepts and idioms. It, therefore, demonstrates the fea-sibility of supporting a functional programming style in the above mentioned lan-guages. Of course, it is not possible to turn stateful languages into pure functionalprogramming languages. This is not intended, however.

“This discussion suggests that what is important is the functional program-ming style, in which the above features are manifest and in which side effectsare strongly discouraged but not necessarily eliminated. [Hudak89].”

– Paul Hudak

Although some of the functional patterns to be presented introduce restrictions togain safety, they are intended to be additions rather than constraints to existingobject-oriented practices.

Prior to the formulation of patterns an analysis of the two paradigms is per-formed with the view to embed functional programming into the object-orientedparadigm. This is, as such, a valuable endevour.

“We need to find, generalize, and integrate similarities in programminglanguages. Otherwise, there will be a proliferation of concepts which nobodywill be able to overview and understand [Reynolds96].” – John C. Reynolds

In addition to that, the world of object-oriented design is enriched with the func-tional paradigm. The result is a multi-paradigm design space. The author is ingood company in his belief that multi-paradigm programming will play a crucialrole in the future:

“. . . mixed language working — “integration of programming paradigms”is an important theme [Hankin et al.97].” – Chris Hankin et al.

A multi-paradigm toolkit does not only represent an enhanced choice of tools butalso enhances the tool user.

“Research results from the psychology of programming [Petre89] indicatethat expertise in programming is far more strongly related to the number of dif-ferent programming styles understood by an individual that it is to the numberyears experience in programming [Budd95].” – Timothy Budd

1Translated from German.

Page 22: A Functional Pattern System for Object-Oriented Design

4 Prologue

This can be explained by the fact that the available notions of a particular program-ming language restrict the solution space of a designer who is familiar with thislanguage only.

“Wir mussen den starken und unbestreitbaren Einfluß unserer Sprache aufdie Art unseres Denkens erkennen. Mit ihr wird in der Tat der abstrakte Raumdefiniert und begrenzt, in dem wir unsere Gedanken formulieren, ihnen eineForm geben.” – Nicklaus Wirth

In the context of this thesis it is appropriate to interpret the term “Sprache” (lan-guage) as “paradigm”. The single paradigm possibly implied by even a groupof programming languages, thus, restricts designers that are familiar with thisparadigm only. The introduction of functional patterns, hence, enlarges the de-sign space of formerly pure object-oriented designers. Interestingly, this is possiblewithout requiring them to learn a new language, since the functional patterns areexpressed with well-known object-oriented concepts.

The attempt to capture functional concepts as patterns, evaluate their softwareengineering properties and then document them as parts of a engineering hand-book for design, is in tune with the primal definition of science.

“It is my aim to first enumerate experience and then to proof with reasonwhy it must act in this way.” – Leonardo da Vinci

Design patterns can be a blessing for design practitioners but they are alsooften testimony to the weaknesses of implementation languages or underlyingparadigms [Baumgartner et al.96, Seiter et al.96, Norvig96]. A further lesson to belearned from the functional pattern system is, therefore, how to better support it infuture languages.

Hints on reading

The “Newsreader” approach to this thesis is to read chapter “Thesis” on page iiionly. A more comprehensive quicktour is obtained by adding the prologue onpage 1 and epilogue on page 261. Anybody interested in a glance at the patternsmay take a pattern quicktour by reading chapters catalog on page 85 and collab-oration on page 221. Design practitioners, however, will savor the whole patternsystem part starting on page 85.

Chapters of interest to language designers are the calculus comparison onpage 45, the analysis of paradigm integration on page 55, and the whole languagedesign part beginning on page 233.

Chapters functional programming on page 9, object-orientation on page 29, anddesign patterns on page 69 are meant as an introduction to the uninitiated but alsoserve to establish and clarify terminology.

Page 23: A Functional Pattern System for Object-Oriented Design

Prologue 5

Thesis

Prologue

FOUNDATION

Object-Orientation

Calculus Comparison

Design Patterns

PATTERN SYSTEM

Catalog

Function Object

Lazy Object

Value Object

Transfold

Void Value

Translator

Collaboration

LANGUAGE DESIGN

Epilogue

Conflict & Cohabitance

Functional Programming

Newsreader

Design Practitioner

Lang

uage

Des

igne

r

Qui

ckto

ur

Pat

tern

Qui

ckto

ur

Page 24: A Functional Pattern System for Object-Oriented Design

6 Prologue

Page 25: A Functional Pattern System for Object-Oriented Design

7

Part I

Foundation

Page 26: A Functional Pattern System for Object-Oriented Design

8

Page 27: A Functional Pattern System for Object-Oriented Design

9

1 Functional programming

This chapter introduces functional programming and defines some termsthat are used in later discussions. I explain the underlying worldview(section 1.1) and the most important concepts (section 1.2 on the next

page). Finally, section 1.3 on page 17 enumerates pros and cons of functional pro-gramming.

1.1 Worldview

The functional paradigm suggests to regard everything as an expression. Thiscan be seen as the logical conclusion from FORTRAN (mathematical flavorthrough expressions), to functions in PASCAL, to expression oriented style inSCHEME [Hudak89]. Since the value of mathematical expressions does not changeover time, there is no need for any notion of state. Also, there is no need for controlflow, because the order of expression evaluation has no impact on the final result(as long as partiality is excluded).

The most important type of expression is called function application, i.e., somedata is fed into a function and the function produces a result. We might say “Thefunctional paradigm suggests to regard everything as a function” and loose only alittle bit of truth. In fact, it is possible to model everything with functions only (e.g.,with the pure λ-calculus [Barendregt84, Hankin94]), but functional programminglanguages typically provide built-in primitive data structures and operations.

Apparently, functional programming corresponds to two outdated softwareparadigms: On a small scale the transformation of data with functions correspondsto the Input/Processing/Output model [Pepper92], which has been replaced bymore modern views such as distributed computation and event-triggered behav-ior. On a large scale the structuring of a program into a hierarchy of functionscorresponds to structured analysis and design, which has been replaced by entity-relationship or object-oriented decomposition techniques.

The above should not create the impression that functional programming isof no use anymore. On the contrary, we will see that data transformation andfunctional decomposition are still very useful concepts. Also, apart from the on-going academic interest in functional programming languages (e.g., [Hudak96,Gostanza et al.96, Laufer96]) the rise of new parallel computer architectures addsimportance to languages with a strong potential for parallel execution models.

Page 28: A Functional Pattern System for Object-Oriented Design

10 1 Functional programming

1.2 Concepts

The following sections are meant to briefly introduce concepts in functional pro-gramming. For a more thorough discussion and also for a history of functionalprogramming languages the reader is referred to [Ebert87] and [Hudak89]. Somereaders may miss the concepts of polymorphism, strong static typing, data abstrac-tion, and garbage collection. These are not discussed since they can be regarded asorthogonal to language paradigms. Equally, all these concepts have been adoptedby both functional and object-oriented languages. Type inference is not paradigmspecific either but has not been successfully applied to object-oriented languagesyet and therefore will be a stimulus for the extension of object-oriented languages(see part III starting at page 233).

Subsequently, I will use the syntax of the functional language HASKELL to de-pict functional concepts. The notation is very intuitive, but you may want to referto an introduction like [Hudak & Fasel92].

1.2.1 Functional decomposition

I already mentioned the correspondence between functional decomposition andstructured analysis and design. A program is viewed as a main function, that isdecomposed into more functions with less work to accomplish (see figure 1.1).

Main Function

Decomposition FunctionDecomposition Function

Decomposition FunctionLibrary Function

Operations Operations

Figure 1.1: Functional decomposition

By functional decomposition I also include the separation between functionsand data, i.e., the tools and materials metaphor. New functionality typically doesnot require to change existing data structures. It is simply added with externalfunctions operating on top of existing data structures. However, changes to datastructures often require to change existing functions. As functions depend on data,they must be adapted to properly react to, e.g., new constructors. See [Cook90] fora technical and Chapter 12 of [Meyer88] for a software engineering discussion offunctional versus object-oriented decomposition.

Page 29: A Functional Pattern System for Object-Oriented Design

1.2 Concepts 11

1.2.2 Reduction Semantics

Computations within a functional programming language are performed just likereductions in a calculus. Instead of altering an implicit state with instructions, com-putation is defined as the reduction of expressions. Intuitively, we can think of areduction step as a replacement of a term with a simpler term1. For instance:

1+2+4 =⇒ 3+4 =⇒ 7

Reduction does not always mean simplification in a naıve sense, i.e., expressionsmay as well grow in size in intermediate steps:

(1+2)2 =⇒ (1+2)∗ (1+2) =⇒ 3∗3 =⇒ 9 (1.1)

Yet, reduction is guaranteed to produce a unique so-called normal form, if it ter-minates at all2. An expression in normal form cannot be reduced any further andis what we intuitively think of as the value of an expression. Uniqueness of nor-mal forms for the untyped λ-calculus is guaranteed by the Church-Rosser TheoremI [Hudak89].

In addition, the normal form may be reached through every possible reductionorder, e.g.,

(1+2)2 =⇒ (3)2 =⇒ (3)∗ (3) =⇒ 9 (1.2)

yields the same result as the above calculation. We have to be more precise if errors(e.g., division by zero) or non-termination may also occur. For instance,

False∧ (1/0 = 1) =⇒ False

but only if logical conjunction (∧) is non-strict, that is, does not evaluate its secondargument unless necessary. Here, the reduction order used in reduction 1.2 calledapplicative-order reduction3 would not produce the normal form “False”, but pro-duce a division by zero error.

Fortunately, normal-order reduction4, the strategy used in reduction 1.1, alwaysyields the normal form, if it exists. This is stated by the Church-Rosser Theorem IIfor the untyped λ-calculus [Hudak89].

Reduction results, as well as any other expression, are immutable values.Adding an element to a list produces a new list copy with that element appended.Furthermore, the result is not distinguishable from a second list with identical el-ement order that was obtained in a different way (e.g., by removing an element),that is, values have no identity. This is a remarkable property. To see why, considerthe following (non-HASKELL) code:

1Though this view is a crude oversimplification it suffices for our purposes here.2Termination is not guaranteed in general. However, there are special systems like the simply

typed λ-calculus, the polymorphic typed λ-calculus, system F, and languages like FP that evenguarantee termination.

3Evaluate arguments first; then apply function (just like call-by-value parameter passing).4Evaluate function first; then arguments (akin to call-by-name parameter passing).

Page 30: A Functional Pattern System for Object-Oriented Design

12 1 Functional programming

x := 1+6y := 2+5

Here are some questions (adapted from [Appel93]):

• Is x the same 7 as y?

• If we modify x does y change?

• Do we need a copy of 7 to implement z := x?

• When we no longer need x how do we dispose of the 7?

You rightfully consider these questions as silly but what about posing them againin a different context?

x := aList.add(anItem)y := aList.add(anItem)

We can conclude that numbers have value semantics5 and many “silly” questionsarise when reference semantics, i.e., references, mutable objects, and identity comeinto play.

For our purposes we may recall reduction semantics to mean:

1. No implicit state→ No side-effects→ Great freedom in reduction order.

2. No mutable objects nor identity→ Aliasing is not an issue.

1.2.3 Higher-Order Functions

Functions are first-class values often called first-class citizens. A function can be anargument to another function (downward-funarg) as well as be the result of anotherfunction (upward-funarg). An example for passing a function as an argument is themapfunction:

map f [ ] = [ ] (1.3)map f (x : xs) = ( f x) : map f xs (1.4)

It applies a function to all elements of a list. Hence,

map(+ 1) [1,2,3] =⇒ [2,3,4]

and

[”House” , ”Boat” ]map(”my” ++)6

=⇒ [”myHouse” , ”myBoat” ].

5Even in imperative languages!

Page 31: A Functional Pattern System for Object-Oriented Design

1.2 Concepts 13

Thus, downward-funargs are a perfect means to achieve behavior parameteriza-tion.

Let us look at a very basic function for an example of returning a function,namely function composition:

compose f g = λx→ f (g x)7.

From two functions f and g a new function is produced that applies g to its argu-ment and then applies f to the result of the former application. So,

7compose(+ 21) (∗ 3)

=⇒ 42,

since the result of 7 multiplied by 3 added to 21 yields 42. The composefunctionis an example for a functional, i.e., higher-order function, since it takes functionsas arguments and produces a new function. Returning functions makes it easy todefine new functions by a combination of existing functions. An interesting varia-tion of creating a new function by supplying less parameters than needed is calledpartial application and uses a curried8 function: Given

add x y = x+y

several functions can be defined by supplying only one of two parameters:

inc = add1

dec = add(−1)addTen = add10

inc 98 =⇒ 99

dec3 =⇒ 2

addTen1 =⇒ 11.

In fact, currying is possible with runtime values. We may create a functionaddXby supplying add with a number from user input. The function inc actuallyrepresents a closure. It represents function add but carries the value of x with it.Another typical way to create a closure is by capturing the value of a free (or global)variable as in

let y= 13 in

λx→ add x y.

Here we created a function that will add its argument (x) to 13. Variable y is saidto be a free variable in the scope of the λ abstraction, as its value is determined bythe function’s environment rather than by a function parameter (Functions with nofree variables are also called combinators). Closures are truly functions created at

6(+1) and (”my” ++) (append) are called sections and denote the partial application of “+” to 1and append to ”my” respectively.

7λ builds a function abstraction and binds a variable (in this case x) to the value that will bereceived by a later function application.

8Named after the logician Haskell B. Curry, who used the notation f x y to denote ( f x) y, previ-ously written as f (x,y).

Page 32: A Functional Pattern System for Object-Oriented Design

14 1 Functional programming

runtime which is also documented by the fact that they cannot be optimized bypartial evaluation [Davies & Pfenning96].

The essence of the above is that higher-order functions provide more possibil-ities than functions normally do in imperative languages. In C, for instance, onecan pass and return function pointers, but without a means to capture environmentvalues. PASCAL allows closures as downward-funargs but (for reasons of languageimplementation) not as upward-funargs. With true first-class functions, however,the modularity of programs can be enhanced significantly [Hughes87].

1.2.4 Lazy evaluation

Most programming languages have strict semantics. We already got to know thistype of evaluation strategy in section 1.2.2 on page 11 as applicative-order reductionand it is also known as call-by-value parameter passing. Yet another name for it iseager evaluation. Here is why: Given

select x y = x

we may obtain a reduction sequence like

select1 (2+3) =⇒ select1 5 =⇒ 1.

The intermediate result 5 was computed although it was subject to be thrown awayimmediately. Even worse, eagerness may not only cause unnecessary work to bedone it can also prevent the computation of meaningful results. Just imagine anon-terminating expression or (1/0) in place of (2+3). The result would be non-termination or a division-by-zero error, though the result 1 would be perfectly rea-sonable.

The solution is to use normal-order reduction, akin to call-by-name. With anon-strict selectfunction the reduction sequence becomes

select1 (2+3) =⇒ 1,

which is both faster and safer. Indeed, almost any programming language containsat least one non-strict operator/statement in order to allow recursion or choice ofside-effects. Typically, it is an if-statement that does not evaluate its branching ar-guments until one can be chosen.

Nevertheless, we already have seen an example when lazy evaluation causesmore work than eager-evaluation. If you compare reduction sequence 1.2 onpage 11 using eager evaluation to reduction sequence 1.1 you will note that it needsone addition operation less. The problem was created by duplicating an expression(by unfolding exponentiation to multiplication) which also duplicated the compu-tation amount. Alas, all we need to do is to record the duplication and to share com-putation results. Speaking in calculi terms we replace string reduction by graphreduction. Then, reduction sequence 1.1, becomes

Page 33: A Functional Pattern System for Object-Oriented Design

1.2 Concepts 15

(1+2)2 =⇒ (• ∗ •)↘↙︷ ︸︸ ︷

(1+2)

=⇒ (• ∗ •) =⇒ 9

↘↙︷︸︸︷3

causing (1+2) to be evaluated only once. The combination of normal-order reduc-tion and sharing of expression results is known as call-by-need.

With call-by-need we may not only save some computations we may even re-duce the computational complexity of algorithms. Let us obtain the minimum of alist by first sorting the list and then retrieving the first element:

min xs = hd ◦ 9sort xs

When we implement sorting with insertion-sort [Bird & Wadler88] the complexityof algorithm min with eager evaluation is O(n2), n being the number of elementsin the input list. With lazy evaluation the complexity of min is reduced to O(n),since insertion-sort finds the first element of the sorted list in O(n) time and sortingof the rest of the list does not take place, as it is thrown away by the applicationof hd. In effect, what is thrown away is a function closure that could produce therest of the list — item by item — if demanded. We can directly transfer the aboveobservation for the implementation of Kruskal’s algorithm for minimal spanningtrees. This algorithm uses a sorted list of edges to construct a minimal spanningtree of a graph [Aho & Ullmann92]. If the number of edges in the original graph islarge compared to the number of edges needed to constitute the resulting spanningtree it pays off to use lazy sorting (e.g., lazy quicksort) that just sorts as much as isrequested by the algorithm [Okasaki95a].

Besides efficiency improvements, lazy evaluation opens up the world of infinitedata structures. A definition like

ones = 1 : ones

is not possible with a strict version of cons (:)10. There would be no reasonable useof ones, in that every function accessing it would not terminate. With a lazy cons,however, the above definition provides an infinite number of ones. Only as muchones as needed are produced, e.g.,

take5 ones =⇒ [1,1,1,1,1].

We can even perform infinite transformations as in

twos = map(+1) ones

( =⇒ [2,2,2, . . .]).

A more useful definition is the list of all prime numbers:

primes = sieve[2, . . .] (1.5)sieve(p : xs) = p : sieve(filter (( 6= 0)◦ (mod p)) xs) (1.6)

9Infix notation for the composefunction of section 1.2.3 on page 12.10This operator produces a list by appending the second argument to the first.

Page 34: A Functional Pattern System for Object-Oriented Design

16 1 Functional programming

The algorithm uses the famous Sieve of Erathostenes11 and filters all non-primenumbers by testing an infinite amount of generated numbers ([2, . . .]) against allalready found prime numbers. Most importantly, it relies on an infinite supply ofnumbers to test against and produces an inexhaustible list of prime numbers. Thereis no need to speculate about the highest number a client could possibly request for.

Summarizing, we keep in mind that lazy evaluation avoids unnecessary non-termination or errors by creating a network of data dependencies and evaluatingjust that and nothing more. A considerable amount of computation can be sparedthis way to the amount of reducing the complexity of algorithms. Computationcomplexity, thus, may not depend on an algorithm’s nature, but on its usage, i.e.,which data in what order is requested. Finally, lazy constructors allow for infinitedata structures [Friedman & Wise76]. Lazy functions imply lazy data but there arelanguages like HOPE that provide lazy data only in order to avoid non-strict seman-tics for functions [Burstall et al.80]. The reason for this is to avoid closure creationfor suspended function applications and to retain an applicative order reduction,e.g., for side-effects or easier debbuging.

Anyway, lazy evaluation, similar to higher-order functions, can significantlyenhance the modularity of programs [Hughes87], as they free data generators fromknowing about the specifics of consumers. Termination control can be shifted tothe consumer as opposed to a conventional mix of generation and control.

1.2.5 Pattern Matching

Pattern matching is a typical example for syntactic sugar. Instead of testing forargument values within the function body, e.g.,

fac n = if n == 0 then1

else n∗ fac(n−1)or

fac n = case n of

n == 0→ 1

n> 0→ n∗ fac(n−1)

we may write:

fac0 = 1

fac(n+1) = (n+1)∗ fac n.

Along with the idea of using equations as part of the syntax, pattern matching addsmuch to the declarative nature of functional programs. All case discriminations areclearly separated from each other and the implicit assembly of partial function def-initions is very appealing. Note, however, besides a possibly psychological factorthere is no real software engineering significance associated with pattern matching.The expressive power of a language [Felleisen91] is not augmented with patternmatching in any way.

11Eratosthenes was a Alexandrian Greek philosopher in the third century B.C.

Page 35: A Functional Pattern System for Object-Oriented Design

1.3 Review 17

1.2.6 Type inference

Among the many innovations of ML [Milner et al.90, Wikstrom87] was a type sys-tem that liberated a programmer from explicitly declaring the types of expressions.For instance, the type system would automatically infer the type of function map(see definition 1.3 on page 12) to be

map :: (a→ b)→ [a]→ [b], (1.7)

that is, a function from type a to b and a list of elements of type a is used to producea list with elements of type b. This is an example of parametric polymorphism, thatis, a single function definition works in the same way for many types.

A strongly and statically typed language with type inference ensures the ab-sence of any runtime type errors without putting any declaration burden on theprogrammer. For each expression the most general (so-called principle) type iscomputed. With explicit type declarations a programmer might over-specify typesand thereby unintentionally narrow the use of a function. For instance, someoneprogramming functions on integer lists may type mapas

map :: (Int→ Int)→ [Int]→ [Int],

thus, excluding many other possible applications of map.There is a different view on this matter that argues that type declarations are

not just a service to compilers but constitute a part of the programs documenta-tion and even constitutes an albeit weak specification contribution. The nice thingabout type inference is that it allows a mixed style, i.e., explicit type declarationsoverrule computed types and affect the type inference of associated expressions. Ineffect, in the presence of type declarations the type system performs type checking,comparing the inferred with the declared type.

Like pattern matching there is no real impact of type inference in comparisonwith type checking on software designs. Notwithstanding, the impact of the read-ability of programs should not be underestimated. An example how type declara-tions can get in the way of an otherwise clean syntax is LEDA [Budd95].

1.3 Review

The following two sections give a subjective assessment of functional programmingfrom a software engineering point of view.

1.3.1 Pro

Pointers are like jumps, leading wildly from one part of the data structure to another.Their introduction into high-level languages has been a step backwards

from which we may never recover.– C.A.R. Hoare

Page 36: A Functional Pattern System for Object-Oriented Design

18 1 Functional programming

We already highlighted functional concepts (e.g., higher-order functions andlazy evaluation) and their ability to improve software properties (e.g., modularityof programs) in the preceding sections. Now, we turn to the foundations of func-tional programming and its positive implications.

1.3.1.1 Programming Discipline

We can think of functions as a disciplined use of gotos [Dijkstra76] and parameterbinding as a disciplined use of assignment. In this light, the absence of imperativecontrol structures and side-effects is not regarded as inhibiting expressiveness butas a discipline for good programming [Hudak89].

Referential Transparency Representing computations in terms of expressionspromotes the specification of problems and establishes data dependencies ratherthan giving an over-specified algorithm and execution order. That is why func-tional languages are considered to be declarative. Maintaining programs is facil-itated by the fact that equals may be replaced by equals, i.e., equivalent expres-sions can be interchanged without regard for introducing interferences throughside-effects.

Furthermore, the absence of side-effects makes programs much more amenableto parallelization. With regard to the multi-processor and massive parallel architec-ture designs trend in hardware there is a great demand for languages with a highpotential for parallel execution.

Finally, correctness proofs are much easier in a language with reduction seman-tics. Imperative languages require an additional calculus such as Dijkstra’s pred-icate calculus or Hoare’s weakest preconditions. Program transformations can beproven to be correct almost as easily as manipulating mathematical formulas.

1.3.1.2 Concise programs

Functional programs are typically shorter than their imperative counterparts; atleast given an appropriate problem domain which is amenable to a mathematicaldescription [Harrison et al.94].

Short Syntax The most important operation in functional programming — func-tion application — is denoted as juxtaposition, that is, it does not need any syn-tactical elements! This economy in syntax pays back in short programs. Also, thefrequent use of manifest constants reduces the amount for preliminary initializa-tions. In

take5 (filter even[1..]) (1.8)

we need five characters to denote an infinite list of integers. The creation of anew function by composing two existing functions (filter and even) solely requiresa space. There is even no need for bracketing, since function application associates

Page 37: A Functional Pattern System for Object-Oriented Design

1.3 Review 19

to the left. For the very same reason, we actually need to bracket the list argu-ment for take, i.e., this time we need a space and parentheses in order to express thesequencing of functions12.

The fact that the above short program involves the concepts of

• infinite data structures,

• lazy evaluation (by list construction, filter and take),

• higher-order functions (passing the predicate even), and

• type-safe instantiation of polymorphic functions (especially specialization oftakeand filter to integers)

clearly witnesses the expressiveness of functional programming syntax.Another useful syntactical shortcut, inspired by mathematical notations to de-

fine the contents of sets, are list comprehensions:

[x∗y | x← [6,9..],y← [1..9], odd y]

denotes a list of all products between two lists ranging from one to nine (but oddnumbers only) and from six to whatever number is reached by picking numbers insteps of three. Note that due to the nesting semantics of ‘,’ in list comprehensionsthe first list produces unique numbers only while the second recycles again andagain.

Compositionality As there is just one world of expressions in functional pro-gramming rather than two worlds of expressions and statements like in imperativeprogramming [Backus78] the composition of program parts is especially easy. Theresult of calling filter in program 1.8 on the facing page is simply passed on to takewithout a need for a communication device. Compare the program fragments infigure 1.2. The functional version much more clearly expresses the intent of the

l1.add(’a’)l2.add(’b’)l1.concat(l2);Result:=l1;

versus (′a′ : l1) ++ (′b′ : l2)

Figure 1.2: Comparison between imperative and functional style

program, does not need to pass the result via l1 , and leaves l1 and l2 intact forother uses.

During the examination of program 1.8 on the facing page we have alreadyseen that not only values but also functions may be “glued” together13. A nice

12Another way for writing the same function is (take5)◦ (filter even) [1..].13By the simple fact that functions are values too, i.e., first-class expressions.

Page 38: A Functional Pattern System for Object-Oriented Design

20 1 Functional programming

demonstration of composing programs (parsers) with higher-order functions isgiven in [Hutton92].

Yet another type of gluing is enabled by the use of lists as a paradigmatic datastructure. Many types of data structures in functional programming are modeledwith lists, e.g., sequences, sets, flattened trees, etc14. An instructive example is themodeling of a chessboard by a list of numbers in the eight queens problem: Finda configuration of eight queens on a chessboard such that no queen threatens an-other [Bird & Wadler88]. The full program incrementally produces solutions withincreasing board sizes [Wadler85], but we only look at the function that checkswhether a new queen can safely be put on column n given a board configuration p(list of taken columns).

safe p n = and[not (check(i, j)(m,n)) | (i, j)← zip([1..#p], p)] (1.9)where m= #p+1

For the reason that only column positions are stored, whereas rows are implicitlyencoded in list positions, safehas to construct one position for the new queen (col-umn n & row m) and reconstruct one position tuple for all old queens respectively.The latter part is accomplished by (zip) that joins the column positions with a gener-ated list of row positions. All thus reconstructed position tuples are checked againstthe new position, yielding a list of safeness indicators. This list of booleans is thenreduced to a single boolean result by and. The use of zip and andwas possible onlyby using a list to model column positions and using a list of safeness indicators.

Many standard functions take and produce lists and, therefore, can be oftenreused for purposes as above. The list effectively serves as a lingua franca betweenfunctions.

As a final example for the elegance of functional programming have a look atthe definition of the famous quicksort algorithm.

quicksort[ ] = [ ]quicksort(x : xs) = quicksort(filter (< x) xs)

++[x] ++quicksort(filter (>= x) xs)

The idea of the algorithm (to conquer by dividing the problem into the concatena-tion of two lists and a pivot element) almost springs to the eye of the reader. A typ-ical imperative solution (using nested while-statements to pre-sort the list in-place)almost gives no clue about the design idea.

Assuming that concise programs are faster to write the compactness of func-tional programs translates to increased programmer productivity [Ebert87].

Despite their shortness functional programs are often said to execute ineffi-ciently. However, experiments with a heavily optimizing compiler for the strict

14Lists are used as the single and universal data structure in John Backus’ FP language [Backus78].Many functions are just there to navigate values to their right position in lists.

Page 39: A Functional Pattern System for Object-Oriented Design

1.3 Review 21

functional language SISAL show that functional programs can be faster than FOR-TRAN [Cann92] and programs that require complicated and expensive storage man-agement in C may run faster in a ML implementation with a good garbage collec-tor [Clinger & Hansen92].

1.3.2 Contra

Purely applicative languages are poorly applicable.– Alan J. Perlis

The more light there is, the more shadow one might expect. Indeed, a part of thesame aspects that we mentioned in favor of functional programming can be turnedinto counterarguments from a different perspective.

1.3.2.1 Functional decomposition

Functional decomposition works fine for the tools and materials metaphor whenprogramming in the small. Also, a decomposition based on actions and trans-formations can often be more intuitive than some entity-relationship organiza-tion [Moynihan94]. On the other hand, it is difficult to assign a “main function”to any large system and any choice is likely to be invalidated some time. A func-tional decomposition is inherently instable, since the topmost functions are notgrounded in the problem domain and are subject to change whenever new re-quirements occur. Additionally, often systems are expected to cope with new datawhich demands to accommodate all affected functions. Precisely these reasons ledto the redemption of structured analysis and design by object-oriented methodolo-gies [Meyer88].

1.3.2.2 Reduction semantics

The very same feature — renunciation of state — that gives functional program-ming its strength (absence of side effects) is responsible for its main weakness (lackof updates).

“How can someone program in a language that does not have a notion ofstate? The answer, of course, is that we cannot. . . [Hudak89].” – Paul Hudak

Of course, Hudak goes on explaining that functional languages treat state explicitlyrather than implicitly. Passing around state for clarity and modeling references byindirection is fine but some serious problems remain.

Essential State

Felder sind physikalische Zustande des Raumes.– Albert Einstein

Page 40: A Functional Pattern System for Object-Oriented Design

22 1 Functional programming

We perceive the real world as consisting of objects which change over time. Realobjects have identity and state. There is a great impedance mismatch between re-ality and software solution if we have to, say, deal with copies of a pressure tankevery time pressure or temperature changes. Modeling a bank account with fair ac-cess to multiple users is a problem in functional languages [Abelson & Sussman87].In fact, the problems to model state with a feedback model [Abelson & Sussman87](see also figure 1.4 on page 28) and the prospect to incorporate functional pro-gramming into an object-oriented approach with a functional design pattern sys-tem caused the MUSE project to cancel the use of the functional programminglanguage SAMPλE [Henhapl et al.91, Deegener et al.94, Jager et al.88, Kuhnapfel93,Kuhnapfel & Große94].

Something fundamental and innocent looking as simple I/O has been a hardproblem for functional languages. Many models, such lazy streams, continuationsemantics, and systems model [Hudak & Sundaresh88, Gordon93b, Gordon93a]have been proposed to overcome the discrepancy between a referential trans-parent language and I/O side effects. Hudak gives examples of the aboveI/O styles [Hudak89] and although imperative syntax is mimicked they lookvery un-intuitive and cumbersome to someone used to plain I/O statements.Recently, a step forward has been made by employing so-called monads forI/O [Jones & Wadler93, Carlsson & Hallgren93]. This model has also been adoptedfor the latest revision for HASKELL. It remains a matter of taste whether such atreatment of I/O state is conceived as natural or unacceptable.

A related area, also suffering from lack of updates, are cyclic data structures(e.g., cyclic graphs). While these are easily and elegantly expressible in functionalprogramming updating them is very inefficient, since there is no way around re-building the whole cycle15. One circumvention is to emulate pointers with mutablearrays (e.g., in HASKELL) [van Yzendoor95], but then we start to experience a mis-match between paradigm (imperative) and language (functional) used.

Especially reactive systems, furthermore, demand a notion of event. An imagerecognizing robot must stop improving its assumptions when some movement isabsolutely necessary. Functional languages cannot express such time-dependentplanning, since an event interrupt implies execution order, which has no meaningin a functional program [Meunier95a].

Howbeit the tasteful use of a goto may improve a program [Knuth74] it isnot difficult to do without goto altogether. To renounce implicit state, how-ever, appears much harder. Although, monads may emulate backtracking, non-determinism, exceptions, and state, in sum —

“. . . it is true that programming with updates is a proven technology, andprogramming entirely without them is still ‘research’ [Appel93]16.”

– Andrew W. Appel

Monads especially do not seem to allow decoupling and structuring state in a sys-tem.

15Unless the whole data structure is single-threaded which can be difficult to prove.16Appel already knew about Monads and cites [Wadler92].

Page 41: A Functional Pattern System for Object-Oriented Design

1.3 Review 23

Desirable State Arrays are often used due to efficiency considerations. Yet, func-tional programming languages must conceptually copy whole arrays for single up-dates. Only if the compiler can detect a single threaded array use it can use efficientdestructive updates [Wadler90, Odersky91, Jones & Wadler93]. Monadic arrays areguaranteed to be single threaded, though.

It is very convenient to side-effect a result cache, e.g., to reduce the runtimecomplexity of

fib 1 = 0

fib 2 = 1

fib n = fib (n−1) +fib (n−2)

from O(en) down to O(n). There are purely applicative version of memoiza-tion [Keller & Sleep86], but to overcome limitations like expensive quality checkson lists more advanced approaches are necessary. Hughes’ so-called lazy memo-functions are not syntactic sugar anymore, since an identity predicate — normallynot provided by a functional language — is required [Hughes85].

“The interesting thing about memoization in general is that it begins totouch on some of the limitations of functional languages — in particular, theinability to side effect global objects such as caches — and solutions such as lazymemo-functions represent useful compromises. It remains to be seen whethermore general solutions can be found that eliminate the need for these special-purpose features [Hudak89].”

– Paul Hudak

There are more function definitions like the fibonacci function definition above thatlook beautiful but are hopelessly inefficient. Often the culprit is the ++ operatorwhich takes time linear to the size of its left argument. So, instead of

reverse[ ] = [ ]reverse(x : xs) = (reverse xs) ++ [x]

one should apply the accumulator technique [Burstall & Darlington77,Bird & Wadler88] and use

reverse = rev [ ]rev acc[ ] = acc

rev acc(x : xs) = rev(x : acc) xs.

This version of reverse is O(n) instead of O(n2) but undoubtedly lost clarity as well.For the very same reason the nice formulation of displaying a tree

showTree(Leaf x) = show x

showTree(Branch lr) = ′′ <′′ ++ showTree l++ ′′|′′++ showTree r++ ′′ >′′

should be replaced by a less nice version using shows[Hudak & Fasel92].

Page 42: A Functional Pattern System for Object-Oriented Design

24 1 Functional programming

Transformations aimed at efficiency like the accumulator technique or tail-recursive functions often destroy the clarity and beauty of initial solutions17: Onecannot really speak of declarative programming, concise programs, and mathe-matical specifications in view of the tweaked definition of a function to producepermutations:

perms[ ] tail res = tail ++ res

perms(x : xr) tail res = cycle[ ] x xr tail res

cycle left mid[ ] tail res = perms left(mid : tail) res

cycle left mid right@(r : rr ) tail res = cycle(mid : left) r rr tail

(perms(left ++ right) mid : tail res)

fastperms xs = perms xs[ ] [ ]

“Functional languages etc. do of course abstract away from more of themachine-level details than do imperative ones, but real programs in any lan-guage contain a great deal of ‘how’. The nearest thing I know to a declarativelanguage in this sense is Prolog, as described in lecture 1. Unfortunately, lec-tures 2. . . n follow :-) [Sargeant96a].”

– John Sargeant

So, unfortunately a lot of the over-specification of “how” to do things that couldbe left out in nice formulations must be given later in case more efficient versionare needed. Note that the definition of quicksort(see definition 1.10 on page 20)compares each element twice for creating two new input lists. A more efficientversion using an auxiliary split function already appears less clear. Beyond this, thecombination of result lists with append (++) is very inefficient again in time (addingan O(n) factor) and cannot compete with the in-place sort of the imperative version(zero space overhead).

Beyond aesthetical considerations there appears to be a class of algorithms thatare inherently imperative causing one to end up asymptotically worse without im-perative features [Ponder88, Hunt & Szymanski77].

Just to achieve O(1) complexity for insertion and deletion of elements in list — atriviality in imperative programming — some tricky machinery has to be employedin functional programming [Okasaki95c].

Also, some algorithms have a very intuitive imperative form. In John HortonConway’s Game of Life cells need to count the number of inhabited neighbor cells.One approach is to iterate all cells and determine their neighbor count by testingall adjacent cells (see functional version on the left of figure 1.3 on the next page,needing eight tests).

17Evil imperative programmers might remark that they do not care whether their programs areamenable to transformations, since they get acceptable efficency at once and functional programsneed to be transformable because of the poor performance of initial versions.

Page 43: A Functional Pattern System for Object-Oriented Design

1.3 Review 25

!?

Figure 1.3: Functional and imperative LIFE

Alternatively, one may listinhabited cells only and in-crease (by a side-effect) theneighbor count of all adjacentcells (see imperative version onthe right of figure 1.3, needingjust three18 updates).

Unavoidable State Again, itis not possible to circumventstate. Ordering of actions inimperative solutions translatesto ordering of data dependen-cies or nesting in functionalsolutions. Consider JosephWeizenbaum’s Eliza program. One part of it needs to replace words in order touse User input for Eliza’s questions. Given the input list inpsand the replacementlist reps—

inps = [” i” , ”am” , ” i” , ”says” , ”Eliza” ]reps = [(”you” , ” i”),(” i” , ”you”),(”am” , ”are”)]

— you are invited to convince yourself which of the following two expressions(substitute1 or substitute2) does the correct substitutions:

replace org(from, to) xs = if org == from then to else xs

replace′ (from, to) org = if org == from then to else org

substitute1 = map(λw→ foldr (replace w) w reps) inps

substitute2 = foldr (λ p→map(replace′ p)) inps reps.

The above problem is akin to the considerations to be made when you have todetermine the order of list generators in a list comprehension. Unless the correctnesting is specified the result will not be as desired.

While one of the above alternatives is wrong there is another example of twocorrect but nevertheless different solutions: To calculate all possible chessboardconfigurations of eight queens that do not threaten each other (see the correspond-ing ‘safe’ function in definition 1.9 on page 20) both programs queensand sneeuq,with the relation

queens m= map reverse(sneeuq m),

need the same time. Notwithstanding, sneeuqis ten times slower in producing justone solution [Bird & Wadler88]. This effect is due to the order relevance of steps

18In general, the number of neighbors, which is usually low.

Page 44: A Functional Pattern System for Object-Oriented Design

26 1 Functional programming

that produce potential solutions to be tested. An unfavorable order causes muchmore invalid configurations to be tested19.

Another example how ordering corresponds to nesting are monads used in con-junction with folds [Meijer & Jeuring95]. The functions foldr and foldl replace listconstructors with functions. When an associative function (together with an iden-tity element) is used the choice of foldl and foldr does not affect the result20 but ismade according to runtime characteristics. However, when the function is, say, astate monad the result will be affected, akin to an imperative left or right traversalof a sequence with side-effects.

Finally, if one needs to model references, e.g., to represent sharing of nodes ina graph, of course aliasing may occur just as in imperative languages. In this case,the danger is much more concentrated and explicit in a functional program, butnevertheless, unavoidable.

In conclusion, treating state explicitly does not make reasoning about state eas-ier. It only may make reasoning about programs easier, which — though of coursedesirable in itself — is not the same as the former.

1.3.2.3 Deceptive Clearness

Functional programs often look very nice but a deeper inspection frequently re-veals unexpected behavior.

Inefficient lists Given the often weak performance of algorithms using lists itappears unfortunate that much reuse is gained by representing data structureswith lists and using standard list functions for processing and interconnection (seeparagraph Compositionality in section 1.3.1 on page 17). Moreover, often abstractdatatypes are more appropriate for encapsulating data and providing natural op-erations. For instance, a representation of a list of columns for a chessboard con-figuration (see definition 1.9 on page 20) is fine but should be encapsulated by anabstract data type.

Machine lurks below Albeit transformations such as (a+b)+c =⇒ a+(b+c) arepossible and valid one should not be surprised if floating point results are affectedin case machine arithmetic rounding effects come into place. Also, the illusion ofa mathematical world is destroyed when recursion does not terminate and, thus,causes the control stack to overflow. In fact, the problems aligned with uncontrolleduser defined recursion (unproved termination, necessity for inductive proofs, un-known time and space complexity, hindrance of automatic optimization) have beena motivation for the so-called Squiggol school to allow a fixed set of recursive com-

19The author once wrote a backtracking program to generate a solution for the well-known soli-taire game. A different ordering of north, east, south, and west moves caused a one megahertz 6502processor to conclude in 30 seconds while an eight megahertz 68000 processor calculated for 2 days.

20First duality theorem of folds [Bird & Wadler88].

Page 45: A Functional Pattern System for Object-Oriented Design

1.3 Review 27

binators only [Bird & de Moor92, Meijer et al.91]. This research direction towards amore algebraic notion of functional programming demonstrates that

1. reasoning about general recursion is not easy and

2. there is a need to optimize the time and space efficiency of functional pro-grams.

Pattern Matching Often, nice definitions as

pred0 = 0

pred(n+1) = n

are shown but one should not be mislead to believe that the automatic inversecalculation of operands is possible in general. The n+ k pattern is just a specialcase and often one will need to decompose arguments with functions as opposedto patterns.

In lazy languages subtle effects may occur:

f 1 1 = 1

f 2 = 2

behaves differently than

g 1 1 = 1

g 2 = 2

because f 2 ⊥ 21 yields 2 but g ⊥ 2 yields⊥ [Hudak89], i.e., contrary to intuitionpattern matching depends on the ordering of arguments. The same applies to theordering of patterns which is crucial for the correct definition of the function. Byexchanging the first two patterns of take

take0 = [ ]take [ ] = [ ]

take(n+1) (x : xs) = x : take n xs

one can tune a preference for possibly getting a result though the numeric argu-ment (or alternatively the list argument) is not available (i.e., ⊥). Other subtle ef-fects of pattern and argument ordering caused by pattern matching are discussedin [Hudak89]. HASKELL uses a top-to-bottom left-to-right ordering of patterns andarguments. Note that this imposes restrictions for compilers. They are no longerfree to evaluate arguments in any order.

21“Bottom” is used to denote “error” or non-termination.

Page 46: A Functional Pattern System for Object-Oriented Design

28 1 Functional programming

init

resps

reqsclient server

Figure 1.4: I/O feedback model

Laziness meets pattern matching also incase of circular dependencies: HASKELL’sI/O model was based on a feedback loopdefined by

reqs = client init resps

resps = server reqs

The associated function definitionsare [Hudak & Fasel92]:

client init (resp: resps) = init : client (next resp) resps

server(req : reqs) = process req: server reqs.

Before client has issued a request it already “pattern matches” for a response. Pat-tern matching causes evaluation and subsequently a deadlock. Note that the moreconventional version without pattern matching

client init resps = init : client (next(head resps)) (tail resps)

would work! For these and other cases HASKELL provides a lazy pattern matchingoperator.

Sometimes pattern matching works but causes unexpected inefficiencies: Thedefinition of merge sort

merge[ ] ys = [ ]merge xs[ ] = [ ]

merge(x : xs) (y : ys) = x : merge xs(y : ys), x<= y

y : merge(x : xs) ys, x> y

splits the heads from input lists only to reassemble them in two branches respec-tively [Buckley95]. HASKELL allows naming patterns, e.g., merge xs′@(x : xs) ys′@(y :ys) in order to use xs′ for a recursive call for merge, thereby adding another specialoption to pattern matching. This option introduces a further subtlety: Actually, thetype (given in parentheses) of

data Sum a b = L a | R b

copyr(R b) = R b (:: Sum a b→ Sum a c)

is different to

copyr r@(R b) = r (:: Sum a b→ Sum a b),

since the former definition amounts to a reconstruction [Okasaki95b].An interesting collection of typical problems students encounter when learning

functional programming is given in [Clack & Myers95].

Page 47: A Functional Pattern System for Object-Oriented Design

29

2 Object-orientation

Computing is viewed as an intrinsic capability of objectsthat can be uniformly invoked by sending messages.

– Adele Goldberg

This chapter introduces object-orientation and defines some terms thatare used in later discussions. I explain the underlying worldview (sec-tion 2.1) and the most important concepts (section 2.2 on page 31). Fi-

nally, section 2.3 on page 38 enumerates pros and cons of object-orientation.In this dissertation I restrict myself to class based languages like C++, EIFFEL,

and SMALLTALK. There is another interesting class of so-called delegation basedor prototyping languages like SELF which use a dynamic type of inheritance andrenounce classes. However, classless languages are not in widespread use and oneof my aims is to improve the practice in object-oriented design.

2.1 Worldview

The object-oriented paradigm suggests to decompose systems in autonomous ob-jects. Autonomous means an object

1. represents a complete entity. It is not just material or tools but both at once.

2. has a self-supporting state. It manages and keeps its state without needingsupport.

3. provides self-sustained operations. In case it refers to other objects it does notmatter to clients.

In terms of real world modeling objects represent real world objects and capturetheir identity, state, and behavior. In terms of software decomposition each objectcan be regarded as a little computer inside the computer.

“For the first time I thought of the whole as the entire computer andwondered why anyone would want to divide it up into weaker things calleddata structures and procedures. Why not divide it up into little comput-ers. . . ? [Kay96]”

– Alan Kay

Page 48: A Functional Pattern System for Object-Oriented Design

30 2 Object-orientation

So, object-oriented systems have a less hierarchical but a more collaborative nature.Even when reducing complexity by decomposition it is not done at the expense ofthe power of the parts.

“The basic principle of recursive design is to make the parts have the samepower as the whole.” – Bob Barton

In a purely object-oriented languages there are no free functions that are definedon objects. Every function must be part of a data abstraction. Hence, an object-oriented systems bears no resemblance to an Input/Processing/Output model butconstitutes a network of interactive events and responses.

Objects can be thought of as abstract datatypes. This correspondence is finewith regard to real world modeling but from a software engineering perspectivewe have to take a closer look. Abstract datatypes define functions on constructors,i.e., provide data abstraction. Objects, distribute operations to constructors, i.e.,provide procedural abstraction [Cook90]. An object operation implicitly knowsthe constructor it operates on, since it is tied to its definition. A function on anabstract data type has to distinguish between constructors first. That is why, itis hard to introduce new constructors to abstract datatypes (all functions must beextended) and easy to just add a new object. Conversely, it is hard to add a functionto objects (all objects must be changed) and easy just to define a new function forall constructors.

Note, that most object-oriented languages allow using classes for both abstractdatatypes (hide representation of implementation) and procedural abstraction (dis-tribute constructors to subclasses and bind dynamically). In any case, objects areaccessible only via the operations they provide. These are invoked by so-calledmessage sends (or method calls).

Object-orientation not only decomposes complex systems into smaller parts cor-responding to real world entities, but also captures these entities in a taxonomy ofinheritance relationships. Inheritance may denote:

BIRDMAMMAL

WHALE PIG

ANIMAL

VULTURE

Figure 2.1: Object-oriented taxonomy

Page 49: A Functional Pattern System for Object-Oriented Design

2.2 Concepts 31

Is-a The heir is of the kind of its ancestor (also referred to as specialization inher-itance). This relationship classifies entities, similar to the classification ofanimals in zoology1 (e.g., Whale is a Mammal ).

Subtype The heir can be used whenever the use of its ancestor is appropriate Heirsconform in interface and behavior and provide monotonic extensions only.This is also known as the Liskov Substitution principle [Liskov & Wing93] (e.g.,a Vulture can be used whenever a Bird is expected).

Code reuse Heirs inherit code from their ancestors (also known as subclassing).Either through directly re-exporting inherited features or by their use for thedefinition of new features heirs may exploit ancestor code (e.g., Pig may reusecode templates of Mammal ).

CAR

SPORTSCAR

VEHICLE

Figure 2.2: Idealinheritance

Ideally, these three types of inheritance coincide (see fig-ure 2.2), but normally they are in conflict with each other. Forinstance, a IntegerSet is-a Set but it is not a subtype, sinceclients may want to insert strings. Furthermore, Set may in-herit code from HashArray but neither is-a nor subtyping areaccurate [LaLonde & Pugh91].

The object-oriented worldview can be said to rule the soft-ware engineering world. Since its birth through SIMULA in1967 it became the programming language and methodologyparadigm [Rumbaugh et al.97] of choice. There was a 60% in-crease in SMALLTALK uses in 1993–1994 [Shan95] and today it iseven used in embedded systems like oscilloscopes [Thomas95].

2.2 Concepts

The following sections are meant to briefly introduce concepts of object-orientedlanguages. For a more thorough discussion the reader is referred to [Wegner87,Wegner90, Beaudouin-Lafon94] for technical matters, to [Nelson91] for a clarifica-tion of terms, and to [Meyer88, Nierstrasz89, Jacobson et al.94, Booch94] for soft-ware engineering and system development relevance.

Subsequently, I will use the syntax of the object-oriented language EIFFEL inorder to depict object-oriented concepts. The syntax is very clean and intuitive,but you may want to refer to a definition [Meyer92], introduction [Racko94],textbook [Rist & Terwilliger95, Thomas & Weedon95], or application developmentbooks [Walden & Nerson95, Jezequel96]. EIFFEL’s garbage collection avoids dis-tractions from memory management and its static type system allows typing issuesto be discussed. EIFFEL’s clear syntax, simple semantics, and support of correctnessthrough the use of assertions made it the premier choice for an education languagefor many universities throughout the world [Meyer93].

1With multiple inheritance one may even let whales inherit from mammals and fishes, thus,avoiding duplications caused by single inheritance.

Page 50: A Functional Pattern System for Object-Oriented Design

32 2 Object-orientation

2.2.1 Object-oriented decomposition

The idea to draw subsystem boundaries around data and associated functions isone of the most important aspect of object-orientation. Actually, it is a simple ap-plication of information hiding [Parnas72]. That way, the representation of dataand the implementation of functions is hidden from other subsystems. Object-orientation supports this modularity by encapsulation (see section 2.2.2) and boostsit by allowing multiple instances of modules. The latter lifts a tool to control soft-ware complexity to a paradigm for real world modeling.

Right in the spirit of SIMULA object-oriented systems simulate real world pro-cesses. Each participant in the real world can be represented by a software artifactcalled object. In an object-oriented bureau we would not see functions like “filecontent of incoming letter into drawer”, but objects like Letter (supporting retrievalof content) and Drawer (supporting addition of information). Clearly, objects offerservices and do not prescribe the order of actions. The object-oriented secretary be-comes a conductor of object capabilities as opposed to a hierarchical manipulatorof materials. As a result, the design is much more stable, because even if processesin the bureau are drastically changed the basic participants (objects) are likely tostay. This promises less software maintenance in case of change but also greaterpotential for reuse of objects in other domains (e.g., Letter will be useful in otherdomains as well). Again, the reason for this reuse potential is the absence of a rigidtop-down conceived function call structure. The bottom-up provision of generalservices is more suited to be reused in altered conditions.

The recursive decomposition of data works especially fine due to the conceptof procedural abstraction (see section 2.2.5 on page 35). In the object-oriented bu-reau objects issue goals to each other. The secretary may ask a drawer to file aninformation. The drawer in turn forwards the goal to one of its sub-compartmentsand so on until the goal is achieved. The secretary does not need to know aboutthe internal organization of drawers which also do not care what types of sub-compartments exist. Clients never care about the types of their servers. Conse-quently, servers may be exchanged without need to alter clients. This would not bethe case if, e.g., a secretary took different actions depending on drawer types.

2.2.2 Encapsulation

We already noted above that encapsulation supports the mutual protection of in-dependent software entities. This observation is important for programming in thelarge. For programming in the small it is important to recognize the support ofencapsulation for data integrity. An object is responsible for its own initializationafter creation. It is ought to establish a consistent state at this point. Later ma-nipulations to the object’s state may only occur through the associated procedures.These, however, are designed to keep the object’s state consistent as well. It is notpossible to accidentally add an element to the bottom of a Stack because one hasaccess to the stack’s List representation and choose an invalid operation. A clientof the chessboard data structure from section 1.3.2 on page 21 may insert an integer

Page 51: A Functional Pattern System for Object-Oriented Design

2.2 Concepts 33

out of range which does not correspond to a column. Encapsulation would preventa chessboard to get in such an invalid state.

For the reason that procedures and functions (i.e., methods) are defined on anencapsulated object it is reasonable to allow an implicit access to the object’s fea-tures (methods and attributes). Hence, object descriptions get shorter and the tightbinding of features to an object is emphasized. One can think of methods to alwayshave one2 (the object reference) implicit argument.

The combination of encapsulation and procedural abstraction (see section 2.2.5on page 35) allows for a nice treatment of non-free abstract data-types. For in-stance, a rational number object may keep its numerator and denominator alwaysin normalized form and clients cannot be confused by comparing non-normalizedinstances with seemingly unequal normalized instances.

Object-oriented languages differ in their encapsulation scope. A language withclass encapsulation — such as C++ — allows methods to access internals of entitiesof the same class. A Stack class may access the representation of a stack argumentin the implementation of a pushAllFromStack method. In a language with objectencapsulation — such as EIFFEL — objects are protected against each other even ifthey are instances of the same class. The pushAllFromStack from above would haveto use the public Stack interface (e.g., to pop all elements one by one) only.

2.2.3 Inheritance

In comparison to languages like PASCAL or C, object-oriented languages empowerthe programmer much more, since it is not only possible to define new types

Cha

nges

in ti

me

yiel

d v

ersi

ons

Different refinements yield variants

VERSION

VARIANT1 VARIANT2

COMMON_BASE

Figure 2.3: Classes as versions and variants

but these can be made to appear justlike built-in types3. Even beyond,types can be incrementally derivedfrom other types by inheritance. Anheir inherits both interface (methodsignatures) and code (attributes andmethod bodies) from its ancestor. Atypical use of inheritance is to spe-cialize a type, e.g., derive a sports-car from a car. Other uses of inher-itance are discussed in section 2.3.2on page 41. Inheritance can be useto classify both data (see figure 2.1 onpage 30) and algorithms [Schmitz92].Therefore, it can be used as an orga-nization principle for libraries.

There is also an interesting corre-spondence between Inheritance and

2CLOS methods may break the encapsulation of more than one object.3C++ and EIFFEL even allow user defined infix operators.

Page 52: A Functional Pattern System for Object-Oriented Design

34 2 Object-orientation

version/variant control4 (see figure 2.3 on the preceding page). When inheritanceis used to factor out code, that is, a set of subclasses uses and shares superclass code,it establishes a system of variants. Changes that should affect all classes are madein the common superclass. Changes meant for individual variants only are madeto subclasses only. A subclass can be regarded as being a variant to its siblings orto be a version of its ancestor.

When object-oriented languages began to compete for market-shares theirprominently advertised feature was inheritance. As a mechanism directly support-ing reuse it was promoted as a unique and most important feature. The presenceor absence of inheritance was used to distinguish between truly object-oriented orjust object-based languages [Wegner90].

After all, inheritance turned out to be of less significance. The author rates datacentered design, encapsulation, and dynamic binding to be of much more value. Infact, until today inheritance is not fully understood yet (see critique in section 2.3.2on page 41). My view is supported by the fact that SMALLTALK-72 did not evenhave inheritance [Kay96], though its paragon SIMULA67 did.

2.2.4 Subtyping

scale

FIGURE

ELLIPSE

scale

CIRCLE

scaleradius1radius2

radius

Figure 2.4: Subtyping inheritance

We already got subtyping to know as aspecial kind of inheritance in section 2.1on page 29. Yet, Subtyping has lessto do with incremental class definitionsbut rather denotes a particular formof polymorphism called inclusion poly-morphism [Cardelli & Wegner85]. Fig-uratively, one can assume the type Fig-ure (see figure 2.4) to include the typeCircle , since a Circle will behave ex-actly as a Figure when viewed andmanipulated through a Figure inter-face. For subtyping, or the Liskov Sub-stitution principle [Liskov & Wing93], tohold true it is important that not only argument types vary contravariantly andresult types vary covariantly, but also the pre- and post-conditions must obey thesame principle. That is, pre-conditions may be weakened and post-conditions canbe be strengthened. Subtyping, hence, works especially well with a programming-by-contract model, which is supported by EIFFEL. Its language support for pre-and post-conditions promotes the verification of behavioral subtyping as well.

Inclusion polymorphism is one of the distinguished features of object-orientedlanguages in comparison to functional languages. Functional languages, typicallyoffer so-called parametric polymorphism only5. For instance, in the type of map

4Consult [Schroeder95] for terminology, history, and classification of version controlling.5Of course, subtyping is desirable for functional languages too and its integration into functional

Page 53: A Functional Pattern System for Object-Oriented Design

2.2 Concepts 35

(see typing judgment 1.7 on page 17) there is an implicit all-quantifier in front ofthe judgment. This type corresponds to unconstrained generic classes in object-oriented languages [Meyer92]. A variable of type Figure , however, is existentiallyquantified. The difference is that a parametric polymorphic function does the samefor all instantiations of the type variable. Inclusion polymorphism just states thatthere is a concrete type which will behave individually.

Subtyping is of special value for the user of a library. It guarantees the sub-stitutability of classes. A piece of code will work for all subtypes, if it worksfor the supertype. Library writers, on the other hand, often long for subclassing(for code reuse) and is-a relationships (for classification). For the reason of thesediverging goals subtyping should not be tied to subclassing as it is the case inC++ or EIFFEL. Rare exceptions to this rule are POOL [America & v. Linden90] andSATHER [Murer et al.93a].

In sum, subtyping can be regarded as putting a discipline on inheritance (al-lowing conforming subclasses only) and polymorphism (excluding ad-hoc poly-morphism, which allows no assumption about behavioral commonalities).

2.2.5 Dynamic Binding

Just as structure programming hid the functionality of goto behind if/else, while and do;OOP has hidden the functionality of indirect goto behind polymorphism.

– Robert C. Martin

Subtyping without existential qualification of type variables would be akin toHASKELL’s type classes. This is a fine mechanism to make ad-hoc polymorphismless ad-hoc [Wadler & Blott89]. Contrary to an occasional misconception [Berger91]type classes do not allow for true object-oriented programming.

Most important for true object-oriented programming is the concept of dynamicbinding. Subtyping just restricts dynamic binding “vertically” to the inheritancehierarchy. Now, a routine call to an object variable is not compiled as a standardsubroutine call. The code to be executed is not determined before runtime. That iswhy, dynamic binding is often also called late binding. The code selection dependson the actual object contained in the variable at runtime. Consider the code

figure : FIGURE;

!CIRCLE!figure;figure.display;figure.typeMessage;

Although figure is of type Figure it refers to an object of type Circle . In figure 2.5on the next page the lookup of methods is depicted.

All method searches start at Circle . As method display is defined by Circle it isused in spite the presence of a general display method in Figure . As innocent as this

languages is a important research topic [Laufer96].

Page 54: A Functional Pattern System for Object-Oriented Design

36 2 Object-orientation

"display"

"typeMessage"

FIGURE

displayprintTypetypeMessage

CIRCLE

displayprintType

print "I am a ";printType;print " object.";

print "Circle";

Figure 2.5: Dynamic method lookup

looks it is most important for the reuse promise of object-oriented programming,for it allows old code to use new code. For instance, a drawing editor may use aniterator to display all visible objects. If the editor uses the Figure interface to invokedisplay one can add new figure types without any need to change the iterator code.This inversion of control (don’t call us — we call you), or Hollywood-principle,is typically not possible in procedural languages like C6. Indeed, however, it liftsreuse from plain library reuse to the reuse of design, that is, prefabricated applica-tion skeletons (called frameworks). The prefabricated code can be tailored throughthe use of dynamic binding.

For this to work properly it is most important that self calls (implicit calls tomethods of the object itself) are also subject to dynamic binding. To see why, con-sider the typeMessage call in figure 2.5. First, it is redirected to Figure as there is noredefined version in Circle . The implementation of typeMessage prints a string tem-plate and calls printType to output the actual information. Now, instead of invokingthe implementation in Figure this self-call is also late bound to the implementationin Circle . This leads to the desired result of printing “I am a Circle object”. Late bind-ing of self-calls, ergo, enable this factoring of code into code templates7 that can bealtered and refined by new classes.

Dynamic binding effectively defers behavior distinction from the main programto the datatypes. This opens up the possibility to build frameworks that do not

6One may exploit function pointers for this but it is not a common programming style.7Method typeMessage is indeed part of the Template Method pattern [Gamma et al.94].

Page 55: A Functional Pattern System for Object-Oriented Design

2.2 Concepts 37

need to know about new types to come. Dynamic binding also makes it reasonableto operate with lists of heterogeneous types. Functional lists are homogenous, i.e.,allow one element type only. One may use a sum type for homogenous lists butin order to differentiate actions according to individual element types type casesbecome necessary. Dynamic binding assigns the responsibility to differentiate tothe elements and, therefore, avoids the type cases. Actually, the main “trick” usedis the data centered approach, i.e., distributing operations to their data. Hence,case statements — otherwise contained in functions scattered around the wholeprogram – are concentrated at data abstractions. Dynamic binding “merely” hidesthe indirection used to access the distributed operations. In this light it appears aslanguage support for data centered programming which can be pursued also (bythe albeit clumsy use of function pointers) in C.

Note that functional languages also allow using old code with new code. Forinstance, mapcan be used with any new function with appropriate type. Notwith-standing, this type of parameterization does not amount to the same flexibility,since the variable parts (e.g., function to map) must be specified in advance. Anobject-oriented framework, however, can be adapted in many unforeseen ways, asit is possible to override any methods — not just those intended for parameteriza-tion. For instance, Circle may also redefine typeMessage to print “Circle, I am”.

Since the latter form of reuse requires knowledge about the internals of thereused classes it is called “white-box reuse”. The corresponding name for com-posing opaque parts — just like higher-order functions — is “black-box reuse”.

Akin to lazy evaluation (see section 1.2.4 on page 14) dynamic binding sup-ports modularization. Analog to the separation of data generation and control theseparation between method selection and method invocation decouples the clientfrom the server. The client does not need to prescribe the server’s action. It justdeclares a goal (through a message send) and the server is free to choose whateverappropriate action.

Almost all object-oriented languages use single-dispatch, i.e., dynamic bindingis used for the receiver of a message send only. If dynamic binding is extendedto the arguments of the message send it is called multi-dispatch. The languagesCECIL [Chambers92b] and CLOS [Bobrow et al.86a] hereby allow implementationsto be selected with respect to all argument types.

2.2.6 Identity

All objects are created unequal.– me

The concept of object identity [Khoshafian & Copeland86] is easily overlookeddue to the presence of more prominent concepts like inheritance, encapsulation,and polymorphism. Nevertheless it also significantly contributes to the expressive-ness of the object-oriented paradigm. What does object identity mean? One wayto look at it is to observe that objects are mutable. When an object changes, e.g., anelement is inserted into a list, there is no new list being created. The list remains the

Page 56: A Functional Pattern System for Object-Oriented Design

38 2 Object-orientation

same but has a different value. This property gives rise to easy real world model-ing but also may induce problems due to unwanted aliasing. Aliasing occurs whentwo or more variables refer to one object and some interference takes place, e.g., aclient with access to an object is not aware of other accesses and is invalidated byexternal changes made to its referenced object.

In contrast values (from functional programming) are immutable and lack iden-tity [MacLennan82, Eckert & Kempe94]. It does not make sense to make a differ-ence between two numbers of the same value or two lists with the same elements.This perspective, however, leads to the second way to look at identity: Considerpersonal data, such as name, age, city of birth, etc., held in records. Let us assumeIvan Smith exists twice with the same age. One Ivan is born in St. Petersburg (Rus-sia) and the other is born in St. Petersburg (Florida). Unless we include the state ofbirth the two Ivan’s have the same value, i.e., they are the same person in a func-tional language. Represented as objects, there is no problem at all. If one comparesfor data equality one will find out about the curiosity of the coincidental birth date.Yet, if one compares for identity it is clear that we have two distinct persons andnot just one person that we somehow received twice. Objects may join their valuesbut their identity remains [Harrison & Ossher93].

Summarizing, identity is very closely related to state, but one should emphasizeits role in modeling real world objects with identity (A tank does not change onlybecause its pressure does) and its role to distinguish between objects that happento have the same value (Ivan is not Ivan).

2.3 Review

The following two sections give a subjective assessment of object-oriented pro-gramming from a software engineering point of view.

2.3.1 Pro

Why does the combination of the above presented concepts work well? This sectionexamines the positive implications of the object-oriented paradigm.

2.3.1.1 Real world modeling

Whereas Turing machine behavior can be expressed by mathematical models,the observable behavior of interaction machines corresponds to that

of empirical systems in the natural sciences.– Peter Wegner

Two properties allow for real world modeling without impedance mismatch8:

8When a modulated current flows through a conductor which is not properly terminated someenergy will be lost due to reflections. Similarly, “energy” will be lost if the modeling domain cannotadequately express the things to be modeled.

Page 57: A Functional Pattern System for Object-Oriented Design

2.3 Review 39

1. The emphasis on a collaborative set of self-sustained objects and

2. direct support for the notions of state and identity.

Real world systems are often composed of a collaborative set of self-sustained ob-jects themselves and their structure provides a natural starting point for an object-oriented design. Maintaining the system, thus, will be possible without recon-structing too many transformation and abstraction steps. An insufficient under-standing of the system would lead to faulty changes or bug fixes.

With regard to state support it is instructive to note that all of the argumentsagainst reduction semantics (in section 1.3.2 on page 21) vanish. Furthermore, theusually as negative discredited implications of state support like references andaliasing are by no means just a crude copy of an underlying primitive von Neu-mann hardware. Consider an office software where you may embed pictures intoword-processor texts. Often we want to share images between multiple texts. Acorrection to the image should be reflected in all uses of the image9. This is a trulyuseful use of aliasing. Also, we may have a link to our office software representedby an icon to click on in order to start the software. The link allows changing thelocation or version of the software without bothering the user with a new place tostart from or a new icon to use. This is a truly useful use of referencing. Changesto documents are modifications to mutable objects with an identity. A model re-flecting this employs a truly useful use of state. Given these useful applications ofidentity and state it seems worthwhile to assign them direct language support.

2.3.1.2 Maintainability

“Software systems are long-lived and must survive many modificationsin order to prove useful over their intended life span. The primary linguis-tic mechanisms for managing complexity are modularity (separating a systeminto parts) and abstraction (hiding details that are only relevant to the internalstructure of each part). A challenge for future language design is to supportmodularity and abstraction in a manner that allows incremental changes to bemade as easily as possible. Object-oriented concepts have much to offer and arethe topic of much on-going investigation [Hankin et al.97].”

– Chris Hankin et al.

Seamless development All phases of a software development (requirementsanalysis, analysis model, design, implementation) are based on the same founda-tion, hence, avoiding paradigm shifts inbetween. Phase transitions are consideredseamless [Walden & Nerson95] and, ergo, backward or forward changes have lessimplications compared to a methodology involving paradigm shifts, i.e., redesigns.

9A individual change can be realized by first making a copy of the image.

Page 58: A Functional Pattern System for Object-Oriented Design

40 2 Object-orientation

Object-oriented decomposition Building up on a base of reusable self-sustaineddomain or business objects yields more stable systems. There is no main functionthat can be invalidated by a series of requirement changes. The introduction ofnew data — i.e., the “know how” of a system is applied to a new domain — is easy,for it amounts to a local addition of code only. Similarly to extensions, changesshould occur more localized too. The distribution of functionality from a hierar-chy of functions to the former “dead data”, consequently, can be thought of as acomplexity reducing mechanism. Class definitions create a small working spaceand make the first argument of their methods implicit. Changes, ergo, become amatter of altering a small encapsulated system fraction with short access to localdata. Usually, there is no need to teach binding environments and techniques likedisplay tables or static links anymore due to the simple structure of classes10.

Modularity We already explained in the preceding concept sections how encap-sulation (section 2.2.2 on page 32) and procedural abstraction (section 2.2.5 onpage 35 and section 2.2.1 on page 32) aid the modularity of programs. Clientsdo not care how servers fulfill their services and can rely on consistent and self-managing objects at any time.

2.3.1.3 Beyond the imperative

Although the object-oriented paradigm is closely related to an imperative style ofprogramming it has been argued that there are important differences [Kuhne96a].Objects successfully hide the use of state and represent explicit substates to changeand to handle, hence, escape the von Neumann bottleneck. The famous critique ofJohn Backus [Backus78] towards imperative languages like FORTRAN and ALGOLmust be re-evaluated with regard to the object-oriented paradigm. Let us recallthe quotation of Alan Kay on page 29: He viewed objects as a decomposition ofthe whole computer with equal power. Now, the whole computer and its decom-position objects do not need to be von Neumann architectures. It is possible to useobjects in a referential transparent way [Meyer94a] or as nodes in a dataflow graph.

“What I got from Simula was that you could now replace bindings andassignment with goals. The last thing you wanted any programmer to dois mess with internal state even if presented figuratively. . . . It is unfortu-nate that much of what is called ‘object-oriented programming’ today is simplyold style programming with fancier constructs. Many programs are loadedwith ‘assignment-style’ operations now done by more expensive attached pro-cedures. [Kay96]”

– Alan Kay

In particular, the improved control abstraction facilities of object-oriented lan-guages compared to early imperative languages weaken Backus’ critique. We willreturn to the object-oriented possibilities to escape the limitations of plain impera-tive languages in section 4.2 on page 57.

10C++and JAVA allow nested classes though.

Page 59: A Functional Pattern System for Object-Oriented Design

2.3 Review 41

2.3.2 Contra

Programming today is a race between software engineersstriving to build bigger and better idiot-proof programs,

and the Universe trying to produce bigger and better idiots.So far, the Universe is winning.

– Rich Cook

2.3.2.1 Imperative heritage

Most object-oriented languages are based on ALGOL-like languages (C++, SIMULA,EIFFEL) and some on LISP (SMALLTALK, CLOS, DYLAN). All share an imperativebase trough their ancestors. While we argued in section 2.3.1.2 on page 39 thatobject-oriented languages reach beyond their imperative roots, they still suffer fromtheir imperative heritage. One example are system shut-downs due to methodcalls on void references. This is clearly an initialization problem which in generalhas already been criticized by John Backus [Backus78] and which we will tackle inchapter 11 on page 191.

Aliasing Consider a program fragment using a complex number library:

!!a.make(2,0);b:=a;c:=a+b;!!a.make(0,2);d:=a+b;

We would expect d to have the value 2+2i but if complex numbers are implementedwith reference semantics — which is quite likely to be the case due to efficiencyconsiderations — the result will be 4i. The reason for this is the unexpected aliasingof b with a. Though object-oriented languages typically provide support for valuesemantics (e.g., expanded classes in EIFFEL, part-objects in BETA, non-referencevariables in C++) there is no agreed system how to choose between reference andvalue semantics.

Another source of aliasing are interferences between parameters and results. Amethod does not represent a good abstraction if the effects of

o.m(x, y)

differ to

o.m(x, x).

Also,

aMatrix.multiply(aMatrix2)

possibly yields the wrong result when aMatrix2 happens to reference aMatrix ,since then result elements will override still to be used argument elements.

Page 60: A Functional Pattern System for Object-Oriented Design

42 2 Object-orientation

Broken encapsulation When object state is distributed over several sub-objectsand these objects are accessible from outside the object the state-encapsulation ofthe object is broken. For instance, a careful bank customer, aiming at never havingless than $2000 on his account, will be invalidated by his wife, if she has accessto the account too [Duke95]. Similarly, a balanced tree can get unbalanced if oneof its element is changed by a side-effect. A solution for these problems has beenproposed by the use of so-called Islands [Hogg91].

2.3.2.2 Classes for everything

The class concept is used for representing modules, encapsulation borders, in-stance generation, code pools, types, visibility mechanism, and interfaces. Al-though the strive for unification, i.e., to capture many things with a single ab-straction, is desirable, the wealth of a class’ functions appears too much. Therehas been arguments to separate modules and classes [Szyperski92], code pools andtypes [Murer et al.93a], and encapsulation and classes [Limberghen & Mens94]. So-called Kinds have been proposed to aid classes with typing [Yu & Zhuang95] and“namespaces” are supposed to cluster C++ classes. Obviously, there will be moreresearch necessary to remove the overloading of concepts on classes.

2.3.2.3 Inheritance for everything

A modeling tool is more than a passive medium for recording our view of reality. It shapesthat view, and limits our perceptions. If a mind is committed to a certain [tool], then it willperform amazing feats of distortion to see things structured that way, and it will simply be

blind to the things which don’t fit that structure [Kent78].– W. Kent

Inheritance has been the “hammer” for many problems (be they “nails” or not)for both language designers and language users. Though inheritance is primarilyan incremental modification operator on classes it fulfills many purposes:

Language designers use inheritance for

Subtyping An heir is implicitly assumed to be a subtype to its ancestor and, thus,is allowed to be substituted for it.

Code reuse Code reuse happens through inheriting methods which continue towork in the heir but also by deliberately importing methods only meant to beused for the implementation of the heir.

Interfaces So-called pure, abstract, or deferred classes only specify interfaces anddo not contain any code. They are used to define an interface that implemen-tations may adhere to.

Page 61: A Functional Pattern System for Object-Oriented Design

2.3 Review 43

Modules The collection of mathematical constants in the EIFFEL library is an ex-ample for solely using a class as a namespace and container of data withoutintending to use it as a instance generator.

Mixins Languages with multiple inheritance suggest a LEGO approach to the com-position of code. Each superclass mixin provides a facet of a class like per-sistence, observeability, etc. This style is also referred to as facility inheri-tance [Meyer94b].

Language users exploit inheritance for

Classification Libraries are sometimes organized as a taxonomy of concepts (seefigure 2.1 on page 30). This helps to understand classes in terms of others.Also, algorithms may be subject to classification [Schmitz92]. Each branch inthe inheritance hierarchy then represents a design decision.

Specialization Heirs narrow the scope of ancestors, e.g., a Student is a special Per-son , typically by adding information (e.g., student identification). Special-ization may conform to subtyping (like in the SportsCar and Car example infigure 2.2 on page 31) but does not necessarily (a 3DPoint is not a subtype ofPoint due to the covariant equality method).

Generalization Heirs cancel limitations of their ancestors. For instance an all pur-pose vehicle may be subclassed to obtain an all purpose vehicle that allowssteering of the rear axle. This usage is typically due to unforeseen extensionsand coincides with code reuse.

Versioning As explained earlier (see figure 2.3 on page 33) it is possible to use heirsas versions of their ancestors. This allows extending data records in a systemwhile reusing all of the control code unchanged.

Parameterization Partially abstract classes are used as ancestors to concrete sub-classes that specify the missing code. This type of code parameterization hasbeen described as the Template Method pattern [Gamma et al.94].

The problem with all these uses for inheritance is that always the same rulesfor visibility, substitutability, and namespaces are applied. While breaking en-capsulation is necessary for white-box reuse it leads to fragile base classes in gen-eral [Snyder86, Kuhne95b]. Also,

• inheritance used for code reuse or covariant specialization should not enablesubstitutability [LaLonde & Pugh91].

• Inheritance used for parameterization should not break encapsula-tion [Kuhne95b].

• Interface inheritance should not allow covariant redefinitions of argu-ments [Cook89b], etc.

Page 62: A Functional Pattern System for Object-Oriented Design

44 2 Object-orientation

Consult section 2.3.2.2 on page 42 for a list of suggestions to escape the “one-trick-pony” mentality towards inheritance in object-oriented languages.

Especially multiple inheritance causes additional problems like aggravating en-capsulation, modularity and the open-closed principle [Carre & Geib90], which iswhy many languages do not provide it (e.g., SMALLTALK, BETA, JAVA).

It can be argued about whether it is positive or negative that inheritance typ-ically is a static mechanism. There is no doubt, however, that inheritance shouldnot be used when more dynamic solutions are appropriate. We will return to thissubject in chapter 7 on page 93.

Page 63: A Functional Pattern System for Object-Oriented Design

45

3 Calculus comparison

As we are going to subsume functional concepts with the object-orientedparadigm in chapter 4 on page 55 and aim at capturing functional tech-niques with object-oriented design patterns in part II starting at page 85

this chapter shall assure us about the validity of this approach.Of course, all functional and object-oriented languages are Turing complete

with regard to their computation power. There is no doubt that any of these lan-guages can emulate one of the other. In spite of that it is quite interesting to askwhether one emulation is easier to accomplish than the other. If it is easier to cap-ture object-oriented programming with a functional language we should write pat-terns facilitating object-oriented programming in a functional language. The goalof this chapter is to prove the opposite.

3.1 Language comparisons

Looking for the computational difference between two languages is likecomparing apples to oranges only to find out that both are fruit.

– me

What is left if we beware of falling into to the Turing tarpit, i.e., try to find adifference in computational power that is simply not there? What other indica-tors for expressiveness are available? One interesting approach is to evaluate theamount of restructuring a program necessary to emulate the addition of new con-struct [Felleisen91]. If local changes are necessary only then the new construct canbe judged to add no further expressiveness. Unfortunately, the theoretical frame-work used for this approach does work for conservative extensions to languagesonly, i.e., cannot be used to compare two fundamentally different languages.

For the purpose of this chapter my idea was to define a translation of one lan-guage into the other and vice versa and then compare the translation complexities.The rational is that a language should emulate a less expressive language withease while a considerable amount of machinery would be necessary the other wayround.

In order to compare the fundamental mechanisms instead of technicalities ofspecific languages it appeared most reasonable to compare calculi expressing thenature of the two paradigms respectively. The calculus representing functional pro-gramming is easily found and is referred to as the λ-calculus. You might want to

Page 64: A Functional Pattern System for Object-Oriented Design

46 3 Calculus comparison

refer to an introduction for its syntax and semantics [Barendregt84, Hankin94]. It ismuch less obvious to choose a calculus representing the object-oriented paradigm.Many approaches to model object-oriented principles with calculi use an extendedversion of the λ-calculus or do contain first-order functions [Abadi94, Bruce94,Cardelli84, Cook89a, Fisher & Mitchel95, Pierce & Turner94, Reddy88]. A com-parison with such a calculus would be ridiculous because of the trivial emula-tion of the λ-calculus within it. When I made the decision which calculus tochoose there where two candidates that did not model objects as records in afunctional language but attempted to model object-oriented languages from firstprinciples. One was OPUS [Mens et al.94, Mens et al.95] and the other was the σ-calculus [Abadi & Cardelli94]. In fact, the latter calculus was inappropriate too forit allowed to bind the self reference of an object to an arbitrary name and, thus,enabled to access variables of arbitrary nesting depth. In a comparison carriedout by Dirk Thierbach this feature was found out to be very “functional” in na-ture [Thierbach96] and also would have prevented a fair comparison. Thierbachalso identified several weaknesses of OPUS and developed an improvement calledOPULUS. He proved OPULUS to have the Church-Rosser property and gave a com-parison to both OPUS and σ-calculus [Thierbach96].

3.2 Opulus

OPULUS is a simple calculus that denotes

• encapsulation of multiple methods to an object by . . . . . . . . . . . . . . . [E],

• referencing “Self” as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . $,

• sending a message N with Argument F to object E with . . . . . E N : F ,

• referencing the parameter of a message send as . . . . . . . . . . . . . . . . . . .#,

• incremental modification (E is subclassed by F) by . . . . . . . . . . .E +F ,

• declaration of attributes as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N = E,

• declaration of methods (with Name N and body E) as . . . . . . λN = E,

• the empty object by . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ,

• and finally, hiding of methods (N1–Nk) with . . . . . . . . . . .E{N1, . . . ,Nk}.

Table 3.1: Opulus syntax

Its grammar (with start-symbol “Expression”), therefore, is as given in table 3.2on the facing page.

For a more thorough definition please consult [Thierbach96]. For our purposeswe will be satisfied with just one example showing how the calculus works. Con-

Page 65: A Functional Pattern System for Object-Oriented Design

3.3 Opulus within λ-calculus 47

Expression ::= ”[” Expression ”]” | Expression ”+” Expression |Expression ”{” Namelist ”}” |Expression Name ”:” Expression |Name ”=” Expression | ”λ” Name ”=” Expression |”#” | ”$” | ” ” |”(” Expression ”)”

Namelist ::= Name ”,” Name

Name ::= Character { Character }Character ::= ”a” | ”b” | . . . | ”z”

Table 3.2: Opulus BNF

sider an object POINT:

POINT ≡ [x = 7+y = 13+λsetx= [$+x = #]].

It has two attributes x and y and one method setxthat changes the value of x byproducing a new object. The new object is the old object (referenced by $) modifiedby an attribute x that has the value of the setxparameter.

The reduction occurs as follows:POINT setx: 42

→β app(POINT,setx, ,42)

≡ app(λsetx= [$+x = #],setx,x = 7+y = 13+λsetx= ($+x = #),42)

≡ [x = 7+y = 13+λsetx= ($+x = #) +x = 42]

→→η [x = 7+(y = 13+(λsetx= ($+x = #) +x = 42))]

→η [y = 13+(λsetx= ($+x = #) +x = 42))]

The result is a new object with x possesing the value 42. It is one of the distin-guishing features of OPULUS to be able to get rid of the overriden “x = 7” attributeby η-reduction.

3.3 Opulus within λ-calculus

A standard translation of OPULUS into the λ-calculus represents OPULUS termsin a tree structure and uses programmed reduction rules to manipulate thetree [Thierbach96]. The fixpoint combinator Y can then be employed to, e.g., re-peatedly apply β-reduction.

A more efficient translation is achievable

• by recognizing that substitution in OPULUS (e.g., replacing # with the argu-ment value) can be captured with λ-abstraction variable bindings,

Page 66: A Functional Pattern System for Object-Oriented Design

48 3 Calculus comparison

• using a continuation passing style for method calling that might “backtrack”to search in A after B in the case of (A+B) message: ,

• and by directly translating OPULUS terms into structure evaluating func-tions instead of separating term representation and reduction rule applica-tion [Thierbach96].

The complete (fast) translation of OPULUS to the λ-calculus is given in table 3.3(parameter d refers to the self-reference, e to the value of an argument, n to thename of a method, and f to the continuation to be used in case a message must beredirected to the modified operand, e.g., A in A+B).

� $� ≡ self

� #� ≡ arg

� � ≡ Y λy.λd en f.y

� [A]� ≡ λd en f.((λx.xx)� A�) en f

≡ λd.(λx.xx)1� A�� A+B� ≡ λd en f.� B� d en(� A� d en f)

� A M : B� ≡ � A�� �� B��M�� λN = A� ≡ λd en f.� N� ((λself.λarg� A�) d e) f n

�N = A� ≡ λd en f.� N�� A� f n

� A{N}� ≡ λd en f.� N� f (� A� d en f) n

Table 3.3: Translation of Opulus to λ-calculus

It should be noted that η-reductions are not considered anymore and that atranslation for OPUS would not be possible in this manner.

The translation appears straightforward (e.g., message send corresponds tofunction application) but we should pay attention to the fact that OPULUS names(such as method names) must be encoded in the λ-calculus, since it does not sup-port a similar notion. Thierbach translated names to an encoding of numbers in theλ-calculus, because all one really needs is to compare names for equality. One can,alternatively, also think of an extended λ-calculus with δ-reductions for numeralsor names. In our case, however, comparing names adds a complexity factor of O(v),where v is the number of distinct names used in the OPULUS term.

Figures 3.1 and 3.2 on the facing page show an OPULUS term and its mechani-cally2 derived translation to the λ-calculus respectively. Both are screenshots takenof a system generated by CENTAUR [Despeyroux88, Jager et al.91, Centaur92] fromspecifications written by Dirk Thierbach [Thierbach96].

The M and N macros in figure 3.2 on the next page represent the encodings ofOPULUS names.

1An object has itself as an implicit argument.2Some minor modifications have been made by hand to enhance clarity.

Page 67: A Functional Pattern System for Object-Oriented Design

3.4 λ-calculus within Opulus 49

Figure 3.1: Screenshot: OPULUS expression

Figure 3.2: Screenshot: OPULUS expression represented in the λ-calculus

3.4 λ-calculus within Opulus

Again a standard translation is easily obtained by encoding λ-terms into objectswhich understand application and substitution messages [Thierbach96]. The diffi-culties with α-conversions necessary for β-reductions are easily avoided by calcu-lating weak-head-normal-forms only [Field & Harrison88].

A cleverer translation is obtained by exploiting the closure like nature ofobjects. Closures are functions that carry their variable environment withthem [Field & Harrison88]. How can we make use of closures then? It is easy to

Page 68: A Functional Pattern System for Object-Oriented Design

50 3 Calculus comparison

translate the identity function (λx.x) to OPULUS as

[λapp= #].

Then the application to an argument

[λapp= #] app:

yields the argument itself, i.e., .However, we need a more sophisticated approach in the case of λx.λy.x. When

λy.x is applied to a value we need to remember the value of x. This is precisely thecase for a closure which can remember earlier variable bindings. Hence, the resultof applying λx. . . .must be a closure representing the function λy.xbut rememberingthe value of x. A closure, howbeit, is most easily represented by an object storingthe variable binding in its state, ergo

[λapp= [x = #+λapp= ($ x : )]]

represents the function λx.λy.x (the sub-term “$ x : ”retrieves the function bodyvalue from the variable environment). Note that if the lambda term had beenλx.λy.y the variable access of the innermost function body translation would havebeen “#” instead of “$ x : ”. As a result, we need a context dependent translationof parameter access [Thierbach96]. In table 3.4 the parameter context is denotedwith subscript indices.

� λX.B� ≡ λapp=� B�X (3.1)� λX.B�Y ≡ [$+Y = #+λapp=� B�X] (3.2)� L R� ≡ � L� app:� R� (3.3)� L R�Y ≡ � L�Y app:�R�Y (3.4)� X�X ≡ # (3.5)� X�Y ≡ ($ X : ), if X 6≡Y (3.6)� X� ≡ ($ X : ) (3.7)

Table 3.4: Translation of λ-calculus to Opulus

The subscript index is defined and respected by lambda abstraction (rules 3.1and 3.2 of table 3.4) and distributed over application (rules 3.3 and 3.4). If the cur-rent binding variable coincides with the variable to be accessed then the parametersymbol is used (rule 3.5). Otherwise, the variable is accessed via the variable envi-ronment (rules 3.6 and 3.7).

Interestingly, in contrast to the standard translation the fast translation aboveeven allows to dispose the restriction of calculating the weak-head-normal-formonly. Figure 3.3 on the next page shows a lambda expression in a CENTAUR win-dow. Figure 3.4 on the facing page shows the corresponding OPULUS term whichwas derived by automatic translation.

Page 69: A Functional Pattern System for Object-Oriented Design

3.5 Conclusion 51

Figure 3.3: Screenshot: λ-calculus expression

Figure 3.4: Screenshot: λ-calculus expression represented in OPULUS

3.5 Conclusion

It did not come as a surprise that either translation was possible at all but howabout the results of comparing the complexity of performing one calculus in theother? Table 3.5 on the next page summarizes the results for emulating OPULUS inthe λ-calculus and table 3.6 on the following page shows the results for emulating λ-calculus in OPULUS. Ranges of complexity denote the best and worst case, whereasthe average case is always closer to the lower bound.

Let us first regard the standard translation which we did not explicate in thepreceding sections but that we remember as using a straightforward term repre-sentation with associated reduction functions. We note that the derivation of emu-lating the λ-calculus in OPULUS was done within two pages [Thierbach96] resultingin three conceptually easy object definitions all implementing two messages each(substitution and β-reduction). Deriving the opposite standard translation tookmore than three pages and resulted in nine translation rules needing nine construc-tors with nine associated selection functions. In addition, this does not include theemulation of OPULUS names in the λ-calculus.

The difference in verbosity of the two emulations can be explained by two rea-sons:

1. The λ-calculus is somewhat weaker in its basic operations causing the needfor a name comparison emulation and

Page 70: A Functional Pattern System for Object-Oriented Design

52 3 Calculus comparison

2. OPULUS is a somewhat bigger calculus with a larger grammar causing theneed for more (nine compared to three) emulation rules.

On the one hand the size of OPULUS may appear arbitrary given the current ma-turity of object-oriented calculi and one may easily excuse the λ-calculus for itsefforts to emulate such a “clumsy” formalism. On the other hand, OPULUS doesa very good job of capturing the basic mechanisms of class based object-orientedlanguages. Capturing the others calculus’ richness is part of the game and the λ-calculus has obviously a harder job.

The first of the above argument also shows up in the complexity comparison:One full reduction step in OPULUS needs O(nv) steps in the λ-calculus (see table 3.5),whereas v is the number of distinct names in the OPULUS term.

Fast translation Standard translation

Substitution O(1) O(n)

Application O(nv) O(nv)

Redex contraction O(nv) O(nv)

Redex recognition O(1) O(1)–O(nv)

Reduction-step O(nv) O(nv)–O(n2v)†

† It easy to reduce this to O(nv) by combining the functions that search aredex and perform a reduction.

Table 3.5: Complexity of Opulus in the λ-calculus

Fast translation Standard translation

Substitution O(n) O(n)

Redex contraction O(n) O(n)

Redex recognition O(1) O(1)–O(n)

Reduction-step O(n) O(n)

Table 3.6: Complexity of λ-calculus in Opulus

It is interesting to note that the built-in name comparison of OPULUS causesextra work when emulated in the λ-calculus. Yet, it should not be given to muchimportance, since the λ-calculus can easily be extended with δ-reductions that ac-complish the required comparisons with O(1) complexity.

So, abstracting from the name evaluation overhead the two standard transla-tions essentially do not differ (compare table 3.5 and table 3.6). Let us turn to thefast translations: It immediately strikes our attention that redex recognition is O(1)in either translation. This is due to the fact that redexes are directly translated into

Page 71: A Functional Pattern System for Object-Oriented Design

3.5 Conclusion 53

redexes of the other calculus. Besides this exception, there is the usual overhead forname comparisons in the λ-calculus. Notwithstanding, substitution in OPULUS (re-placing $ and #) is just O(1) in the λ-calculus. This is achieved by using β-reduction,i.e., the application of λsel f.λarg.� A� to the values of $ and #, reduces in two(constant) steps.

Substitution of λ-calculus terms in OPULUS has linear complexity, because vari-ables have to be looked up in the variable environment. In an object-oriented pro-gramming language this step is constant too, since it simply requires an attributeaccess or — in the worst case — an access through an indirection table in case of anoverridden attribute.

So, on the one hand substituting λ-calculus terms takes linear time in OPULUSbut on the other hand note that looking up OPULUS methods takes linear time inthe λ-calculus (see row “Application” in table 3.5 on the facing page). There is noequivalent to looking up method names in the λ-calculus which is why there isno such entry in table 3.6 on the preceding page. De facto, these prominent dif-ference in complexity of emulations point out a crucial difference of the underly-ing paradigms: Object-orientation is characterized by the atomicity of looking upa name and performing the corresponding method. Functional programming ischaracterized by having access to function parameters at any place in any nestingdepth.

Overall, the two calculi appear to be almost equal in expressiveness3, sincetheir translations involve linear complexity respectively (excluding the overheadfor comparing names in the λ-calculus). Where differences are present (methodlookup and variable substitution) they point out paradigm fundamentals.

Especially, the fast translations — we might also say native translations, sinceidiomatic constructs were used — demonstrated a very direct way of translatingcalculi constructs: Functions in the λ-calculus could be captured by objects operat-ing as closures. Objects in OPULUS could be represented by λ-terms and selectorfunctions, e.g., an object with the fields a, b, and c —

λs.s a b c

— receives a message (selector) “b” by being applied to λx y z.y, yielding field b.Finally, one may note that fast and standard emulation of the λ-calculus are very

close to each other (see table 3.6 on the facing page). This may hint to the fact thatobjects are a good abstraction for simulation. The direct simulation of λ-calculuswas almost as efficient as the version that exploited native features of OPULUS.

Also, we may recall that an object-oriented languages would not need linearcomplexity to look up λ-variables in the environment. In contrast, an implementa-tion of OPULUS in a functional programming language will still need linear com-plexity to look up names, unless further efforts like introducing method lookuparrays or similar machinery is introduced.

Before we derive further conclusions from the translation complexities foundhere, a small investigation is in order. Apparently, translation complexities depend

3You may want to consider [Thierbach96] for a full account including η-reductions.

Page 72: A Functional Pattern System for Object-Oriented Design

54 3 Calculus comparison

on the translation strategies. What if a better scheme, say for translating OPU-LUS into the λ-calculus, could be found? Is it not possible that one calculus is stillsuperior but we have not found the right translation scheme yet? The fact thatboth translations exhibit mostly linear characteristics gives us confidence that wefound optimal translations and future translation schemes will not affect the over-all result. But what about sub-linear translations? Are they possible? Consider, atranslation from a RAM model with just numerical addition into one that featuresmultiplication also. When addition loops are converted into multiplications thiscould result into a sub-linear translation. However, the reverse translation wouldobviously be super-linear. If one our translations could be optimized to a sub-lineartranslation, the other one could not possibly have linear characteristics right now.Proof by false assumption: Assume the forth translation to be sub-linear and theback translation to be linear. With repeated back and forth translations a programcould be made infinitely faster. As the conclusion is obviously false, so must be theassumption [Thierbach97].

In summary, it is certainly not a reverse logic approach to aim at subsumingfunctions with objects. While the other way round is not impossible either it ap-pears less natural. Moreover, when leaving the level of calculi, it appears more rea-sonable to subsume reduction semantics with state than to “ruin” the basic assump-tion of referential transparency with the introduction of stateful objects. Note, how-ever, the striking correspondence of some well-known combinators with impera-tive statements: S≡ “:= ”, K≡ “const ”, I ≡ “goto ”, and CB≡ “; ” [Hudak89], sug-gesting how to emulate imperative features in a declarative language. I will con-tinue the discussion about the conflict between state and referential transparencyin section 4.1.1 on the next page and section 4.2.1 on page 57.

Page 73: A Functional Pattern System for Object-Oriented Design

55

4 Conflict & Cohabitance

When two worlds collide the question is whether they cancel out or enlighten each other.– me

Now that we know the foundations of both functional and object-orientedprogramming and convinced ourselves that subsuming functional pro-gramming with an object-oriented language works fine at the calculus

level it is time to refocus our overall goal. Part II starting at page 85 is going topresent functional concepts made amenable to object-oriented design by captur-ing them in design patterns. But which functional concepts are appropriate? Thischapter discusses functional and object-oriented concepts that seem to be at oddswith each other (section 4.1) and shows a path of possible integration (section 4.2on page 57) partly reconciling conflicts discovered in the following section.

4.1 Conflict

Although, the preceding chapter gave us confidence that an integration of func-tional concepts into an object-oriented language is viable it might be the case thatincommensurable properties prevent an integration at the programming languagelevel. Indeed, the following sections reveal some immediate oppositions and re-dundancies.

4.1.1 Oppositions

This section discusses diametral oppositions of the functional and the object-oriented paradigm.

4.1.1.1 Semantic model

As elaborated in section 1.2.2 on page 11 functional programming is founded onreduction semantics, i.e., excludes the presence of side-effects. Object-orientation,however, relies on stateful objects (see section 2.2.6 on page 37). This is a seriousconflict. Abandoning either reduction semantics or stateful objects seems to de-stroy one of the paradigm foundation piles respectively. Actually, the integrationof functional and object-oriented features amounts to combining both declarativeand algorithmic language paradigms (see figure 4.1 on the next page).

Page 74: A Functional Pattern System for Object-Oriented Design

56 4 Conflict & Cohabitance

proceduralobject-orientedfunctionallogic

declarative algorithmic

Programming Paradigms

Figure 4.1: Classification of programming paradigms

4.1.1.2 Decomposition

There is a fundamental dichotomy between the decomposition strategies in func-tional and object-oriented programming on two levels: On a large scale we mustdecide whether to decompose a software system into functions (section 1.2.1 onpage 10) or into objects (section 2.2.1 on page 32) [Meyer88]. On a small scale wehave to choose between data abstraction (section 1.2.1 on page 10) or procedural ab-straction (section 2.2.1 on page 32) [Cook90]. Either we package data constructorsand use functions that dispatch on those or we package functions and distributethem to their respective constructors.

4.1.1.3 Evaluation

In section 1.2.2 on page 11 and section 1.2.4 on page 14 we have learned that normal-order reduction or lazy evaluation has desirable properties. This is the case as longas side-effects are not allowed. Side-effects do not fit with lazy evaluation withits unintuitive and data dependent evaluation order. For this reason, functionallanguages with side-effects (e.g., ML or UFO) use eager evaluation. Then, side-effects of the function arguments will appear prior to that of the function itself.

It has also been said that laziness can conflict with dynamic binding. In orderto dynamically bind on an argument it needs to be evaluated anyway without anyoption of delay [Sargeant95].

4.1.1.4 Encapsulation

Data abstraction, i.e., exposing data constructors to functions of an abstractdatatype works well with pattern matching (section 1.2.5 on page 16) but is at oddswith encapsulation (section 2.2.2 on page 32). This is not an issue of object en-capsulation but has been recognized in the functional programming community aswell [Wadler87]. A function that uses pattern matching for a datatype is exposedto its representation — as it accesses its constructors — and is subject to changewhenever the datatype has to be changed. There has been a number of propos-als how to avoid this drawback, each trying to improve on the weaknesses of theformer [Wadler87, Thompson89, Burton & Cameron94, Gostanza et al.96].

Page 75: A Functional Pattern System for Object-Oriented Design

4.2 Cohabitance 57

4.1.2 Redundancy

It is obvious that opposing concepts exclude their integration. All the same, it isof no use to integrate concepts that are redundant to each other. Concepts mustcomplement each other otherwise a software solution will become an incoherentmix of styles which is hard to understand but does not justify its diversity withcorresponding properties.

4.1.2.1 Parameterization

Higher-order functions (see section 1.2.3 on page 12) and inheritance (see sec-tion 2.2.3 on page 33) can both be used for behavior parameterization [Kuhne95b].The Template Method pattern uses subclassing to achieve behavior parameteriza-tion akin to higher-order functions. Unless the two mechanism do not yield soft-ware solutions with different properties and no further complement of one to theother can be found, one should be dismissed.

4.1.2.2 Dispatch

Pattern matching (section 1.2.5 on page 16) and dynamic binding (section 2.2.5 onpage 35) can both be used to decompose a function into partial definitions and toselect the appropriate portion when executing the function. Both mechanisms di-vide a function according to the constructors it operates on. While pattern matchingkeeps the patterns at one place, dynamic binding distributes it to the constructors.It should be clear whether two code selection mechanisms are needed.

4.2 Cohabitance

If we literally agreed to all the arguments made in the previous sections it would betime to abandon any paradigm integration at all. Luckily, many points made abovethat at first appear excluding are in fact complementing. In the following, I proposehow to subsume and integrate functional concepts into those of the object-orientedparadigm. The last section goes beyond feasibility and presents synergistic effectsbetween the two paradigms.

4.2.1 Subsumption

Subsuming functional concepts into object-oriented ones means to find ways toexpress them without changing an object-oriented language.

4.2.1.1 Pure functions

Even without motivation from functional languages it has been suggested to useside-effect free functions only [Meyer88]. The so-called command-query separationprinciple prescribes to use side-effects for commands only. For instance the code,

Page 76: A Functional Pattern System for Object-Oriented Design

58 4 Conflict & Cohabitance

a:=io.nextChar;b:=io.nextChar;

should be written as a:=io.lastChar;io.nextChar;b:=io.lastChar;io.nextChar;

because the latter version clearly differentiates between reading input (query) andadvancing the input stream (command). Thus, it is possible to assign a charac-ter from the input stream to two variables without accidently advancing the inputstream in between.

Concerning the side-effects of functions one might make a difference betweenabstract state and concrete state of objects. The latter may change without affectingthe former. For instance, a complex number may have two states of representation:A Polar coordinates state for fast multiplication and a Cartesian coordinates statefor fast addition. A complex number object may autonomously switch betweenthese concrete states without affecting the abstract state, i.e., its complex numbervalue. Also, some side-effects that do not affect the behavior of a system can beallowed too. The protocoling of executed commands [Dosch95] is a typical exampleof harmless side-effects.

As a result, the command-query separation principle reconciles state with re-duction semantics and also state with lazy evaluation. The reduction semanticsof functional languages is embedded into an imperative environment by restrict-ing functions to query functions only. Especially, with the interpretation of objectsbeing sub-states of a system [Kuhne96a] the configuration of side-effect free func-tions operating on system states is close to the design of John Backus’ AST1 sys-tem [Backus78] with its main-computations between system states.

Furthermore, when functions do not cause state changes and also are not influ-enced by state changes (see section 4.2.2.2 on the facing page) they may be executedin any order. This opens up the possibility of lazy evaluation again.

4.2.1.2 Dispatch

We have seen that pattern matching is responsible for many subtleties (section 1.3.2on page 21) and problems (section 4.1.2.2 on the preceding page) without a real im-pact on software engineering properties (section 1.2.5 on page 16). We therefore de-cide to abandon pattern matching in favor of dynamic binding which is capable ofachieving the same but in addition provides further opportunities (see section 2.2.5on page 35).

I admit it is unfortunate to loose the nice syntax of pattern matching and to beforced to work with several classes (representing constructors) in order to deal withone function. Notwithstanding, the question of how to present and manipulate afunction seems to be more a matter of tools rather than language [Kuhne96b]. Al-though functions are distributed over objects they can be presented to the program-mer as a single definition with an appropriate tool (see the epilogue on page 261for a further discussion).

1Applicative-State-Transition

Page 77: A Functional Pattern System for Object-Oriented Design

4.2 Cohabitance 59

4.2.2 Integration

In contrast to subsumption, integration demands for adaptions of the host languagein order to encompass functional concepts.

4.2.2.1 Evaluation

We already regained functions for lazy evaluation (section 4.2.1.1 on page 57)but may also want to have lazy object state semantics too. Otherwise, internalobject state might be computed without being requested at all. One approachto achieve lazy effects relies on a refined state monad concept [Launchbury93,Launchbury & Jones94]. Another opportunity is to use models from concurrentobject-oriented languages and to propose lazy message mailboxes for objects. Inthe context of this dissertation we will be satisfied with lazy functions only, though.

A short note is in order regarding the remark that dynamic binding does not co-habit with lazy evaluation (see section 4.1.1.3 on page 56). First, dynamic bindingonly requires to know the type the receiver which amounts to a partial evaluationonly. Second, function application is strict in its first argument anyway. One mightevaluate arguments first but eventually the function to be applied must be eval-uated to a function abstraction. Therefore, dynamic binding poses no additionalrequirements that would render lazy evaluation useless.

4.2.2.2 Closures

Section 3.4 on page 49 demonstrated how to use objects as closures in order to im-plement functions with lexical scoping. Closures capture the values of variables attheir declaration and/or application environment as opposed to the point of exe-cution. That is why, one can safely use closures in conjunction with lazy evaluation(see section 4.2.1.1 on page 57). Once applied to values closures do not depend onstate changes anymore.

Why do closures need to be integrated rather than subsumed? Most object-oriented languages do not allow free functions2 and require class definition over-head for closures which especially gets worse when currying should be supported.Moreover, few object-oriented languages (e.g., SMALLTALK and JAVA) providemechanisms for anonymous closures (e.g., blocks [Goldberg & Robson83] and in-ner classes [Sun97] respectively).

4.2.3 Synergy

Synergy occurs when two or more concepts complement each other in a way thatresults in properties that cannot be described by the sum of the separated concepts.

2C++ being an exception but then its functions are too weak to support closures.

Page 78: A Functional Pattern System for Object-Oriented Design

60 4 Conflict & Cohabitance

4.2.3.1 Parameterization

Section 4.1.2 on page 57 postulated to either surrender higher-order functions orinheritance unless a good reason not to do so can be found. This section arguesthat both concepts are necessary and even amplify each other. We will proceedwith an example involving a cook who accepts recipes in order to prepare dishes.Written mathematically: Cook⊕ Recipe⇒ Dish. We will try several instantiationsfor the ⊕-operator, i.e.,

⊕= object responsibility

⊕= single-, multiple-, repeated-inheritance

⊕= parameterization

We can evaluate the resulting solutions by asking a set of questions:

Category Question

SIMPLICITY What is the number of classes and the nature of class rela-tions needed?

FLEXIBILITY How flexible are we in adding new cooks and recipes?Is it possible to adapt cooks?

SCALABILITY How does the model scale up when we add new cooks andrecipes?

SELECTABILITY What support do we have in selecting cooks and recipes?

REUSABILITY Can we reuse cooks and recipes for other purposes?

ENCAPSULATION Which degree of privacy is maintained in a cook and recipecombination?

Table 4.1: Criteria to evaluate cook and recipe combination

We will now try each of the above alternatives for the ⊕ combinator. We assessthe resulting solutions by referring to the above criteria by a SMALL CAPS typeset-ting.

prepare2 : Dish2prepare1 : Dish1

Cook

Figure 4.2: Cooking withobject responsibility

Object responsibility The first natural approach is toconsider recipes as the cook’s knowledge and, thus, as-sign them to a cook as methods (see figure 4.2). This isthe most SIMPLE model but it does not offer much FLEX-IBILITY: If the Cook does not know how to prepare ourfavorite dish, we are lost. There is no way to extend theavailable recipes except by changing cook itself. Sub-classing Cook for cooks with more recipes is akin to thealternative discussed in the next paragraph. Another

Page 79: A Functional Pattern System for Object-Oriented Design

4.2 Cohabitance 61

drawback is that we cannot REUSE the recipes for other purposes, e.g., nutritionanalysis. Finally, we have to SELECT recipes by name (prepare1 , prepare2 , . . . ), i.e.,we cannot exploit dynamic binding for recipe selection. Consequently, code thatdemands a Cook to prepare a dish is sensitive to the addition of new dishes.

Cook2Cook1

recipe : Dish2recipe : Dish1

recipe : Dish

Cook

prepare : Dish

Figure 4.3: Cooking with single inheritance

Single inheritance Creating acook subclass for each new recipeallows to add recipes withoutchanging existing cooks (see fig-ure 4.3; prepare is a templatemethod using recipe as a hookmethod, hence this design fol-lows the design pattern TemplateMethod [Gamma et al.94]). How-ever, the price to pay for this FLEX-IBILITY is a lot of traffic in the classname space for all these specializedcooks especially when we considermultiple cooks (e.g., French andItalian chefs de cuisine). Neverthe-

less, the SELECTABILITY problem is coincidentally solved too. Now we can sayprepare to Cook and depending on the actual subclass (Cook1 or Cook2 ) we getthe corresponding dish. Code can rely on the abstract recipe method and is notinvalidated by the addition of new recipes. Unfortunately, we still cannot REUSErecipes for other purposes.

Cook1

Recipe1

recipe : Dish1

Cook2

Recipe2

recipe : Dish2

recipe : Dish

Cook

prepare : Dish

Figure 4.4: Cooking with multiple inheritance

Multiple inheritance Theonly way to make the recipesreusable is to make them en-tities (classes) of their own.Using inheritance this leadsto multiple inheritance (seefigure 4.4). Now we canREUSE recipes, SELECT recipeswith dynamic binding, andare FLEXIBLE in adding newrecipes. But the model getsmore COMPLEX, because of theinheritance relations. SCAL-ABILITY suffers by the factthat each new recipe demandsfor an additional combinatorclass. A new cook multipliesthe inheritance relationships.

Page 80: A Functional Pattern System for Object-Oriented Design

62 4 Conflict & Cohabitance

Finally we experience a loss of ENCAPSULATION: Not only cook and recipes breakthe encapsulation of each other (as already the case with single inheritance) but thecombinator classes break the encapsulation of both cook and recipes. A change tothe implementation of Recipe2 may produce a conflict with the implementation ofCook (e.g., through the use of a common variable name).

Recipe1

recipe : Dish1

Recipe2

recipe : Dish2

prepare1 : Dish1

AllPurposeCook

prepare2 : Dish2

prepare : Dish

Cook

prepare : Dish

Figure 4.5: Cooking with repeated inheritance

Repeated inheritance Thefinal try involving inheri-tance aims at saving combi-nator classes, hence, regain-ing SIMPLICITY and SCALA-BILITY. The idea is to foldall combinator classes fromthe solution above to a sin-gle combinator class (see fig-ure 4.5). This combina-tor class (AllPurposeCook ) re-peatedly inherits Cook anddistributes each inheritancepath and recipe to a uniquerecipe name [Meyer92]. Wegained a little SIMPLICITYbut did not really gain onSCALABILITY, since for manyrecipes we get big combina-tor classes with a lot of re-naming needed inside. More-over, adding a Cook meansadding a new big combinator. As a serious drawback we lost on SELECTABILITY.We cannot exploit late binding for choosing cooks or recipes anymore. Finally, EN-CAPSULATION problems represent the worst of all solutions.

Parameterization Clearly, inheritance does not provide a satisfactory answer tothe combination of cooks with recipes. Now let us take a fresh look at the prob-lem and regard cooks as a higher-order function parameterized with recipes. NowCook “takes-a3” Recipe (see figure 4.6 on the next page). This is a client/serverrelationship, hence, ENCAPSULATION is provided. The Cook asks recipes to applythemselves and depending on the actual Recipe1 the corresponding dish is pre-pared. Ergo, SELECTABILITY is also enabled. All components are REUSABLE andthe model SCALES up without problems. The model also is fairly SIMPLE especiallysince one can forget about the (abstract) inheritance relationship as a model user. Itonly needs to be considered when adding new recipes which must simply containan inherit clause in their class definition.

3“uses” or accepts as a parameter.

Page 81: A Functional Pattern System for Object-Oriented Design

4.2 Cohabitance 63

apply : Dish2

Recipe2

apply : Dish1

Recipe1

Cook

prepare(recipe : Recipe) : Dish apply : Dish

Recipe

Figure 4.6: Cooking with parameterization

Now is the above model the solution to all problems? Unfortunately not, asit is not as FLEXIBLE as the inheritance models. Why is this so? Remember, thecriterion for FLEXIBILITY (see table 4.1 on page 60) also asked for the ADAPTABILITYof cooks: Let us assume we receive an exquisite recipe requiring a special procedureof evaporation. Our standard cook is not capable of that so we need an extendedversion. The inheritance solutions have no difficulties with that requirement, forthey can add the extension in the subclass that combines cook and recipe. In apure parameterization model a redesign would require to change the parameter ofCook to something like CookAdapter , provide a dummy adapter for old recipes,and develop a special adapter — containing the cooking extension — for the newrecipe.

Parameterization and Inheritance To reconcile FLEXIBILITY with parameteriza-tion one can simply combine inheritance and parameterization in their appropriateplaces (see figure 4.7).

Cook*

prepare(recipe : Recipe) : Dishevaporate : Something apply : Dish2

Recipe2

Cook

apply : Dish1

Recipe1

prepare(recipe : Recipe) : Dish

Recipe

apply : Dish

Figure 4.7: Cooking with synergy

Page 82: A Functional Pattern System for Object-Oriented Design

64 4 Conflict & Cohabitance

In this setup both parameterization and inheritance contribute to a solution thatyields optimal criteria evaluation except for a little loss of ENCAPSULATION be-tween the cook classes. Yet, this appears inevitable considering the amount ofFLEXIBILITY possible with regard to a cook adaption. Note that using a pure pa-rameterization model it is not an option to simply add a new cook to the systemsince that would not allow subtyping between cooks, that is, we cannot reuse codethat assumes the existence of one cook type only.

What are the lessons to be learned from this excursion? Parameterization is su-perior to inheritance to model behavior variability. Although, it is not true — asoften claimed [Pree94] — that inheritance allows for static combinations only, it in-volves severe encapsulation problems between the entities to combine. Note that itis easy to apply the arguments for cooks and recipes to other domains. For instance,let cook be an iterator and recipes iteration actions. Indeed, even the repeated in-heritance solution has been suggested for combining iterators with several iterationactions [Meyer94b]. Parameterization achieves its complexity and encapsulationadvantages through the principle of black-box reuse [Johnson & Foote88, Griss95],that is, to reuse components as is without further modification.

Nonetheless, parameterization alone can reduce flexibility. The strength of in-heritance, i.e., to adapt a class in unforeseen ways, is also called “white-box reuse”.White-box reuse breaches encapsulation and requires internal knowledge of thereused component but is a powerful supplement to black-box reuse due to its flex-ibility.

Of course, inheritance has many other useful purposes we did not mention here(see section 2.3.2.3 on page 42) that justifies its coexistence with parameterization.Parameterization in turn has more to offer than demonstrated here. This discus-sion is continued in chapter 7 on page 93 mentioning, among other issues, whyrestricted flexibility can also be of advantage.

4.2.3.2 Decomposition

Data abstraction We already mentioned the fundamental dichotomy between afunctional and an object-oriented decomposition (see section 4.1.1.2 on page 56).Today there is no doubt about the advantages of structuring a software system ac-cording to the business objects it handles. Enormous grow rates for object-orientedsystems, languages and publications, give testimony to the historical win of theobject-oriented paradigm since the mid eighties over structured design and analy-sis [Quibeldey-Cirkel94, Shan95]. Apparently, there is no need for functions oper-ating on data, i.e., data abstraction, since everything can be — with advantage —expressed with data annotated with functions, i.e., procedural abstraction.

Unfortunately, it is not as easy like this. The decision for data abstraction ofprocedural abstraction is an inevitable trade-off. It just happens to be the case thatchoosing procedural abstraction more often results in better future maintainability.Why is this? Consider table 4.2 on the next page.

The first row contains data constructors while the first column contains opera-tions. The intersection cells contain operation implementations for the correspond-

Page 83: A Functional Pattern System for Object-Oriented Design

4.2 Cohabitance 65

Line Rectangle Circle

move(x, y) move twopoints

move fourpoints

move center

scale(f) scale onepoint

scale onepoint

scale radius

display draw line draw fourlines

draw circle

Table 4.2: Decomposition matrix

ing data constructor. If we slice table 4.2 into columns we obtain an object-orienteddecomposition. Each constructor packages its operation implementations. If weslice table 4.2 into rows we obtain a functional decomposition. Each function pack-ages the implementations for all constructors.

What happens when we add a new constructor (e.g., Rhombus) to table 4.2 repre-senting our our software system? This is the case where functional decompositionrequires to modify all functions in order to account for the new constructor (see ta-ble 4.3). It does not matter whether we need to add a new function pattern or haveto extent a type switch. The crucial point is that all functions must be changed andthat they might be spread over the whole software system hiding in unexpectedregions. This is the case where the object-oriented decomposition excels. All thatneeds to be done is to add another object to the system supporting all requiredoperations.

Adding

constructor function

Functional open allfunctions

local functionadditionDecomposition

Object-oriented local objectaddition

open all objects

Table 4.3: Sensitivity of decomposition types

Now, what happens when we add a new function (e.g., rotate(a)) to table 4.2?The object-oriented decomposition requires to open all objects and add the opera-

Page 84: A Functional Pattern System for Object-Oriented Design

66 4 Conflict & Cohabitance

tion implementation (see table 4.3 on the preceding page). At least, no client codehas to be modified. Still, we must perform changes at distributed locations and pos-sibly re-testing (→ regression test) of objects becomes necessary. Furthermore, op-erations will accumulate at object interfaces which become bloated sooner or later.Is display an operation an object should support? Or should display be a functionof an external view component whose exchange would allow for eased manage-ment of changing presentation style? The addition of a function is the case wherefunctional decomposition excels. Only a local addition of a function covering allconstructors is necessary.

Two reasons speak for preferring the object-oriented decomposition. First, itis more likely to expect the addition of constructor. A software system can be re-garded as a competence provider. Quite often one wants to expand the compe-tence to more data (e.g., handle more graphical objects) rather than expanding thecompetences (adding yet another user interaction). Second, extending objects withoperations (monotonously) does not affect clients. Changing data in a functionaldecomposition, on the contrary, involves changing client code at many differentplaces.

Nevertheless, there are opportunities where a functional decomposition paysoff. For instance, you might want to experiment with language tools working onan abstract syntax representation. Then the language will not change, that is, theconstructors are fixed, but often new operations will be defined on the abstractsyntax tree. Here, the functional decomposition is better suited as it, in addition,keeps individual operations — which are the focus in this scenario — at a singleplace rather than distributing them over abstract syntax nodes. This discussion iscontinued in chapter 12 on page 201 that combines functional decomposition withlocal semantic functions.

In summary, there are reasons for both object-oriented and functional decom-position. One accounts for data- and the other for operational extensions. It ismy understanding of Ivar Jacobson’s control objects [Jacobson et al.94] that these ac-count for operational extensions. Jacobson also features entity objects which couldbe — using a dogmatic object-oriented view — extended to support any requiredfunctionality. However, a division of functionality into intrinsic object properties(e.g., a circle’s center and radius) and extrinsic properties (e.g., displaying a circleor calculating π by stochastically dropping a needle onto a circle) results in a systemthat features hot spots for extending both data and operations.

Part III beginning at page 233 will discuss a third option to interpret table 4.2 onthe preceding page, elegantly escaping the dichotomy.

Structured programming While it appears worthwhile to retain functions — out-side of class definitions — there is further motivation caused by the need to struc-ture functions. It has been argued that functional decomposition techniques arestill useful for the decomposition of methods [Henderson & Constantin91]. Fur-thermore, functional decomposition techniques should also be applied to extractmethods from objects. Consider the simulation of planes. During take-off a lot of

Page 85: A Functional Pattern System for Object-Oriented Design

4.2 Cohabitance 67

management is needed to control landing gear, throttle, vertical rudder, etc. Thisis best managed by a dedicated takeOff function that abstracts from individual ac-tions required. In case we also want to handle helicopters we may simply altertakeOff adding rotor adjustments etc. Other code remains unchanged since it relieson a take-off abstraction. With this view, takeOff is a business-transaction [Coplien92]that orchestrates multiple objects to achieve an operational effect. It is not a directproperty or capability of an object. It is true that it is perfectly natural to createan abstraction FlyingObject that supports takeOff as a method. In this case objectand function abstraction just happily coincide. But what about checkPlane? Is it afeature of planes or of service personnel? Is the presentation (view) of an object itsresponsibility or an external function?

ShaftPiece

Collision

interfacedependency

Piece

collision(s : Shaft)

interfacedependency

Shaft

representationdependency

Figure 4.8: Restructured dependencies

Is it the responsibility of aTetris piece or the shaft to de-tect collisions? In the latter ex-ample with either choice a rep-resentation dependency wouldbe created (see upper half offigure 4.8). Representationchanges to the class containingthe collision method would af-fect the method’s implementa-tion and interface changes tothe other class would require toadapt the former. Ergo, a nat-ural solution would be a freecollision function belonging toneither Piece nor Shaft (seelower half of figure 4.8). In thatcase changes to piece or shaftrepresentation do not affect thecollision function and interfacechanges are not propagated be-yond the collision function. Ineither case a potential ripple ef-fect of changes through the sys-tem is prevented. Object-oriented purists might invent a Part object abstraction thatwould feature a collision method and let Piece , Shaft , or both inherit from it. Justin order to avoid a free function (Collision ) the resulting design would posses thenatural coincidence of object and function abstraction as in the takeOff case aboveand, furthermore, would have worse properties than that of figure 4.8. If Part isused for other function abstractions and even holds data then a restructuring ofone function abstraction affects the others and may require the data hierarchy tobe changed. This should not happen and would be the result of forcing functionabstractions into a pure object-oriented view.

Page 86: A Functional Pattern System for Object-Oriented Design

68 4 Conflict & Cohabitance

4.3 Conclusion

Although a fruitful integration of functional concepts into object-oriented program-ming appeared to be impossible first (section 4.1 on page 55) I demonstrated themost concepts to be complementary rather then excluding (section 4.2 on page 57).

We retained stateful objects for real world modeling without impedance mis-match, efficiency, and for maintenence reasons.

“Pure languages ease change by making manifest the data upon which eachoperation depends. But, sometimes, a seemingly small change may require aprogram in a pure language to be extensively restructured, when judicious useof an impure feature may obtain the same effect by altering a mere handful oflines. [Wadler92]” – Philip Wadler

Nevertheless, the virtues of referential transparency are still available due to thecommand-query separation principle.

We have seen that a restriction to a pure functional or object-oriented de-composition leads to unsatisfactory results in either case. Both object-orientedor functional decomposition can be appropriate depending whether the objectsor the functions (business-transactions) are the more stable concept. After aninitial repulsion of any function which is not a object method it is time to re-integrate free functions into object-oriented design again. A move towards thisdirection are so-called command objects, e.g., used for application callback func-tions [Meyer88, Gamma et al.94]. Still, these are not acknowledged as functionsbut as representations of actions to be undone or protocoled [Meyer88].

Regarding the separation between subsumption (section 4.2.1 on page 57) andintegration (section 4.2.2 on page 59) it should be noted that the borderline be-tween is somewhat arbitrary. One may also claim that objects subsume functionswithout further integration needed or that side-effect free functions and value se-mantics need further integration. With the assessment that stateful programmingsubsumes functional programming and objects subsume values I meant the op-portunity to partly renounce side-effects by programmer discipline. Of course, it isacknowledged that language support for controlling side-effects is highly desirable(see section 14.5 on page 243).

In conclusion, it appears quite reasonable and promising to integrate two seem-ingly contradicting paradigms. However, someone using such a dual-paradigmapproach needs guidance when to choose which alternative. Figuratively speaking,when to choose the declarative or the procedural branch of figure 4.1 on page 56. Itis the intent of the pattern system presented in part II starting at page 85 to providea base of arguments for a decision.

Page 87: A Functional Pattern System for Object-Oriented Design

69

5 Design Patterns

Once is an event, twice is an incident, thrice it’s a pattern.– Jerry Weinberg

The notion of a software design pattern is not formally defined. Morethan two years after design patterns have been popularized in 1994 thereare still argues about their correct definition and their meaning for soft-

ware development [Gabriel96]. In order to define the role of design patterns in thecontext of this dissertation I provide an introductory definition starting with ex-amples from everyday life (section 5.1). After considering the origins of patterns(section 5.2 on page 74) I mention some of the promises to be expected from designpatterns (section 5.3 on page 76). This chapter concludes with an explanation ofthe design pattern format (section 5.4 on page 76) and a description of the diagramnotation (section 5.5 on page 79) both to be used in part II beginning at page 85.

5.1 Definition

Patterns are ubiquitous. Most often they are not recognized and less often they arecaptured by a literate form, but still they exist. This is true of the everyday meaningof the word “pattern”, but also, and more importantly, it is also true for a specialmeaning first described by the architect Christopher Alexander:

“Each pattern is a three-part rule, which expresses a relation between acertain context, a problem, and a solution [Alexander79].”

– Christopher Alexander

However, it is the nature of patterns to recur, so let us simplify the above to:

A pattern is a recurring solution to a problem in a context.

That is why patterns are ubiquitous. People, but also the mechanisms of nature,repeatedly apply the same solutions to problems that occur in a certain context.

5.1.1 A gentle introduction

Let us assume the context of writing a piece of music for the mass market. We areconfronted with conflicting forces:

Page 88: A Functional Pattern System for Object-Oriented Design

70 5 Design Patterns

• Easy listening. For quick reception and lasting pleasure we need a catchingtheme that is often repeated.

• Boring monotony. Simple repetition of the same stuff will turn people off andwe will loose their interest.

A solution to these forces that has been applied again and again by composers allover the world is to use two basic themes that are repeated in turn and throw ina special theme near the end of the song. If we name the themes A, B, and C, thesuccessful composition pattern is depicted by figure 5.1.

A B A C A B

fade o

ut

calm

down

wake u

p

conso

lidate

enter

tain

intro

duce

Figure 5.1: A composer’s pattern

Using this pattern has several consequences:

• Familiarity. The listener is smoothly introduced into our musical ideas andcan learn the themes due to their repetition. After the exiting change withspecial theme “C” we calm him down again.

• Keep awake. At the point when the listener might think there is no new stuffto discover we surprise him with the more exiting “C” theme.

• Regularity. If we build all our songs according to this knitting pattern the lis-tener will recognize it in the long run and be bored in spite of the surprisingvariation of repetition.

This description does not aim at completeness. It merely attempts to explain thepattern concept with everyday notions. If we travel a bit further down this road wecan see how patterns may collaborate with each other. Let us imagine we composeda song according to the “ABACAB” pattern but aim at a very dramatic coda. Wemay use another item from a composer’s toolbox that can tentatively be called “OneNote Up”. It is another pattern resolving a different set of forces:

• Keep attention. Listeners accustomed to the “ABACAB” pattern may loosetheir interest towards the end of the song.

• Do not confuse. Introducing yet another theme will overstrain the listener.

Page 89: A Functional Pattern System for Object-Oriented Design

5.1 Definition 71

We resolve the forces by repeating the last theme again but this time with all notesincreased by one note. As consequences we have:

• Cosy feeling. The listener recognizes the variation to the theme and is not re-quired to learn a new one.

• Boredom prevented. The added note creates a dramatic effect that keeps thelistener tuned.

Note, how the solution of the first pattern provides the context for the second pat-tern. This is how true pattern languages work. We can think of single patterns aselements of a grammar that, by its production rules, defines a language which tellus how to weave patterns together. In contrast, a set of patterns that provides use-ful collaborations but does not provide guidance how to choose patterns from alower level when higher level decisions have been made is called a pattern system.

Also, we have seen how the names of patterns like “ABACAB” and “One NoteUp” establish a vocabulary to discuss the design of songs. As language can leverour level of thinking we may now design songs on a higher level. In fact, patternswork as chunks and instead of handling only three simple things at once we maydeal with the same amount of high level chunks.

Let us recapitulate by assuring that “pattern” is indeed a good name for theabove described notion. Table 5.1 features everyday meanings on the left hand sideand their interpretation in our context on the right hand side:

Facet1 Meaning

pattern recognition; in-ducing attention by re-peated occurrence.

Patterns are not invented, they are discovered.

form or model proposedfor imitation.

Resolving forces in a successful way is worthwhileto be copied.

knitting pattern; formor style in literary or mu-sical composition.

A pattern description suggests to arrange existingparticipants in a certain style rather than prescribingthe participants.

dress pattern; a con-struction plan.

Pattern descriptions also tell you how to arrive at thesolution described.

configuration; like frost-or a wallpaper patterns.

The participants in a pattern solution form a struc-ture by their relations.

behavior pattern; com-plex of individual orgroup characteristics.

Each pattern participant plays a characteristic role inthe solution.

Table 5.1: Facets of “pattern”

Page 90: A Functional Pattern System for Object-Oriented Design

72 5 Design Patterns

With regard to patterns we may have several level of consciousness.

1. We are not aware of a recurring solution.

2. We know some “tricks” that jump to our help in some situations, but we can-not fully capture what the real magic is, neither can we determine the charac-ter of the applicable situations.

3. We exactly know when we can apply a recurring solution.

4. We know when to apply the solution and are aware of all consequences in-volved.

5. We are able to linearize our knowledge into a literate form such that otherscan learn from our experience.

The last level of course refers to design pattern descriptions. Considerable in-sight is necessary to write a comprehensible pattern description and the result canbe considered to be novel work, although the main content on the contrary mustbe well-known to work. Of course, it is possible to use a pattern description style(see, e.g., section 5.4 on page 76) to document original solutions, i.e., that did notyet recur. It has been suggested to call these “Proto-Patterns”.

5.1.2 Software Patterns

Patterns are useful in the context of software development, since software engi-neering is not a science yet. There is no straight way of creating an optimal systemand probably never will be. This is the case for patterns. They are useful whensomething creative has to be build and no hard and fast rules apply. Patterns are tobe applied by humans not by (today’s) computers.

“. . .software designers are in a similar position to architects and civil en-gineers, particularly those concerned with the design of large heterogeneousconstructions, such as towns and industrial plants. It therefore seems naturalthat we should turn to these subjects for ideas about how to attack the designproblem. As one single example of such a source of ideas I would like to mentionChristopher Alexander: Notes on the Synthesis of Form [Alexander64].”

– Peter Naur

“Subsystems created by the composition of objects or interaction machinesdo not conform to any accepted notion of structure and are very hard to charac-terize, though they do determine subsystems that exhibit reusable regularitiesof interface behavior. The term pattern is used to denote reusable regularitiesof behavior exhibited by interactive subsystems created by the composition ofinteraction primitives. [Wegner95].” – Peter Wegner

1These are partly taken from a dictionary [Harkavy & et al.94].

Page 91: A Functional Pattern System for Object-Oriented Design

5.1 Definition 73

“A pattern is a piece of literature that describes a design problem and ageneral solution for the problem in a particular context.” – James O. Coplien

As yet, no methodology (e.g., [Booch94]) offers a means to describe designs as ap-propriately as patterns can do2.

“The problem specification and results for sorting can be formally spec-ified, while the problem class and effects of interactive patterns cannot gen-erally be formalized and require a sometimes complex qualitative descrip-tion. [Wegner95]”

– Peter Wegner

Patterns tie a design solution to its driving forces and its related consequences.Why is it important to capture the forces too? First, this helps to decide whether apattern is applicable or not.

escher-space-filling.ps

Figure 5.2: Space filling pattern

2The upcoming UML Method integrates patterns, though [Rumbaugh et al.97].

Page 92: A Functional Pattern System for Object-Oriented Design

74 5 Design Patterns

Second, when you look, for instance, at a space filling pattern by the Dutch artistM.C. Escher the complexity of its derivation is completely hidden in the beauty ofits solution. However, if you try a variation to the basic filling pattern you mightbe able to fill some space, only to find out it does not work in general. Similarly,it has been observed that month of work flow into the change of a system only tofind out that there was a particular good reason for the old solution, which has tobe restored then.

In addition to presenting a concrete solution, rather than a set of first principles(like e.g., aim at low coupling and high cohesion), patterns also tie the solution toa concrete example.

“If a prior experience is understood only in terms of the generalization orprinciple behind the case, we don’t have as many places to put the new casein memory. We can tell people abstract rules of thumb which we have derivedfrom prior experiences, but it is very difficult for other people to learn fromthese. We have difficulty remembering such abstractions, but we can moreeasily remember a good story. Stories give life to past experience. Stories makethe events in memory memorable to others and to ourselves. This is one of thereasons why people like to tell stories [Shank90].” – Roger C. Shank

Design expertise is not a collection of abstract rules, because rules, though usefulfor evaluations, do not generate solutions. Design experts know many workingsolutions and know when to apply them:

“. . .wisdom is often ascribed to those who can tell just the right story at theright moment and who often have a large number of stories to tell [Shank90].”

– Roger C. Shank

5.2 History

It is hard to determine who described the notion of a pattern first. Because of itsfundamental and universal meaning for the human mind it should not come asa surprise to discover works as old as Liu Hsieh‘s (465–522) “The Literary Mindand the Carving of Dragons” (subtitle: A study of thought and pattern in Chineseliterature). He discusses forty nine different patterns in Chinese literary classics inthe style of a cookbook [Freddo96].

The first to popularize patterns, however, was Christopher Alexander. He cap-tured successful architectural solutions in pattern descriptions that are supposedto allow even non-architects to create their own individual architectural solutions(e.g., rooms, houses, gardens) [Alexander et al.77, Alexander79]. His patterns forma language, i.e., they provide a top-down guidance from the arrangements of citiesdown to areas, houses, and the shaping of rooms. Alexander’s work also caused thesoftware community to pay attention to patterns. His first influence already tookplace at the 1968 NATO conference3 in Garmisch when Naur, referring to Alexan-der’s Ph.D. thesis [Alexander64], mentioned the connection between architecture

3Coincidentally, Friedrich L. Bauer coined the term “Software Engineering” at this conference.

Page 93: A Functional Pattern System for Object-Oriented Design

5.2 History 75

and software construction [Naur69] (see section 5.1.2 on page 72). Alexander’s the-sis is even said to have been the basis for the structured design concepts of couplingand cohesion [Ballard95].

In retrospect, Robert Floyd mentioned pattern-like ideas in his 1978 TuringAward lecture, where he proposed to teach concepts or paradigms like “prompt-read-check-echo” and “generate-filter-accumulate”, rather than concrete program-ming languages [Floyd87].

But officially, it took the software community nineteen (19!) years to rediscoverpatterns when Ward Cunningham and KentBeck, inspired by Alexander’s work,in 1987 decided to let future users of a system design it by supplying them witha small pattern language for developing user interfaces with SMALLTALK. Theyreported the results at OOPSLA ’87 [Meyrowitz87].

Independently, Erich Gamma was dealing with ET++ [Weinand & Gamma94]and realized the importance of recurring design structures in his Ph.D. thesis aboutET++ [Gamma91].

Ralph E. Johnson used patterns to document a framework in 1992 [Johnson92]and also James O. Coplien’s C++ idioms4 contributed to the pattern litera-ture [Coplien92]. Further publications on patterns followed [Gamma et al.93,Beck & Johnson94]. A landmark was established with the first design pattern cat-alog book by the GOF5 in 1994 [Gamma et al.94]. In August 1994, the first con-ference on patterns were held at the Allerton Park estate near Monticello, Illinois.Many followed since then and are sure to follow in the future.

Curiously, the foundation of object-orientation was perceived by Alan Kaythrough a typical pattern discovery. First, in 1961 he noticed a particularly cleversolution to store and retrieve data for the Burroughs 220 machine [Kay96]. Thescheme provided a flexible file format by using three file sections: The first wasan indirection table to procedures (residing in the second part) that knew how toread and write the data of arbitrary size and format in the third pard. This schemecorresponds to objects that “know” how to store and restore themselves. The nextoccurrence for Kay was the Program Reference Table of the Burroughs B5000. Itsimilarly allowed a procedural access to modules. The third encounter in 1966was caused by the Sketchpad6 system. It used embedded pointers to proceduresto achieve an indirection in calls to procedures. Finally, Kay was asked to fix anALGOL compiler, which has been doctored to compile SIMULA. When, during hiscompiler studies, he noticed the similarity between Sketchpad and SIMULA objectsand discovered the independent nature and message sending activities of SIMULAinstances the “pattern” object-orientation came alive in his mind.

4Idioms can be considered to be language-specific patterns that are used to circumvent limita-tions in — and/or to provide extra functionality beyond — an implementation language.

5Gang of Four: Erich Gamma, Richard Helm, Ralph E. Johnson, and John Vlissides; authors ofthe first design pattern catalog book [Gamma et al.94].

6An early interactive computer graphics system.

Page 94: A Functional Pattern System for Object-Oriented Design

76 5 Design Patterns

5.3 Promise

Design patterns recommend themselves by many good reasons (see table 5.2).

Category Description

Reusable Software Software systems built with patterns are easier to main-tain, since patterns, among other aspects, often capture thekey solutions that make software extensible.

Reusable Design Patterns can be used as building blocks for system de-signs [Beck & Johnson94]. There is no need to invent thesemicro-architectures again and again.

Documentation Augmenting a design documentation with pattern namesand assigning pattern roles to system components in-stantly communicates a chosen design decision to a pat-tern connoisseur7 [Johnson92].

Communication Pattern names establish a vocabulary that enables to dis-cuss about designs at a higher level.

Teaching As recorded experience patterns can be used for teachingsuccessful designs. Patterns are a way to hand down cul-ture from generation to generation [Coplien96b].

Language Design If a recurring solution can be recognized as a work-arounddue to an inappropriate implementation language, itshould be taken in consideration for the design of new lan-guages [Baumgartner et al.96, Seiter et al.96].

Table 5.2: Promise of design patterns

The last category in table 5.2 is not usually promoted as a benefit of patterns, yetplays an important role for this dissertation: Part III starting at page 233 discussesthe impact on language design caused by the pattern system presented in part IIbeginning at page 85.

5.4 Form

All design pattern descriptions of part II use a slightly modified GOF pattern tem-plate [Gamma et al.94]. Besides minor changes to style and the division of the mo-tivation into a problem and solution section I added categories to section “RelatedPatterns” in order to clarify the nature of each relation. The following templatedescription has been adapted from the original GOF template description:

7A scientific proof of this statement has been made with a controlled experiment showing therecognition of patterns to result in faster and better changes to a program [Prechel et al.97].

Page 95: A Functional Pattern System for Object-Oriented Design

5.4 Form 77

escher-pattern-system.ps

Figure 5.3: Software system of interwoven patterns.

Pattern Name

The pattern’s name conveys the essence of the pattern succinctly. A good name isvital, because it will be used in design discussions.

Intent

A short statement that answers the following questions: What does the design pat-tern do? What is its rationale and intent? What particular design issue or problemdoes it address?

Also Known As

Other well-known names for the pattern, if any.

Page 96: A Functional Pattern System for Object-Oriented Design

78 5 Design Patterns

Motivation

A scenario that illustrates —

Problem

— a design problem —

Solution

— and how the class and object structures in the pattern solve the problem.The scenario will help you understand the more abstract description of the pat-

tern that follows.

Applicability

What are the situations in which the design pattern can be applied? What are ex-amples of poor designs that the pattern can address? How can you recognize thesesituations?

• An applicability bullet. An applicable situation.

Structure

A diagram showing relationships between participating classes and/or objects.

Participants

The classes and/or objects participating in the design pattern and their responsi-bilities.

• Participant Name

– Responsibility for what.

Collaborations

An interaction diagram and verbal description explaining how the participants col-laborate to carry out their responsibilities.

• Collaboration.

Page 97: A Functional Pattern System for Object-Oriented Design

5.5 Diagram notation 79

Consequences

How does the pattern support its objectives? What are the trade-offs and results ofusing the pattern? What aspect of system structure does it let you vary indepen-dently?

• An consequence bullet. Description of consequence.

Implementation

What pitfalls, hints, or techniques should you be aware of when implementing thepattern? Are there language-specific issues?

• An implementation bullet. Description of implementation aspect.

Sample Code

Code fragments that illustrate how you might implement the pattern in EIFFEL.

Known Uses

Examples of the pattern found in real systems or languages.

Related Patterns

What design patterns are closely related to this one?

Categorization

Patterns with important similarities and differences.

Collaboration

Patterns that allow useful collaborations.

Implementation

Patterns likely to be used for implementation.

5.5 Diagram notation

Following the GOF pattern template, all interaction-, class- and object diagramsuse an extended OMT diagram notation [Rumbaugh91]. In the following figuresshadowed ellipses are used to separate notational elements from their description.

Page 98: A Functional Pattern System for Object-Oriented Design

80 5 Design Patterns

5.5.1 Class diagram notation

Heir1

Heir2

Heir3

aggr

egat

es

crea

tes

Class name

Type parameter

Abstract Method

method(argument : ArgType)

AbstractClass

GenericClass

inherits

uses

method(argument : ArgType)

method(argument : ArgType) : ResultType

ConcreteClass

Figure 5.4: OMT Notation: Class diagram

Aggregation, as usual, means that a class has an attribute of the pointed-to classtype. The uses relation denotes the pointed-to class to be a server and thus includesaggregation and parameter passing.

5.5.2 Object diagram notation

anObjectanObject

anObject

references creates

Figure 5.5: OMT Notation: Object diagram

Page 99: A Functional Pattern System for Object-Oriented Design

5.5 Diagram notation 81

5.5.3 Interaction diagram notation

Object active

Skipping time

Tim

elin

e

anObject

method call

create

anObject anObject

Object identifier

method(argument)

returning a result

Figure 5.6: OMT Notation: Interaction diagram

Page 100: A Functional Pattern System for Object-Oriented Design

82 5 Design Patterns

Page 101: A Functional Pattern System for Object-Oriented Design

83

Part II

Pattern System

Page 102: A Functional Pattern System for Object-Oriented Design

84

Page 103: A Functional Pattern System for Object-Oriented Design

85

6 Catalog

Design patterns are points in a multidimensional design spacethat denote configurations of abstract design rules that actually work.

– me

Chapter 3 on page 45 discussed the compatibility and utility of severalfunctional concepts for object-oriented design. Now we select the con-cepts to be presented as design patterns. First, I briefly discuss the ten-

sion between Patterns and Proto-Patterns. Second, I motivate the particular selec-tion of concepts made and use so-called pattlets to generalize on each resultingpattern’s solution or supporting idea. Table 6.3 on page 92 lists all patterns withpage index and intent description. Finally, I motivate the use of EIFFEL for sam-ple code. Chapter 13 on page 221 elaborates on the possible interactions betweenpatterns of the system.

6.1 Proto-Patterns

Before I am going to present a system of patterns I should elucidate whether itshould better be called a system of Proto-Patterns. Section 5.1.1 on page 69 intro-duced the term “Proto-Pattern” to denote patterns that lack the empirical evidenceof successful application. After all, I aim at enriching object-oriented culture withconcepts from a different paradigm. Indeed, for most of the presented patternsand their variants I cannot fulfill the rule of three, that is, name three occurrencesin software systems. Does that make the patterns less useful? My answer is no!First, it is problematic to define the quality of something by its quantitative occur-rence. According to this measure, FORTRAN and COBOL would probably be thebest programming languages. Neither does the widespread use of a design solu-tion imply its quality. Second, the patterns presented are proven designs thoughnot in object-oriented systems. Nevertheless, they record successful designs fromfunctional programming.

Apparently, the rule of three is a good idea to motivate pattern authors to dotheir homework but should not be taken too literally.

“And occasionally, we do not start from concrete observation at all, butbuild up the invariant by purely abstract reasoning.

Page 104: A Functional Pattern System for Object-Oriented Design

86 6 Catalog

For of course, the discovery of patterns is not always historical. Some ofthe examples I have given might make it seem as though the only way to findpatterns is to get them from observation. This would imply that it was impossi-ble to find patterns which do not already exist in the world already: and wouldtherefore imply a claustrophobic conservatism; since no patterns which do notalready exist could ever be discovered. The real situation is quite different. Apattern is a discovery in the sense that it is a discovery of relationship betweencontext, forces and relationships in space, which holds absolutely. This discov-ery can be made on a purely theoretical level [Alexander79].”

– Christopher Alexander

Anyway, the presented patterns capture the software engineering properties offunctional concepts and techniques and introduce object-oriented designers tosome remarkable solutions.

6.2 Pattern System

The subsequent sections motivate each pattern (see table 6.3 on page 92) in thecontext of the discussion in chapter 4 on page 55. The actual patterns are presentedin the following chapters using the template description of section 5.4 on page 76.Any collaboration between patterns within the presented system will be covered inchapter 13 on page 221.

Subsequently, I use so-called pattlets to express the general idea, wisdom be-hind, or observations supporting a pattern. Pattlets recommend themselves to ex-press a practical advise without the overhead of a full blown pattern description.For instance:

Pattlet I

Describe a very general pattern extremely poignant and concise with a pattlet.♦

A pattlet just very briefly expresses a statement that has a very general applica-bility and would need specializations at least in the form of multiple examples oreven dedicated patterns in order to be a comprehensible pattern description.

6.2.1 Function Object

One of the most important concepts in functional programming are higher-orderfunctions [Hughes87]. Coincidentally, parameterization was shown to cohabit withinheritance even synergistically (see section 4.2.3.1 on page 60):

Pattlet II

For mature domains use black-box reuse.♦

Page 105: A Functional Pattern System for Object-Oriented Design

6.2 Pattern System 87

This statement refers to the fact that object composition, i.e., function parame-terization is superior to white-box reuse [Johnson & Foote88, Griss95] but the hot-spots of the domain have to be known. In other words, use white-box reuse untilyou found out the right spots to parameterize. Then, black-box reuse is more safe,dynamic, and comfortable.

A Function Object [Kuhne94, Kuhne95b, Kuhne95a, Kuhne96c, Kuhne97] isforemost a method object (akin to the Strategy pattern [Gamma et al.94]) but — asa variant — also subsumes a message object (Command pattern [Gamma et al.94]).A method object “objectifies” [Zimmer94] a method, thus, lifting it to first class sta-tus. A message object reifies a message, hence, making it amenable to persistence,logging, undoing, and most importantly allows to postpone it. The latter propertyis ideal for implementing callbacks [Meyer88, Gamma et al.94].

Beyond this, Function Object adopts the functional technique of currying (seesection 1.2.3 on page 12). Hence, it expands on Strategy by capturing real closuresand extends Command applications with partial parameterization.

6.2.2 Lazy Object

The second most important concept supporting modularity — next to higher-orderfunctions — from functional programming is lazy evaluation [Hughes87]. It isworthwhile attempting to achieve the same decoupling between generators andconsumers, to aim at a similar independence of irrelevant dependencies —

Pattlet IIIThe early bird gets the worm but the lazy lion gets the flesh.

— and to capture infinite data structures.

Pattlet IVCapture infinity by creating a cycle.

Lazy Object handles both aspects of call-by-need semantics:

1. Latest possible evaluation and2. at most once evaluation of expressions.

It also leads to the particular useful notion of lazy streams, which can be used tosupport iteration and conversion of collections.

6.2.3 Value Object

Section 4.2.1.1 on page 57 argued in favor of pure, i.e., side-effect free functions.A deliberate renunciation of state in programming was chosen to reduce softwarecomplexity due to state interactions.

Page 106: A Functional Pattern System for Object-Oriented Design

88 6 Catalog

Pattlet V

Expand your freedom by restricting yourself.♦

Beyond that, we might want to exclude certain procedures from changing other-wise mutable objects. The desirable properties of values from functional program-ming motivate the need for declaring immutable access paths to objects. Further-more, some objects should behave like values, that is, should have copy semantics.

6.2.4 Transfold

Whereas in object-oriented programming iterators are very popular (see Iteratorpattern [Gamma et al.94]) functional programmers use so-called mappers.

Pattlet VI

For safety and reuse: Don’t call us we call you.♦

Mappers (e.g., mapand fold) accept a function and apply it to all elements of acollection. Transfold, therefore, is an application of the functional principle to usegeneral purpose library functions which can be specialized by using higher-orderfunctionality. Transfold is shown to be specializeable to a plethora of operations.

Pattlet VII

Provide the most powerful concept and specialize it to solve concrete problems.♦

Although, it is acknowledged that functional mappers are easier and safer touse than conventional iterators, it is commonly assumed that

• they are not applicable in languages without built-in support for closures and

• they are less flexible than iterators.

Transfold invalidates both arguments by generalized folding with function objects.

6.2.5 Void Value

Void Value [Kuhne96b] is inspired by pattern matching. Yet, instead of establishinga similar mechanism I observe that pattern matching is simply another form of casestatements. One of the fundamentals of object-oriented design, however, is

Pattlet VIII

Replace case statements with dynamic binding.♦

Page 107: A Functional Pattern System for Object-Oriented Design

6.2 Pattern System 89

Although this guideline can also be taken too far (see the Translator pattern onpage 201 for a discussion when external dispatching is superior) it is at the heart ofobject-oriented design to avoid change sensitive client code. Surprisingly, object-oriented languages force their users to write case statements all the time, namelyin the form of conditionals testing for object references to be uninitialized (Nil1)or initialized (non-Nil) [Kuhne96b]. This is caused by the fact that empty or non-initialized data is often represented as a Nil value. Dispatching on Nil does notwork since it is not a type but an exceptional reference state. Luckily, another pattletcomes to our rescue:

Pattlet IX

Dispatch on values by making them types.♦

Void Value shows how to represent datatype constructors as subtypes in or-der to avoid client code checking for uninitialized data. Coincidentally, the valuesas types metaphor shows the object-oriented way of pattern matching. Instead ofrepresenting, e.g., the empty or non-empty state of a list as the list’s state one candefine NilList and ConsList as subclasses of List and distribute any function’s defi-nition respectively.

Curiously, for Void Value the functional concept was not the prototype, butpointed out a weak and inconsistent spot in the object-oriented framework.

6.2.6 Translator

Translator [Kuhne98] uses external functions to avoid element interfaces from be-coming bloated and being subject to changes. It, accordingly, prefers a functionalover an object-oriented decomposition (see section 4.2.3.2 on page 64). Actually, itemploys the Generic Function Object pattern on page 109 to implement free multi-dispatching functions. Translator is inspired by denotational semantics descrip-tions [Schmidt86] and facilitates incremental evaluation.

Pattlet X

Incremental evaluation calls for homomorphic translations.♦

Homomorphic translations are well-known in functional programming (e.g., bythe so-called fold operators [Bird86, Gibbons & Jones93]) and Translator relies onthem for local function descriptions and incremental evaluation.

Translator also uses an intermediate data structure which is in close correspon-dence to the explicit data structures used in functional programming to facilitateproofs and clarify design [King & Launchbury93]. John Backus’ FP language com-

1Nil has many different names such as Null, None, and Void.

Page 108: A Functional Pattern System for Object-Oriented Design

90 6 Catalog

pletely relied on such intermediate structures which were processed by the avail-able set of fixed functional forms [Backus78].

Summarizing, Translator joins the four aspects of

• homomorphic translations,

• separation of translation and semantics,

• potential incremental translations, and

• external polymorphism.

and thus describes a general approach for homomorphic translations in an object-oriented language.

In conclusion, I certainly do not claim to have presented all patterns existing infunctional programming. Functional programming is full of interesting techniquesthat are worthwhile to be documented and are not presented here. Nevertheless,the patterns chosen for this thesis provide a complete coverage of concepts sup-ported by functional languages. See section 4 on page 55 for the discussion aboutconcepts worthwhile to be covered and reasons for rejecting others.

Most importantly, higher order functions and lazy evaluation are captured byFunction Object and Lazy Object. The virtues of immutability are discussed byValue Object. With Transfold we leave the realm of language concepts and enterlanguage usage. Using folds on collections stems from functional programmingfolklore rather than being a particular language concept2. Also, Void Value doesnot capture pattern matching in full but draws an important lesson from this func-tional language concept. Finally, Translator builds on Function Object and the ideaof Transfold — to replace constructors with functions — to externalize function-ality for homomorphic interpretations. While less obvious then folding, recursiveinterpretations and explicit intermediate data structures are certainly reoccurringconcepts in functional programming. Coincidentally, Transfold is an ideal exampleto showcase the virtues of higher-order functions and lazy evaluation contributingto a design solution that resolves surprisingly many issues at once.

The presented patterns cover a spectrum from functional language concepts totypical uses of functional languages as depicted in table 6.1.

Language concept

◊ ➠ Language usage

FunctionObject

LazyObject

ValueObject Transfold Void

Value Translator

Functional programming

◊ ➠ Object-Orientation

Table 6.1: Functional pattern spectrum

2Higher order functions certainly suggest to use internal iteration, though.

Page 109: A Functional Pattern System for Object-Oriented Design

6.3 Why Eiffel? 91

6.3 Why Eiffel?

I choose EIFFEL as the language to illustrate pattern issues with sample code, since

• it is well-known,

• uses garbage collection,

• has a clean-syntax,

• and is purely object-oriented.

A language like C++ would force the examples to include code for memorymanagement, distracting from the prior goals. EIFFEL’s syntax is sufficiently easyto read in order to allow C++- or SMALLTALK programmers to benefit from theexamples. BETA has a much less clear syntax and is also already influenced byfunctional ideas which makes it less clear to demonstrate the emulations neededfor pure object-oriented languages.

EIFFEL scores well above a number of other well-known general purposes lan-guages with 21 source statements per function point [Jones96] (see table 6.2).

Language Level

Average sourcestatements perfunction point

ADA 95 6.50 49

CLOS 15.00 21

C++ 6.00 53

EIFFEL 15.00 21

HASKELL 8.50 38

JAVA 6.00 53

SMALLTALK 15.00 21

Table 6.2: Language levels

JAVA would have been a very good candidate too. I expect a correction to thelanguage level given in table 6.2. Unfortunately, until today JAVA lacks support forgeneric datatypes. Any generic treatment of datatypes means to create superfluoussubclasses or spurious cast statements. Given this limitation, EIFFEL was the bestchoice to express pattern goals with the least distraction from language limitations.

Page 110: A Functional Pattern System for Object-Oriented Design

92 6 Catalog

Name (page) Intent

Function Object (93) Encapsulate a function with an object. This is useful forparameterization of algorithms, partial parameterizationof functions, delayed calculation, lifting methods to first-class citizens, and for separating functions from data.

Lazy Object (115) Defer calculations to the latest possible point in time.This is useful for resolving dependencies, calculationcomplexity reduction, increased modularity and infinitedata structures.

Value Object (149) Use immutable objects with generator operations forprotection against side-effects and aliasing.

Transfold (163) Process the elements of one or more aggregate objectswithout exposing their representation and without writ-ing explicit loops.

Void Value (191) Raise Nil to a first-class value. This allows to treat voidand non-void data uniformly, is a way to provide defaultbehavior, facilitates the definition of recursive methods,and enables to deal with error situations more gracefully.

Translator (201) Add semantics to structures with heterogeneous ele-ments without changing the elements. Separate interpre-tations from each other and use local interpretations thatallow for incremental reevaluation.

Table 6.3: Functional pattern system

Page 111: A Functional Pattern System for Object-Oriented Design

93

7 Function Object

The secret of dealing successfully with a child is not to be its parent.– Mell Lazarus

7.1 Intent

Encapsulate a function with an object. This is useful for parameterization of algo-rithms, partial parameterization of functions, delayed calculation, lifting methodsto first-class citizens, and for separating functions from data.

7.2 Also Known As

Lexical Closure [Breuel88], Functor [Coplien92], Agent [Hillegass93], Agent-Object [Kuhne94], Functionoid [Coleman et al.94], Functoid [Laufer95], Function-Object [Stepanov & Lee94, Kuhne95b, Kuhne97].

7.3 Motivation

Almost any software system makes use of behavior parameterization in one orthe other way. Iterators are a good example. The iteration algorithm is constantwhereas an iteration action or function varies. A special kind of iteration is thehas (member test) function of a collection. Consider a collection of books. Membertesting should cover testing for a particular book title, book author, book type, etc.

7.3.1 Problem

We may implement the predicate in the collection’s elements. This works nicely if

• the element’s natural interface contains the predicate.

• the predicate implementation may vary with the element type, but we do notwant to have a selection of several predicates for one element type.

Composing traversal algorithm and predicate as above uses dynamic binding ofthe element’s predicate method. For instance, it is acceptable for a SortedList of

Page 112: A Functional Pattern System for Object-Oriented Design

94 7 Function Object

Comparable elements to defer the compare predicate to the elements [Meyer94b].As it is the sole purpose of such a collection to transpose an element property (e.g.,ordering relation) to the entire collection, it is convenient that the element typeautomatically provides a fixed predicate. The collection of books, however, shouldbe sortable according to various criteria, like author, title, and date. In order to beable to change the sorting criterion at runtime, we do not want to encumber thetraversal algorithm with a switch statement that would select the appropriate bookcompare method.

One way to achieve this is to use an external iterator. Then the varying predicate(compare title, compare author, etc.) can be combined with the traversal algorithmby placing the predicate in an explicit loop that advances the external iterator oneby one. As a result, the number of explicit loops corresponds to the number ofmember test predicates uses.

However, there are good reasons to write such a loop only once (see “Write aLoop Once” [Martin94], the discussion in the Iterator pattern [Gamma et al.94] andchapter 10 on page 163). Consequently, we use an internal iterator. Given a predi-cate, it returns true if any of the books fits the predicate. Here is how the predicateis “given” to the internal iterator conventionally: The actual member test method isa Template Method [Gamma et al.94], which depends on an abstract predicate. Theimplementation for the abstract predicate, and thus the specific member test op-eration, is given in descendants [Madsen et al.93, Meyer94b, Martin94]. Selectionof the member tests is done by selecting the appropriate descendant. So, traversalalgorithm and functions in general are combined through dynamic binding of theabstract function method. Note that this forces us to place the member test methodoutside the collection of books (e.g., at iteration objects) since we do not want tocreate book collection subclasses but member test variations only. Further disad-vantages aligned with the above application of an object-oriented design, usinginheritance and dynamic binding are:

Static combination. All possible combinations of iteration schemes and functions arefixed at compile time. Neither is it possible to create a new function at runtime.

Combinatorial explosion. Sometimes it is useful to select not just one, but a combi-nation of functions or tests and functions. With subclassing, it is not feasible toprovide any independent combination, since it leads to an exponentially growingnumber of subclasses.

Subclass proliferation. Each new function demands a new Iterator subclass. The classname space is cluttered by many concrete Iterator subclasses. We may use repeatedinheritance to combine all functions in one subclass [Meyer92], but this makesthings worse. First, it is non-local design to lump all functions in one class. Sec-ond, we have to apply heavy renaming for iteration schemes and functions in thesubclass; any combination of iteration scheme and function must be given a dis-tinct name. Third, we lose the ability to use dynamic binding for the selection ofa function. Since all functions belong to one class, we no longer can use concreteIterator instances to select the actual combination of iteration and function.

Page 113: A Functional Pattern System for Object-Oriented Design

7.3 Motivation 95

Awkward reuse. Reusing the functions for other iteration schemes or different pur-poses is practically impossible if they are defined in Iterator subclasses. The solu-tion is to extract the functions in classes of their own. But now multiple inheritanceis necessary in order to inherit from Iterator and to inherit from a particular func-tion. At least multiple tests or functions can be “mixed-in”, but scope resolutionis needed, and each function combination results in a combinator subclass. Notethat some languages, such as SMALLTALK and JAVA do not even allow multipleinheritance.

Poor encapsulation. Composing an iteration scheme and a function with inheritancejoins the name spaces of both. In fact, the multiple inheritance solution causesiterator, function, and combinator class to share the same name-space. Implemen-tation changes to either of the classes can easily invalidate the other. An interfacebetween super- and subclasses, as the private parts in C++ [Ellis & Stroustrup90],alleviates the problem considerably. As an evidence for the relevance of this prob-lem consider the programming of a dialog in JAVA. If you use an is valid flag torecord the validity of the dialog fields you will not get a dialog display althoughthe program compiles fine. It probably takes a while till you find out about a flagis valid in class Component (you happen to indirectly inherit from) which is usedfor recording whether a window has been displayed already [Maughan96].

Unrestricted flexibility. Creating a designated class for the combination of an iter-ation scheme and a function opens up the possibility of overriding the iterationscheme for particular actions. Explicitly counting the elements in a collection couldbe replaced by just returning the value of an attribute count. Unfortunately, thisadvantage for the designer of a library is a disadvantage for the user of a library.The user may rely on properties of the original iteration scheme. If the iterationfunction not only counts the elements, but in addition produces some side-effect,the side-effects will not be executed in the optimized version described above. Or,referring to the cook and recipe example of section 4.2.3.1 on page 60, an adaptedcook may accompany its cooking with singing which is not always expected anddesirable.

Also, it is possible that the optimized version does not fully conform to theoriginal version, not to speak of plain errors. An original robust iterationscheme [Coleman et al.94], that first evaluates all tests and then applies all func-tions, could be replaced by a standard scheme, that evaluates test and functions oneach element separately. The two versions will behave differently when iterationfunctions side-effect neighboring elements.

Pre- and postconditions [Meyer88] can help to enforce behavioral identity betweeniteration schemes, but problems like the above are hard to cover and checking con-tracts at runtime boils down to testing, as opposed to rigorous proofs.

Identity changes. In order to change the iteration function a different iterator in-stance must be used. While one would seldom need to rely on an unchangingiterator instance, this property is inhibiting in other settings of parameterization.

Page 114: A Functional Pattern System for Object-Oriented Design

96 7 Function Object

For instance, it might be mandatory to keep the same instance of a cook, whilebeing able to process different recipes.

7.3.2 Solution

The best way to get rid of the above disadvantages is to objectify predicates with theFunction Object pattern. Combining traversal algorithm and function with Func-tion Object works through literally “giving” an objectified function to an internaliterator. In our example, the member test method accepts a test predicate to be usedduring iteration:

has(predicate : Function[Book, Boolean]) : Boolean isdo

from i:=1;until i>count or Resultloop

if predicate @ (books.item(i)) thenResult:=true;

end;i:=i+1;

end;end

Here, the collection of books simply consists of an array of books that is tra-versed with an integer loop. Note how EIFFEL allows using a nice function appli-cation syntax for passing the current book to the predicate. The operator “@” isdefined as an infix operator in the common interface for all function objects:

deferred class Function[In, Out]feature

infix "@" (v : In) : Out is deferred end;end

The @-operator is defined to take a generic argument type In and to return ageneric result type Out . The member test method from above instantiates these toBook and Boolean respectively.

A predicate to check for a bible instance is:

class IsBibleinherit Function[Book, Boolean]feature

infix "@" (b : Book) : Boolean isdo

Result:=(b.title.is_equal("Bible"));end;

end

Page 115: A Functional Pattern System for Object-Oriented Design

7.3 Motivation 97

Now member testing looks like:

containsBible:=library.has(isBible));

Checking for a book with a certain title can be achieved by passing a predicatethat receives the book title through its constructor:

!!checkTitle.make("Moby Dick");library.has(checkTitle);

The code for CheckTitle is straightforward:

class CheckTitleinherit Function[Book, Boolean]creation makefeature

title : String;

make(b : Book) isdo

title:=clone(b.title);end;

infix "@" (b : Book) : Boolean isdo

Result:=(b.title.is_equal(title));end;

end

Yet, there is another exciting way to achieve the same thing. Suppose the ex-istence of a predicate that compares the titles of two books, e.g., for sorting bookswith respect to titles. Although the member test method expects a predicate withone book parameter only we can make the two argument compare function fit bypassing a book with the title to be looked for in advance:

library.has(titleCompare @ mobyBook);

Predicate titleCompare has two parameters. Passing mobyBook results in apredicate with one parameter that perfectly fits as an argument to the has method.Class titleCompare is easily implemented like this:

class TitleCompareinherit Function[Book, Function[Book, Boolean]]feature

infix "@" (b : Book) : CheckTitle isdo

!!Result.make(b);end;

end

Page 116: A Functional Pattern System for Object-Oriented Design

98 7 Function Object

Thanks to the generic type parameters of function objects the compiler can checkfor correct function application and will reject wrong uses concerning number ortype of arguments.

Finally, we may combine multiple search criteria like title comparison and datechecking by composing predicates with a composite function object:

library.has(and @ pred1 @ pred2);

Function object and takes a variable number of predicates as arguments, applieseach of them to the book it receives, and returns true if all predicates hold.

7.4 Applicability

• Parameterization. Function objects are a good candidate whenever general be-havior can be adapted to special behavior:

Dynamics. In addition to runtime selection of existing function objects, newfunction objects can also be created at runtime. A user may dynamically com-pose a multi-media function object from text-, graphic-, and sound-producingfunction objects.

Orthogonality. Having more than one behavior parameter creates the problemof handling all possible combinations of the individual cases. Function objectscan freely be mixed without interfering and without combinator classes.

Reuse. Function objects can be used by any adaptable algorithm that knowstheir interface. Even if the algorithm was not designed to supply a functionwith mandatory arguments, it is often possible to supply them to the func-tion in advance. Consider an error reporter, parameterized by output formatfunctions, only intended for generating text messages. We can upgrade the re-porter to create a graphical alert-box by passing a function object that alreadyreceived information about box-size, colors, etc.

Identity. When the behavior of an object should change while keeping itsidentity, function objects can be used as behavior parameters to the ob-ject. In contrast, encoding behavior in subclasses calls for something likeSMALLTALK’s “become:” in order to achieve the same effect.

• Business transactions. Often the functions are the stable concepts of a systemand represent good maintainance spots, in order to cope with changing func-tionality. Instead of being a well-defined operation on one single object,transactions are “an orchestration of objects working together toward a commongoal” [Coplien92]. When transactions do not naturally fit into existing data ab-stractions, Function Object can lift them to first-class status while providing auniform function application interface. Functions may even form a hierarchy,just like algorithms, for classification and reuse purposes [Schmitz92].

Page 117: A Functional Pattern System for Object-Oriented Design

7.4 Applicability 99

• Monolithic algorithms. Just like Strategy [Gamma et al.94] Function Object canbe used to define localized algorithms for data structures. When a data struc-ture is stable, but the operations on it often change, it is not a good idea touse the standard object-oriented method to distribute the operations over theobject types involved. For instance, each change or addition of an algorithmon abstract syntax trees (such as typeCheck, compile) demands a change in allnode-object types. In addition, it is not possible to exchange the operations atruntime.

If we turn the algorithm into a Function Object, we must use a generic Func-tion Object (see section 7.10 on page 107) to dispatch on the node types, butin analogy to the Strategy pattern [Gamma et al.94],

• the algorithm logic is localized,

• a function’s state can accumulate results, and

• we can dynamically choose an algorithm.

See section 7.12 on page 111 for a comparison of Command, State, Strategy,and Function Object.

• Small interfaces. When an object potentially supports many extrinsic opera-tions (e.g., CAD-objects may support different viewing methods, cost- andstability calculations, etc.), but its interface preferably should contain the ba-sic, intrinsic functions only (e.g., geometric data), then the functionality canbe implemented in function objects that take the object as an argument. TheMVC-framework is an example for separating views from data structuresthough presentation might be considered to be the model’s responsibility atfirst glance.

• Method simplification. If a large method, containing many lines of code, can-not be split into smaller, more simple methods, because the code heavily usestemporary variables for communication, then the method can be transformedinto a function object. The main transformation is to replace the temporaryvariables with function object attributes. As a result, the method can be splitup into more manageable sub-methods, without passing parameters betweeninter-method (but still intra-object) invocations, since communication still cantake place via function object attributes. The main computation method sim-ply puts the pieces together, itself being as clear as documentation [Beck96].

• Symmetric functions. Some functions do not need to privilege (break the en-capsulation of) one of their arguments, by making it the receiver of a method.For instance, checking whether a Tetris piece hits something in the shaft canequally be implemented by Piece or Shaft . We are even forced to introducea free function if the source code of neither class is available. Likewise, basictypes are often not extendible. Note that the free function replaces a mutual

Page 118: A Functional Pattern System for Object-Oriented Design

100 7 Function Object

dependency between two classes by two classes depending on the free func-tion. If the two classes belong to separate large clusters, we decoupled theclusters with regard to change propagation and recompilation.

• Delayed calculation. Function objects postpone the calculation of their resultuntil it is actually needed. When a function object is passed as a parameterbut the receiving method does not make use of it, it will not be evaluated. Ifthe result is never needed, this pays off in run time efficiency. Function Ob-ject effectively supports lazy evaluation and thus can be used to implementinfinite data structures and supports modularization by decoupling data gen-eration from data consumption [Hughes87].

Trying to achieve this with methods establishes a function object one way orthe other. We may delay method invocation by passing the object that sup-ports the method. Then we use the object as a closure with the disadvantageof non-uniform method names. When we allow the client to call the method,although the client might not really need the result, the method must producean object which stands for the calculation of the result. This is precisely thecase of returning a Function Object.

Do not use Function unless you have a concrete reason. There is a time andspace penalty in creating and calling a function object, instead of just invoking amethod. Also, there is an initial effort to write an extra function class. Functionswith many parameters require just as many class definitions in order to exploitpartial parameterization. Finally, sometimes unrestricted flexibility as mentioned insection 7.3.1 on page 93 is clearly desirable. Functions that vary with the imple-mentation of their argument should be members of this abstraction and not freefunction objects. Also, see the remarks concerning flexibility and efficiency in sec-tion 7.8 on page 102.

7.5 Structure

See figure 7.1 on the next page.

7.6 Participants

• Function

– declares an interface for function application.

• ConcreteFunction (e.g., isBible)

– implements a function.

– collects argument values until evaluation.

Page 119: A Functional Pattern System for Object-Oriented Design

7.7 Collaborations 101

Client

application(argument)

Function

ConcreteFunction

constructor(initial args)application(argument)

collected arguments

Invoker

Figure 7.1: Function Object structure diagram

• Client

– creates or uses a ConcreteFunction .

– possibly applies it to arguments.

– calls Invoker with a ConcreteFunction .

• Invoker (e.g., Iterator)

– applies ConcreteFunction to arguments

– returns a final result to its Client .

7.7 Collaborations

• A client creates a function and possibly supplies arguments.

• An invoker takes the function as a parameter.

• The invoker applies the function to arguments.

• The client receives a result from the invoker.

Client and invoker do not necessarily have to be different objects. A client mayevaluate a function object itself if no further arguments are needed or it does notneed an invoker to determine the point of evaluation. If an invoker does not supplyfurther argument but uses a dummy argument only to evaluate the function objectthan we have an application of the Command pattern.

Page 120: A Functional Pattern System for Object-Oriented Design

102 7 Function Object

aClient

make(initial args)Creation

Parameterization invokerMethod(aFunction)

aFunction anInvoker

apply(arg)Application

Figure 7.2: Function Object interaction diagram

7.8 Consequences

• Abstraction. Function objects abstract from function pointers and in particularfrom pointers to methods [Coplien92]. Instead of the C++ code:

aFilter.*(aFilter.current)(t);

we can write

aFilter(t);

• Simplicity. The use of function objects does not introduce inheritance relation-ships and does not create spurious combinator classes.

• Explicitness. The code cook.prepare(fish) is easy to understand. Whenrecipes are wired into Cook subclasses, cook.prepare depends on theactual Cook type. Clever variable names (e.g., fishCook.prepare )often are not an option, e.g., cook.prepare(fish) , followed bycook.prepare(desert) .

Note that the client must know the function object. A variability on the im-plementation — rather than the behavior — of Cook is better treated with aBridge [Gamma et al.94]. Clients of Cook s should not know about implemen-tation alternatives, unless they explicitly want to decide on time and spacebehavior.

• Compositionality. In analogy to Macro-Command [Gamma et al.94], functionobjects can be dynamically composed to form a sequence of functions byforwarding intermediate results to subsequent function objects. Compositefunction objects may also apply several component function objects in paral-lel. Composite function object variants differ in the way they produce a finaloutput from the single results.

Page 121: A Functional Pattern System for Object-Oriented Design

7.8 Consequences 103

• Uniform invocation. Imposing a function object’s interface on related oper-ations allows uniformly invoking them. Instead of switching to differentmethod names (like compTitle and compAuthor), we evaluate an abstract func-tion object and rely on dynamic binding [Meyer88, Gamma et al.94]. Conse-quently, we can add new operations, without changing the caller (e.g., eventhandler).

Whenever specific evaluation names (e.g., evaluate, solve, find) are consid-ered important, then they can be provided as aliases.

• Encapsulation. As function objects establish client relationships only, theyare protected from implementation changes to algorithms that use them.Likewise, the implementation of function objects can change withoutinvalidating the algorithms. Hence, Function Object allows black-boxreuse [Johnson & Foote88] and helps to advance reuse by inheritance toreuse by composition [Johnson94].

• Security. A client of an adaptable algorithm can be sure to use a fixed algo-rithm semantics. It is impossible to be given an optimized version which doesnot fully comply to the original semantics (see 7.3.1 on page 93, Unrestrictedflexibility).

• Flexibility.

+ A statement like iterator.do(f) is polymorphic in three ways:1. Iterator may internally reference any data structure that conforms to

a particular interface.2. The actual instance of Iterator determines the iteration strategy (e.g.,

pre- or post-order traversal on trees).3. The actual instance of f determines the iteration function.

− It is not possible to automatically optimize algorithms for specific func-tions. Nevertheless, one more level of indirection can explicitly constructoptimized combinations of functions and algorithms.

− As discussed in section 4.2.3.1 on page 60 black-box composition doesnot allow adapting, e.g., cooks for new recipes. Often, it is nonethelesspossible to use new recipes with unaltered cooks by partial applicationof recipes. If a new class of recipes needs the room temperature as anargument (which cooks do not supply) it is possible to curry the recipewith the room temperature in advance. Thus, the new recipe class looksjust like the old ones to cooks (see how keyword parameters boost thisoption in section 7.10 on page 107).

− When a function has been extracted from a data structure, it is no longerpossible to simply redefine it in future derivations. One way to accountfor this is to make the extracted function a generic Function Object (seesection 7.10 on page 107) that will discriminate between data structurevariations.

Page 122: A Functional Pattern System for Object-Oriented Design

104 7 Function Object

• Separation. The ability to carry data (e.g., alert box size) enables function ob-jects to operate on data from outside an (e.g., error-reporter) algorithm as wellas (e.g., error text) data from inside the algorithm. The data from outside thealgorithm can be local to the algorithm caller. There is no need to communi-cate via global data. Since function objects can combine local data spaces, theyallow decoupling data spaces from each other while still supporting commu-nication.

• Reuse. Adaptable algorithms become more reusable because they do not needto know about additional parameters for functions.

Moreover, function objects are multi-purpose:

+ Functions can easily be used for different iteration strategies. They areare not bound to a particular adaptable algorithm, e.g., comparing book-titles is useful for sorting and for membership testing in collections.

+ One function with n parameters actually represents n functions and onevalue. The first function has n parameters. The second, created by ap-plying the first to an argument, has n−1 parameters, and so on, until thelast function is applied to an argument and produces the result1.An example from physics shows the useful functions which can be cre-ated from the gravitational force function [Leavens94]:

GravityLaw m1 r m2 =G m1 m2

r2

forceearth = GravityLaw massearth

forcesurface = forceearthradiusearth

forcemy = forcesurfacemassmy

• Iteration. Function Object suggests the use of internal, rather than external, it-eratorsiterator. Internal iterators avoid explicit state and re-occurring explicitcontrol loops. Often external iterators are promoted to be more flexible. It issaid to be practically impossible to compare two data structures with an in-ternal iterator [Gamma et al.94]. However, the Transfold pattern on page 163simply extends an iterator to accept not just one, but n data structures. Atransfold-method accesses the first, second, etc., elements of all data struc-tures simultaneously.

Function Object allows making iteration a method of data structures since itdoes not demand for subclassing the data structure. This facilitates the useof iterators and allows redefining iteration algorithms for special data struc-tures. Moreover, the data structure (e.g., Dictionary ) then does not need toexport methods (e.g., first, next) in order to allow iterators to access its ele-ments.

1Which could again be a function.

Page 123: A Functional Pattern System for Object-Oriented Design

7.9 Implementation 105

• Efficiency.

+ A function object may calculate partial results from arguments and passthese to a result function. Hence, the partial result is computed onlyonce, no matter how many times the resulting function object will beapplied to different arguments in the future, e.g.,

forcesurface= forceearthcostlyCalculation

and then

forcemy = forcesurfacemassmy

forceyour = forcesurfacemassyour.

− Passing client parameters to function objects can be more inefficient thanto, e.g., directly access internal attributes of an iterator superclass. Inprinciple this could be tackled by compiler optimizations [Sestoft88].

− Care should be taken not to unnecessarily keep references to unevalu-ated calculations, i.e., function objects. Otherwise, the occupied spacecannot be reclaimed.

− Finally, function objects access the public interface of their servers only.This is why Symmetric functions (see section 7.4 on page 98) enforce theencapsulation of their arguments. This represents positive decoupling,but can be more inefficient than unrestricted access. However, selectiveexport (EIFFEL) or friends (C++), allow trading in efficiency for safety.

− It is not possible to apply partial evaluation techniques to clo-sures [Davies & Pfenning96]. Hence, function objects typically cannotbe evaluated before runtime. However, this is just a price to pay forflexibility. Dynamic binding introduces runtime overhead exactly forthe same reason.

7.9 Implementation

• Creation. Quite naturally, function objects must be created before they can beused. A typical code fragment shows the overhead in notation to create theobject:

...local

times : Times[Integer];do

!!times;

Result:=times @ 6 @ 7;end;

Page 124: A Functional Pattern System for Object-Oriented Design

106 7 Function Object

The fragment coincidentally also illustrates the danger in forgetting the cre-ation instruction, causing a runtime exception. Fortunately, EIFFEL enablesthe useful idiom of declaring a variable of expanded type, i.e., rendering thecreation instruction unnecessary. With —

...local

times : expanded Times[Integer];do

Result:=times @ 6 @ 7;end;

— the declaration already takes care of providing times with a value. Nosharing of function objects can occur since the first application of times cre-ates a new instance anyway (see 7.12.3 on page 113, Prototype).

• Call-by-value. Function objects must copy their arguments. Otherwise, theirbehavior will depend on side-effects on their arguments. In general, thiswould produce unpredictable results. In some cases, however, it may be de-sirable. The function object then plays the role of a future variable, which ispassed to an invoker before all data needed to compute the result is avail-able. Long after the function object has been passed to the invoker, it can besupplied with the necessary data by producing side-effects on arguments.

• Initial arguments. Real closures (e.g., SMALLTALK blocks) implicitly bind vari-ables, which are not declared as parameters, in their creation environ-ment. This is not possible with function objects. One way out is to treatinitial arguments and standard arguments uniformly through lambda lift-ing [Field & Harrison88]: Initial arguments are passed as arguments to thefunction object in its creation environment.

Alternatively, it is possible to pass initial arguments through the function ob-ject constructor (see CheckTitle in section 7.3.2 on page 96). This saves theintermediate classes needed to implement the partial application to initial ar-guments. Of course, both variants do not exclude each other.

Note, however, that explicit binding is equivalent to implicit binding. Thisfollows from the reverse application of β-reduction. Yet, the implicit variablebinding of real closures forces them to use the the same variables names asits creation environment. In contrast, a function object can be used in variousenvironments without the need to make initial argument names match theenvironment.

• Delayed calculation. Commonly a function is evaluated after it has received itslast argument. Yet, function object application and evaluation can be sepa-rated by corresponding methods for application and evaluation. As a result,the client, triggering evaluation, does not have to know about the last argu-ment. Also, the supplier of the last argument does not need to enforce the

Page 125: A Functional Pattern System for Object-Oriented Design

7.10 Function Object variants 107

calculation of the result, which is crucial for lazy evaluation. In order to en-able automatic evaluation on full argument supply while still supporting theabove separation, it is possible to evaluate on the last parameter and use akind of dummy parameter (called unit in ML [Wikstrom87]).

So function objects that separate their evaluation from the supplement of thelast parameter (e.g., Call-back functions) are defined with a dummy parame-ter, which is supplied by the evaluator.

• Interface width. As already mentioned in section 7.8 on page 102 function ob-jects may force their argument classes to provide access to otherwise non-public features. If a particular function object is closely coupled to a specificobject type, then declaring the function object as the object’s friend, i.e., us-ing selective export, allows efficient access to internal data nevertheless. As aresult the object can keep its public interface to other clients to a minimum.

• Partial parameterization. Two extremes to implement partial parameterizationexist. The less verbose is to always keep the same instance of function ob-ject and assign incoming arguments to corresponding internal attributes. Thiswill cause trouble when the same function object is used by several clients. Asthe application of a function object does not create a new instance, the clientswill get confused at the shared state. Furthermore, static typing becomes im-possible. If each application of a function object produces a new instance of adifferent type, then static typing is enabled again and no unwanted sharing offunction object state can occur. Unfortunately, this forces us to write at leastn−1 classes for a function object with n parameters.

Note, that it is not necessary to provide application methods, that allow pass-ing multiple arguments at once. This would only produce even more imple-mentation overhead and can easily replaced by passing the arguments one byone.

• Covariance. In principle, EIFFEL allows using a non-generic function interfacesince the application method can be covariantly redefined. A general ancestorwould have the type Any to Any and descendents could narrow input and out-put types. However, we recommend to use a generic interface as presentedin order to avoid complications2 through the combination of polymorphismand covariant redefinition, i.e., catcalls [Meyer96].

7.10 Function Object variants

This section shortly touches upon important extensions to Function Object.

• Keyword Parameters. In addition to standard application, function objects mayprovide keyword parameters. Accordingly, parameters can be passed in any

2Until today no EIFFEL compiler implements system-level validity checking, which is necessaryto catch type errors resulting from mixing argument covariance with polymorphism.

Page 126: A Functional Pattern System for Object-Oriented Design

108 7 Function Object

order. This makes sense, in order to create useful abstractions. If the definitionof the gravitational force in the example of section 7.8 on page 102 had been

GravityLaw m1 m2 r =G m1 m2

r2 ,

(note the different order of parameters) we cannot define:

forcesurface= GravityLaw massearthradiusearth.3

With keyword parameters, notwithstanding, we can write:

force:=gravityLaw.m1(earthMass).r(earthRadius);

Keyword parameters effectively extend currying to partial application. Be-yond the reusability aspect above, however, they also enhance the clarity ofprograms: Consider the initialization of a node with a label and an identifica-tion. The code

node.make(a, b);

does not tell us what parameter plays which role. The meaning is hidden inthe position of the arguments. Surely, we should use more expressive variablenames but these should reflect the meaning in their environment, e.g.:

node.make(name, counter);

In other words, we use a name and a counter to initialize a node but still areconfused about the argument meanings. Only the version using keywordparameters —

node.label(name).id(counter);

— documents the meaning of arguments without compromising the docu-mentation of the environment variable meanings. In another case a node ispossibly created using a number string and a random number which shouldbe visible in the initialization statement. Keyword parameters respect thatthere is both an inner and an outer meaning for parameters and that bothmeanings are important to document.

• Default Parameters. A keyword parameter does not need to be mandatory.Recipes may be designed to work for four persons by default but allow aoptional adaption. So, both

cook.prepare(dinner);

and3Functional programmers know this problem and use the flip function to exchange the order of

(howbeit only) two arguments.

Page 127: A Functional Pattern System for Object-Oriented Design

7.10 Function Object variants 109

cook.prepare(dinner.people(2));

are valid. Default parameters in general have been described as the designpattern Convenience Methods [Hirschfeld96]. Unix shell commands are anexample for the application of both default- and keyword parameters.

• Imperative result. It is possible to use the internal state of a function object tocalculate one or multiple results (add accumulated results to ConcreteFunctionin the structure diagram of section 7.5 on page 100). For instance, one func-tion object may count and simultaneously sum up the integers in a set duringa single traversal. The set iteration client must request the results from thefunction object through an extended interface (add an arrow getResult fromaClient to aFunction in the diagram of section 7.7 on page 101). Note that im-perative function objects may produce any result from a standard traversalalgorithm. The latter does not need any adaption concerning type or whatso-ever.

• Procedure Object. If we allow function objects to have side effects on theirarguments we arrive at the Command pattern extended with parametersand result value. Procedure Object makes methods amenable to persistentcommand logging, command histories for undoing, network distributionof commands, etc. Like Command, Procedure Object may feature an undomethod, which uses information in the procedure object’s state to undo op-erations [Meyer88, Gamma et al.94]. Note how easy it is to compose a pro-cedure object, to be used as an iteration action, with a function object pred-icate that acts as a sentinel for the action. As a result, special iterations as“do if” [Meyer94b] can be replaced with a standard iteration.

• Multi-dispatch. Sometimes an operation depends on more than one argumenttype. For instance, adding two numbers works differently for various pairsof integers, reals, and complex numbers. Simulating multi-dispatch withstandard single-dispatch [Ingalls86] results in many additional methods (likeaddInteger, addReal). The dispatching code is thus distributed over all in-volved classes. If, as in the above example, the operation must cope witha symmetric type relation (e.g., real+int & int+real), each class has to know allother argument types.

A generic4 function object removes the dispatching code from the argumenttypes and concentrates it in one place. It uses runtime type identification toselect the correct code for a given combination of argument types. As such,it is not simply an overloaded Function Object, which would statically resolvethe types.

Note that nested type switches can be avoided with partial parameterization:Upon receipt of an argument, a generic function object uses one type switch

4Named after CLOS’ [DeMichiel & Gabriel87] generic functions.

Page 128: A Functional Pattern System for Object-Oriented Design

110 7 Function Object

statement to create a corresponding new generic function object that will han-dle the rest of the arguments.

Unfortunately, the necessary switch statements on argument types are sensi-tive to the introduction of new types5. Yet, in the case of single-dispatch sim-ulation, new dispatching methods (e.g., addComplex) are necessary as well.

The goals of the Visitor pattern [Gamma et al.94] can be achieved with a com-bination of generic Function Object and any iteration mechanism. A genericfunction object chooses the appropriate code for each combination of opera-tion and element type. Once the generic Function Object has done the dis-patch, the exact element type is known and access to the full interface is pos-sible. Between invocations, function objects can hold intermediate results,e.g., variable environments for a type-checking algorithm on abstract syntaxnodes.

A generic function object may even be realized as a type dispatcher, parame-terized with a set of function objects that actually perform an operation. Thisallows reuse of the dispatching part for various operations.

Visiting data structures with Function Object is acyclic w.r.t. data dependen-cies [Martin97] and does not force the visited classes to know about visi-tors [Nordberg96].

Finally, a generic function object is not restricted to dispatch on types only.It may also take the value of arguments into consideration. Consequently,one may represent musical notes and quarter notes by the same class. Thecorresponding objects will differ in a value, e.g., of attribute duration. Nev-ertheless, it is still possible to use a generic function to dispatch on this noterepresentation.

7.11 Known Uses

Apart from the uncountable uses of Function Object in functional programmingand SCHEME [Abelson & Sussman87], there are many truly object-oriented uses:SMALLTALK [Goldberg & Robson83] features blocks as true closures with implicitbinding of free variables. SATHER provides bound routines [Omohundro94]. AL-GOL 68’s thunks for call-by-name parameter passing are also closures but bind freevariables dynamically in the calling environment. JAVA 1.1 features “inner classes”which are essentially (anonymous) closures used, e.g., for callbacks [Sun97]. TheEiffel Booch Components uses Function Object for searching, sorting, transformingand filtering of containers [Hillegass93]. The Standard Template Library, whichwas adopted as part of the standard C++ library, uses Function Object to inlineoperations for arithmetic, logic, and comparison [Stepanov & Lee94].

5A more flexible approach is to use dynamically extendible dictionaries that associate types withcode.

Page 129: A Functional Pattern System for Object-Oriented Design

7.12 Related Patterns 111

7.12 Related Patterns

7.12.1 Categorization

Objectifier: A function object, like Objectifier, does not represent a concrete objectfrom the real world [Zimmer94], though one can reasonably take business-transactions for real. Function Object is very similar to Objectifier, in that itobjectifies behavior and takes parameters during initialization and call. Percontra, clients “have-an” objectifier, while clients “take-a” function object.The latter is a uses, not a has-a relationship.

Command: A procedure object which does not take any arguments after cre-ation and produces side-effects only boils down to the Command pat-tern [Gamma et al.94]. One key aspect of Command is to decouple an invokerfrom a target object. Function objects typically do not delegate functionality.Rather than delegating behavior to server objects they implement it them-selves. So, function objects normally do not work with side-effects, but returntheir computation as an argument-application result. Nevertheless, functionobjects also can be used for client/server separation, i.e., as Call-back func-tions. In addition to Command, invokers are then able to pass additionalinformation to function objects by supplying arguments.

State,Strategy: Function Object, State, and Strategy [Gamma et al.94] are concerned with

encapsulating behavior. A decision between them can be based on concernssuch as:

• Who is responsible for changing the variable part of an algorithm?The State pattern manages the change of variability autonomously.Function objects are explicitly chosen by the client. Strategies are chosenby the client also, but independently of operation requests. Requestingan operation can rely on a Strategy choice made some time earlier.

• Is it feasible to impose the same interface on all variations?If the available Strategies range from simple to complex, theabstract Strategy must support the maximum parameter inter-face [Gamma et al.94]. Function Object avoids this by partial param-eterization.

• Does the combination of common and variable part constitute a usefulconcept?The State pattern conceptually represents a monolithic finite state ma-chine, so the combination of standard- and state-dependent behaviormakes sense indeed. Strategies are a permanent part of general behav-ior and thus provide default behavior. Here, the combination acts as abuilt-in bookkeeping for the selection of the variable part. Function ob-jects, however, take part in the “takes-a” relation. A function object and

Page 130: A Functional Pattern System for Object-Oriented Design

112 7 Function Object

its receiver are only temporarily combined in order to accomplish a task.Function objects, nevertheless, can have a long lifetime, such as beingmemorized in attributes or representing unevaluated data.

Visitor: Data structures need to know about Visitors because they have to providean accept method [Gamma et al.94]. Sometimes this is undesirable becauseof the so-introduced mutual dependency between data structures and Vis-itors [Martin97]. When the data structure is not available as source code,it is even impossible to add the accept method. A combination of Iteratorand generic Function Object (see also the Transfold pattern in chapter 10 onpage 163) avoids these drawbacks, while providing the same functionality asVisitor:

• It frees the data structures from needing to provide the operations them-selves.

• It differentiates between types in the data structure. The generic functionobject chooses the appropriate code for each combination of operationand element type.

• It allows heterogeneous interfaces on the data elements. Once thegeneric function object has done the dispatch, the exact element typeis known and access to the full interface is possible.

• It concentrates operations at one place and provides a local state forthem. Between invocations, function objects can hold intermediate re-sults, e.g., variable environments for a type-checking algorithm on ab-stract syntax nodes.

Of course, the type switches are sensitive to the addition of new structureobjects. However, if the structure is unstable, Visitor is not recommendedeither [Gamma et al.94].

Lazy Object: An unevaluated, i.e., still parameter awaiting function object is a lazyobject (see chapter 8 on page 115). It represents a suspended calculation andmay, therefore, be used for delaying calculations.

7.12.2 Collaboration

Iterator: Function objects allow the use of data from inside (elements) and outsidethe collection (previous arguments). There is no collaboration between Com-mand and Iterator, since Command does not take arguments.

Adapter: Function Object extends the use of Parameterized adapters as described inthe implementation section of the Adapter pattern [Gamma et al.94] fromSMALLTALK to any object-oriented language.

Page 131: A Functional Pattern System for Object-Oriented Design

7.12 Related Patterns 113

Chain of Responsibility: Pairs of test- (check responsibility) and action function ob-jects can be put into a Chain of Responsibility in order to separate responsibil-ity checks from the execution of tasks. Function Object allows replacing theinheritance relationship between Links and Handlers [Gamma et al.94] withobject-composition.

Observer: Instead of receiving a notification message from a subject an observermay register call-back procedure objects at the subject that will perform neces-sary updates. This scheme adds another variant to the push- and pull- modelsof the Observer pattern.

Void Value: A void value (see chapter 11 on page 191) may define the default func-tion to be used for initializing a parameterized structure.

Void Value,Value Object: A function object does not have to copy void value (see chapter 11 on

page 191) or value object (see chapter 9 on page 149) arguments because theyensure immutability anyway.

7.12.3 Implementation

Composite: Standard and composed function objects can be uniformly accessedwith the Composite pattern [Gamma et al.94]. A composite function forwardsarguments to its component functions. A tuple-Composite applies all func-tions in parallel to the same argument and thus produces multiple results.Several reduce functions (e.g., and of section 7.3.2 on page 96) may somehowfold all results into one output.

A pipeline-Composite applies each function to the result of its predecessorand thus forms a calculation pipeline.

Prototype: Often it is useful to distribute the accumulated state of a function objectto different clients. For instance, a command for deleting text can capture theinformation whether to ask for confirmation or not. However, when placedon a history list for undoing, different commands must maintain differentpointers to the deleted text. Consequently, Prototype can be used to clonepre-configured function objects which should not share their state any fur-ther. With this regard, any partially parameterized function object can be in-terpreted as a prototype for further applications (see example about the costlycalculation in section 7.8 on page 102, Efficiency).

Chain of Responsibility: Generic Function Object can employ a Chain of Responsibil-ity for argument type discrimination. Chain members check whether they canhandle the actual argument type. This enables a highly dynamic exchange ofthe dispatch strategy.

Page 132: A Functional Pattern System for Object-Oriented Design

114 7 Function Object

Page 133: A Functional Pattern System for Object-Oriented Design

115

8 Lazy Object

Ungeduld hat haufig Schuld.– Wilhelm Busch

8.1 Intent

Defer calculations to the latest possible point in time. This is useful for resolvingdependencies, calculation complexity reduction, increased modularity and infinitedata structures.

8.2 Also Known As

Stream [Ritchie84, Abelson & Sussman87, Edwards95], Pipes and Filters Architec-ture [Meunier95b, Buschmann et al.96], Pipeline [Posnak et al.96, Shaw96].

8.3 Motivation

Typically, the specification of a solution to a problem inevitably also makes a state-ment about the order of solution steps to be applied1. In this context, it does notmatter whether order is expressed as a sequence of events in time or as a chain ofdata dependencies. The crucial point is that we welcome to be able to order sub-solutions in order to decompose problems into easier sub-problems. But sometimesoverstating the order of events yields complex systems.

8.3.1 Problem

Listening to music at home usually amounts to the problem of converting a pitstructure on a flat disc to an alternation in air pressure. In this example we concen-trate on the part to be accomplished by the compact disc player (see figure 8.1 onthe next page).

We may think of the surface-reading-unit (SRU) as some electronic circuit thatcontrols the spinning speed of the disc, tracks the laser, and produces a digital bit

1Note, however, that especially the logical paradigm allows being very unspecific about order.

Page 134: A Functional Pattern System for Object-Oriented Design

116 8 Lazy Object

ConverterAnalogDigital/

UnitReadingSurface

C Dto Amplifier

Figure 8.1: Schematic CD-player

stream. These bits are actively fed into the digital-analog-converter (DAC). Let usassume the DAC has a small buffer which it permanently consumes and resorts toduring reading problems. Hence, the SRU has to watch the buffer for underflow— causing speed up of reading — and for overflow — resulting in slowing downreading. In this scenario, both SRU and DAC modules are closely coupled. TheSRU pushes bit per bit into the DAC but always requests the DAC for its buffercondition. Any alternative SRU to be used in the future must know about the bufferquery interface of the DAC module. Vice versa, a future DAC replacement mustaccept bits through a protocol as specified by the current SRU. This coupling limitsthe freedom in designing future implementations.

Furthermore, it will not easily be possible to insert bit processing modules inbe-tween SRU and DAC (see figure 8.2). Provided we still want to keep the bit bufferin the DAC module, the error correction unit (EC) must fake a buffer protocol forthe surface reading unit and the DAC buffer condition must be fed back throughall components.

8.3.2 Solution

Instead of handshaking the bits through a “push and request” procedure call pro-tocol, we better recognize the data flow architecture of the schematic compact discplayer. We should observe that a unit either produces (SRU), transforms (EC andDF), or consumes (DAC) a stream of bits (see figure 8.2).

ReadingSurface

UnitAnalog

Converter

Digital/DigitalFilterCorrection

Error

Figure 8.2: Plug-in modules in a bitstream architecture

With this view, it is most natural to let the DAC request further bits from itspreceding unit whenever its (self managed) buffer runs out of data. Driven by thedemand of the DAC each unit generates bits, possibly by pulling further data fromits preceding unit. In this scenario the modules are much less coupled since buffermanagement is solely accomplished by the DAC itself. The interface between the

Page 135: A Functional Pattern System for Object-Oriented Design

8.3 Motivation 117

modules consists of the stable notion of a bitstream. Ergo, alternate implementa-tions or future extensions (like a digital deemphasize unit) can be easily exchanged,shuffled, and inserted. Data sources and transformers are lazy in that they outputdata only if required by a data sink. Compare this to the model before, where theSRU pushed data to the DAC while managing the DAC’s buffer load.

A lazy data source does not need to know about the existence and nature ofa receiving unit at all. Moreover, the compact disc’s control panel now needs tocommunicate with the DAC only. Instead of directing a press of the stop button tothe SRU it now informs the DAC. Control panel and DAC communicated beforealso (e.g., for digital volume control), therefore, the cohesion between control paneland DAC increased while the coupling of control panel to other units decreased.With the data driven approach, the architecture can now be divided into layers thatdepend on each other in one direction only.

We were able to decouple a data source and a data sink since we abandoned theidea of explicitely handshaking each single piece of information and adopted thenotion of a stream, representing all information that is still to come at once. JohnHughes uses a similar example of separating an algorithm to compute approxima-tions to numerical solutions from the controlling issues like relative or absolute pre-cision reached [Hughes87]. By representing successing approximations as a streambetween generator and controlling module he was also able to achieve a higherdegree of modularization.

Without lazy evaluation we are often forced to either sacrifice modularity orefficiency. Suppose, we want to feed a server object with an argument whose eval-uation requires considerable effort:

server.update(costlyCalculation(a, b));

The server does not always need the argument to perform its update operation.With a lazy calculation everything is fine. With a standard calculation that is al-ways performed, however, we either accept unnecessary calculations or defer thecalculation to the server:

server.calculateAndUpdate(a, b);

While the efficency issue is apparently solved since the server can decide to do thecalculation or not, modularity was severely hurt.

1. The server now contains a calculation that is probably alien to the abstrac-tion it previously represented.

2. The server now depends on two argument types that it did not need toknow before.

It is amazing that the initial version above manages to combine both modularity(separation of concerns) and efficiency (avoiding unnecessary calculations).

A lazy attitude is of special importance when a (possibly infinite) stream is gen-erated by referring to itself. A popular example are the so-called hamming num-bers. They are defined as the sorted list of all numbers with prime factors two,

Page 136: A Functional Pattern System for Object-Oriented Design

118 8 Lazy Object

three, and five [Bird & Wadler88]. A functional program to generate all hammingnumbers — as given by definition 8.1 — clearly shows that hamming numbers canbe generated by depending on themselves.

hamming = 1 : merge(map(∗ 2) hamming) (8.1)(merge(map(∗ 3) hamming) (map(∗ 5) hamming))

merge(x : xs) (y : ys) = x : merge xs ys, x = y

merge(x : xs) (y : ys) = x : merge xs(y : ys), x< y

merge(x : xs) (y : ys) = y : merge(x : xs) ys), y< x

Figure 8.3 shows the unfolding of function calls into a sequence of numbers.

hamming = 1 : merge(map (* 2) , merge((map (* 3) , map (* 5) ))

hamming = 21 : : merge(map (* 2) , merge((map (* 3) , map (* 5) ))

hamming = 32 :1 : : merge(map (* 2) , merge((map (* 3) , map (* 5) ))

hamming = 43 :2 :1 : : merge(map (* 2) , merge((map (* 3) , map (* 5) ))

hamming = 4 :3 : 52 :1 : : merge(map (* 2) , merge((map (* 3) , map (* 5) ))

: merge(map (* 2) , merge((map (* 3) , map (* 5) ))6:5:4:3:2:1hamming =

Figure 8.3: Generating hamming numbers

Each of the three prime factor multipliers (mapinvocations) maintains its ownaccess point to the already generated list of numbers. Note that the recursive calls

Page 137: A Functional Pattern System for Object-Oriented Design

8.4 Applicability 119

of map to the remaining hamming numbers must be suspended until a sufficientamount of numbers has been generated. If recursive calls referred to function callsagain, rather than access already generated concrete numbers, nothing at all wouldbe generated due to an infinite recursion on function calls. This observation alsoemphasizes that in order to make evaluation really lazy — as opposed to just non-strict — we also need to cache generated results. Any element should be calculatedonly once, no matter how often accessed. In an object-oriented implementation thismemoization effect is achieved by maintaining caches and/or replacing suspensionobjects with value objects.

While streams have extensively been used as an example for lazy objects, thisshould not create the impression that Lazy Object is about streams only. Simplyput, streams are just lazy lists. All the same, we may have lazy trees or whatevertype of lazy structure. A lazy tree could be the result of a depth first search traversalof a graph. Such a tree would traverse only as much of the graph as needed forthe tree part actually accessed [King & Launchbury93]. A lazy object is not evenrequired to represent a collection. Any operation that can reasonably be delayed isa candidate to be become a method of a lazy object.

8.4 Applicability

• Unknown demands. Often, the amount of certain data needed is not knownbefore that data is actually accessed. We might, for instance, repeatedly ac-cess fibonacci numbers in order to achieve an evenly workload distributionfor the parallel evaluation of polynomials. An upper bound is difficult to es-timate and we do not want to waste time and space for providing fibonaccinumbers that will never be accessed. The solution is to generate them lazily,i.e., calculate them on demand. Note that although we sometimes might be ina position to provide enough space for some data to be calculated in advance,nevertheless infinite data (like fibonacci numbers) would force us to fix anartificial upper bound.

• Up-front declaration. The freedom of not needing to care about unnecessaryevaluations allows the adoption of a declarative style of programming. It ispossible to decompose a routine’s arguments or make recursive calls to partsof them without concern whether the results will be needed [Jeschke95]. Thisstyle is clearer if the routine needs to multiply access the results. Otherwise,the repeated decomposition code might even require a distributed cache man-agement, if it is not known which branch — of several — will cause the firstevaluation and calculations should be made at most once.

• Dependency specification. Execution of lazy functions follows dynamic datadependencies. As a result, even statically circular definitions can be specified,without regard for execution order. For instance, a machine code generatorneeds to determine the destination address for a forward jump. At the timethe forward jump is generated, the destination address is not known. A lazy

Page 138: A Functional Pattern System for Object-Oriented Design

120 8 Lazy Object

function does not care because it will not evaluate until the destination ad-dress is actually inspected (e.g., by accessing the code after its generation). Atthis time, however, the forward jump can be completed since code generationeither already proceeded beyond the destination address or it will be forcedto do so by the inspection of the forward address.

Similarly, one may use an implicit evaluation order for the generation of awavefront in a table. All that is needed are the constant values for the tableborders and the main equation, e.g., ai j = ai−1 j +ai−1 j−1 +ai j−1 [Hudak89].

• Partial evaluation. When a composed operation, such as retrieving all inter-face information from a programming language library, is evaluated lazily,this may amount to a partial execution of the operation only. When a compiler— for a given source code — does not need to look at the whole library infor-mation, because only a part of the library was used, then it will explore onlyas much interface information as needed [Hudak & Sundaresh88]. Unneededinformation will not be retrieved from disc nor extracted from archives.

• Module decoupling. When modules should exhibit loose coupling but, never-theless, an interleaved inter module execution is desirable, then use a streamto connect the modules. For instance, a parser can be connected to a scannerthrough a stream of tokens. Parser and scanner will alternate their execution,driven by the parser’s demands.

• Co-routines. Co-routines basically represent suspended behavior. The sameeffect can be achieved by using a stream generating all co-routine states insuccession. As with co-routines, clients do not need to care about state man-agement and behavior scheduling which all happens behind the scenes.

Do not apply Lazy Object, in case of

• Strict ordering. If one needs to rely on a fixed evaluation order (e.g., involvingside-effects), a lazy evaluation strategy is inappropriate. Lazy evaluations aredata driven and may vary their execution order from application to applica-tion.

• Limited memory. Suspensions of function calls may claim substantial amountsof heap memory. In the unlikely case (since object-oriented programmingnormally implies heap oriented memory use, anyway) you need an emphasison a stack based memory allocation, you will not be able to draw from thebenefits of Lazy Object.

• Time constraints. Lazy evaluation may cause unexpected and unevenly dis-tributed claims of computing resources. If you need to depend on a calcula-tion complexity that is easy to predict and fixed with regard to an algorithm— rather than depending on an algorithm’s usage — you should prefer eagerevaluation.

Page 139: A Functional Pattern System for Object-Oriented Design

8.5 Structure 121

8.5 Structure

Value

request : LazyObjectaccess : T

Client

request : LazyObjectaccess : T

LazyObject

access : Trequest : LazyObject

Suspension

Figure 8.4: Structure diagram

8.6 Participants

• Client

– issues a request to LazyObject (e.g., calling tail on a stream of hammingnumbers).

– accesses Suspension and later Value for a value (e.g., calling item on astream of hamming numbers).

• LazyObject

– provides an interface for requests and accesses.

– creates and returns a Suspension on request.

• Suspension (e.g., representing a stream’s tail)

– implements LazyObject ’s interface.

– represents a suspended calculation.

– calculates a result when accessed.

– creates a Value for further accesses.

• Value (e.g., a cached hamming number)

– implements LazyObject ’s interface.

– caches the result calculated by Suspension .

Page 140: A Functional Pattern System for Object-Oriented Design

122 8 Lazy Object

request

aValue

Repeated Access

Lazy Call

Data Access

aClient

access

aSuspension

evaluatemake

aLazyObject

make

access

Figure 8.5: Interaction diagram

8.7 Collaborations

• A client issues a request to a lazy object.

• The lazy object creates a suspension and returns it to the client.

• Some time later the client accesses the suspension.

• The suspension calculates a result (possibly accessing the lazy object), returnsit to the client, and creates a value for future accesses2.

• Future client accesses are made to the value that caches the calculation result.

8.8 Consequences

• Initialization. The best place for initialization values (e.g., return 1 as the ra-dius of a fresh circle) is the data abstraction they belong to [Auer94]. Agetter-routine can calculate an initial value whenever it is called the first time.Hence, initial values will not be calculated unless they are actually needed.Also, setter-routines may provide space for received arguments, only whenactually asked to do so.

2For instance, a stream element will convert its successor to a value when it is safe to do so.

Page 141: A Functional Pattern System for Object-Oriented Design

8.8 Consequences 123

• Data generation. Lazy generation of data structures does not require artificialupper bounds in order to restrict time and space investments. The calculateby need approach allows specifying all as the upper bound even in the caseof infinite data structures.

• Data-flow architecture. Streams emphasize data flow rather than control flow.A system based on streams is determined by the types of modules and the in-terconnections (streams) between the modules [Manolescu97]. Delayed eval-uation and streams nicely match because delayed evaluation inherently pro-vides the intertwined processing of data, required for interconnected streams.Note that connected streams do not necessarily operate in a synchronizedfashion. A pipeline with a Huffman encoding3 inbetween (e.g., to simulate adistorted transmission via a serial connection), will do much more process-ing at the two ends of the bitstream due to the distribution of one elementinto several bit elements representing its Huffman encoding. Lazy evalua-tion, however, will make this inner stream automatically run faster than theoutermost ends.

A chain of components, such as chain= double◦filter(< 81)◦square, can be as-sembled without worrying about the size of lists to be processed or specifyingloops with exit conditions [Braine95]. One may also reason about such chainsand, for instance, simplify the above to chain= double◦square◦filter(< 9)4, inorder to save operations.

• Modularity. The ability to separate what is computed from how much of it iscomputed is a powerful aid to writing modular programs [Hughes87]. Thecontrol over data consumption can be separated from the issues involved withdata generation. A lazy stream allows separating iteration loops (deciding onhalting and possibly using other streams) and iterations (including elementgeneration and iteration strategy). See pattern Transfold on page 163.

Modules can be decoupled but not necessarily implying a loss of efficiency.On the contrary, through partial evaluation, efficiency can actually be gained.Lazily connected modules never cause each other to perform beyond the ab-solute minimum to produce the results of the driving module.

• Partial evaluation. With respect to streams it is worthwhile noting that onemay evaluate the spine of stream without causing evaluation of the under-lying stream elements. For instance, the call sieve @ (nats.tail) doesnot evaluate the first natural, i.e., “1” since it directly proceeds beyond to thesecond member “2”. This can be useful when advancing the state is easy butthe actual calculation of elements is costly and not all elements are required.

• Information hiding. A lazy object hides its delayed internals from clients. Aclient is never requested to treat lazy object specially, e.g., by passing a

3A variable-length code, discovered by David Huffman.4Provided processed numbers are positive only.

Page 142: A Functional Pattern System for Object-Oriented Design

124 8 Lazy Object

dummy parameter for evaluation5. Not every lazy computation can be ex-pressed like this, though. In certain cases it might be necessary to use lazyfunction objects, e.g., as elements of a stream. For instance, laziness may beachieved between a program and an operating system that interchange re-quests and responses (see figure 1.4 on page 28), only if a response is struc-tured. An atomic response, e.g., an integer, must be encapsulated in a functionsince it can not be calculated until a request has been processed.

Streams also hide their element generation strategy from clients. They may

1. directly represent stream elements (e.g., a chain of StreamVal s).

2. precompute and buffer groups of elements.

3. compute elements on demand.

All these strategies can be mixed in stream combinations. This is pos-sible since through their access restriction — one may only access thehead of a stream — two dual views on streams are possible without con-flict [Edwards95]. On the one hand, a stream appears as a channel for trans-mitting data. On the other hand, a stream can be regarded as the entirety ofall elements to come.

Summarizing, streams are an excellent example for interface reuse.

• Value semantics. Lazy results are actually lazy values. Repeated evaluationwill not yield different (state dependent) results. Hence, Lazy Object nicelyuses state (for memoizing results) in a referentially transparent manner. Onemay use this kind of clean state to model efficient non-single-threaded amor-tized data structures [Okasaki95c], without the need to resort to side-effects.

• Iteration. The close resemblance of a Stream interface (see section 8.10 onpage 130) to that of an Iterator [Gamma et al.94] suggests to use streams asan intermediate language between collections and iterators. Then, streamswould provide a linear access to both finite and infinite data collections.Clients, in general, do not have to care whether streams are finite or not withthe exception that operations on infinite streams — such as naıvely trying todetect “42” in a list of primes — may not terminate.

• Inter-collection language. Actually, streams are a generalization of collectioniterators since they can be generated by far more alternatives. Moreover,streams can be used to generate collections. Consequently, streams are anintermediate language for the conversion of data structures into each other(see pattern Transfold on page 163).

• Persistence. Streamable collections could also easily made persistent withoutrequiring additional mechanisms, such as Serializer [Riehle et al.97].

5As it is the case with the explicit delay styles using unit and delay of ML and SCHEME.

Page 143: A Functional Pattern System for Object-Oriented Design

8.8 Consequences 125

• Stream configuration. The ease of exchanging stream components facilitatesend user programming [Manolescu97]. Instead of recoding an application bya programmer, such a flexible system allows reconfiguration by the user.

• Parallel execution.

+ A pipeline of interconnected streams can be processed in parallel since itrepresents a pure dataflow architecture [Buschmann et al.96].

− Eager processing can execute operations in parallel to make their resultsavailable prior to their usage. Lazy evaluation is directed by data de-pendencies and, thus, normally does not allow for exploiting peaks ofcomputing resources for future use.

• Workload distribution. The increased modularization achieved by streamscan be exploited to distribute development efforts nicely into autonomousgroups [Manolescu97].

• Execution profile. Lazy operations can lead to a peak in computation perfor-mance. Insertions to a lazily sorted list nicely require constant time only.Yet, the first reading access will have to resolve all necessary sorting (whichmay or may not mean to sort the whole list). This behavior — akin to non-incrementally garbage collected applications — should be kept in mind if amore evenly distribution of work is aspired.

Sometimes, however, it can be desirable to postpone operations. For instance,system start up time should be kept minimal while more elaborate systeminitialization will happen as required [Wampler94].

• Time efficiency.

+ The computational complexity of a lazy algorithm is determined by itsusage rather than its intrinsic properties. It is possible to use an O(n2)sorting algorithm (insertion-sort) to obtain the minimal element from acollection in O(n) time. Access to the first element of the list will notcause the costly sorting of the rest of the list.A collection which is traversed with a predicate and a lazy and operatorwill not be fully visited, if predicate application on one of the elementsyields false.

+ Speed is not only to be gained by avoiding unnecessary calculationsbut also by avoiding to calculate values more than once. For instance,a fibonacci number stream, modeled as the addition of two fibonaccistreams (fibs= 0 : 1 : streamadd fibs tl(fibs)), exhibits linear6 time com-plexity through the inherent caching of stream values. With this respect,

6As opposed to standard exponential time behavior.

Page 144: A Functional Pattern System for Object-Oriented Design

126 8 Lazy Object

Lazy Object is in close relationship to dynamic programming7. For ex-ample, to calculate all squared natural numbers one can choose an incre-mental approach by deriving each new value from its predecessor: Withpred= n2, it follows that (n+1)2 = pred+2n+1, avoiding the costly squar-ing operation for each element. Speed penalties introduced by closurecreations [Allison92] can be more than compensated by this incrementalcomputation option.

+ It is efficient to pass lazy objects (like streams) around since only refer-ences will be moved. There is no need to pass lazy objects with copysemantics since they are immutable anyway.

+ It is often possible to avoid repeated traversals of a structure if resultsare lazily constructed. In order to replace all values in a tree with theminimum value one typically needs to traverse the tree twice. Once,for determining the minimum value, and twice for replacing all valueswith the minimum. With a lazily computed minimum value, however,one can replace all tree values with a value yet to be computed [Bird84].The same traversal which is used to replace the values is also used tobuild a hierarchical function structure of min functions over the wholetree. When the tree is accessed afterwards, the minimum function willbe evaluated once and the obtained value will be used for each tree valueaccessed.

− When streams are designed to be interchangeable and therefore are re-quired to share a common element format, this may impose conversionoverhead. For instance, UNIX commands need to parse ASCII8 repre-sentations of numbers, calculate, and write out an ASCII representationagain [Buschmann et al.96]. Object-oriented streams, however, open upthe possibility to defer conversions to the elements themselves. Then,special stream element subtypes could avoid any conversions at all.

− Access to lazy values is indirect. Typically, method dispatch redirectscalls to either existing values or function calls. Alternatively, flags haveto be checked whether cached values already exist.

• Space efficiency.

+ Streams can help to restrict the amount of memory needed. Consider adata source that is transformed into another structure that requires ex-ponential space. If this structure is to be processed by a function thatextracts a linearly sized result, it is beneficial to model the structure gen-erating process as a stream. Then the extraction function is applied tothe stream, resulting in the same linearly sized result, without causinga transient exponential space requirement. Thus, useful intermediate

7Also called memoing or tabulation [Abelson & Sussman87].8American Standard Code for Information Interchange.

Page 145: A Functional Pattern System for Object-Oriented Design

8.9 Implementation 127

structures [King & Launchbury93] do not imply infeasible space over-head.

+ Since lazy objects are immutable they can be shared by multiple clientswithout requiring duplication in order to avoid interferences.

− As can be seen in section 8.10 on page 130 (figures 8.12 on page 138, 8.13on page 139, 8.14 on page 141 and 8.15 on page 142) the representation ofa stream with objects can be storage intensive (see section 8.9 for allevia-tions). Also, the space required by stream suspensions might be overkill(see next bullet Closure overhead). Care should be taken not to unneces-sarily keep references to stream suspensions. This would prevent theirearly removal by garbage collection.Although object overhead encumbers garbage collection, clever mem-ory management can make lazy languages outperform eager and evenimperative languages [Kozato & Otto93].

• Closure overhead. Sometimes, more overhead through closure management isintroduced, than is gained by computational savings. Accessing the secondelement of two lazily added lists [1,2,3,4] + [5,6,7,8], results in, (1+ 5) : 8 :([3,4] + [7,8]), creating two suspensions for one standard addition and a de-ferred list addition respectively. Especially, when all elements of a structurewill eventually be evaluated anyway, it can be worthwhile to avoid the cre-ation of space and time (through creation and management) consuming sus-pensions.

• Event handling. A pull driven stream model as presented here cannot handleprioritized or asynchronous events. It is, however, possible to either use pushdriven streams [Buschmann et al.96] or to apply an Out-of-band and In-bandpartition [Manolescu97]. The latter approach divides an interactive applica-tion into user interaction (events) and data (stream) processing parts.

8.9 Implementation

On a first reading the reader might want to skip this section. After examination ofthe sample code in section 8.10 the following implementations details are easier todeal with.

• Stream functions. It is very interesting to note that the value, step, and eos in-terface of ContFunc (see section 8.10 on page 130) is justified by a categori-cal interpretation of streams as coinductive types [Jacobs & Rutten97]. Here,streams over a type A are modeled as a state of type B (state of a ContFuncobject) and two functions: Function h : B→ A produces a new stream element(value) and function t : B→ B produces a new state (step).

Then, an anamorphism on h and t (a stream generating function) is definedto be the unique function f : B→ Str Asuch that diagram 8.6 on the next page

Page 146: A Functional Pattern System for Object-Oriented Design

128 8 Lazy Object

A×B�〈h, t〉

B

A×Str A

idA× f

?�〈head, tail〉 Str A

?

f

Figure 8.6: Definition of an Anamorphism

commutes. A stream generator f , hence, needs to build a list cell (eventuallyStreamValue ) from calling an element generator h (value) and itself composedwith a state transformer t (step): f = Cons◦ 〈h, f ◦ t〉 [Pardo98b].

This view concerns infinite streams only. Introducing finite streams involvesconsideration of a function that determines the stream’s end (eos of ContFuncand last of Stream, respectively). For a further discussion the reader is referredto [Meijer et al.91].

• Avoiding tail suspensions. Some stream generating functions may create a suc-cessing stream state without waiting for arguments to become available. Forexample, it is not necessary to suspend a tail call to a StreamFunc which refersto a NatFunc (see section 8.10 on page 130). One can save the creation of aStreamTail and its later removal by introducing an optimized stream function(StreamFuncOpt ) that redefines the tail method of StreamSuspension and theforceTail method of StreamFunc :

class StreamFuncOpt[G]inherit StreamFunc[G] redefine tail, forceTail endcreation makefeature

tail : Stream[G] isdo

if not tailIsValue thenif tailCache/=Void then

if tailCache.isNotSuspended thentailCache:=tailCache.toValue;tailIsValue:=True;

end;elseif not last then

tailCache:=forcedTail;end;

end;

Page 147: A Functional Pattern System for Object-Oriented Design

8.9 Implementation 129

Result:=tailCache;end;

feature {None}forceTail : Stream[G] isdo

!StreamFuncOpt[G]!Result.make(contFunc.step);end;

end

Instead of creating a stream tail suspension, tail directly creates a streamfunction successor by calling forceTail .

A client can make advantage of this optimized scheme by declaring, e.g.,

nats : StreamFuncOpt[Integer];

Creation and usage of the optimized stream is exactly as before.

Clients can be made unaware of the existence of two different StreamFuncby either going via an application syntax function like Pipe (see section 8.10on the next page) or deferring creation of stream functions to continuationfunctions.

• Class versus object adapter. In order to improve clarity of presentation I usedthe object version of Adapter [Gamma et al.94] for class StreamFunc (see sec-tion 8.10 on the following page). What it essentially does is to translate aservice from continuation functions (value, step, eos) to the requirements ofStreamSuspension (forceItem, forceTail, last).

Alternatively, one can use the class adaptor version for StreamFunc . Therewould be several stream function heirs to StreamSuspension that would alsoinherit from an individual continuation function respectively. On the onehand this would less cleanly separate stream functionality (e.g., forcing) fromcontinuation functionality (e.g., argument processing). On the other hand, aconsiderable amount of object creation and storage would be saved. In fig-ure 8.15 on page 142 all pairs of StreamFunc and ContFunc objects wouldcollapse into single objects.

On may even think about dropping any adaption at all and make continua-tion functions direct heirs to StreamSuspension . Yet, this would burden im-plementors of new continuation functions to create objects of their respectivefunctions in the step routine. Either class or object adaption scheme nicelytakes care of this.

• Storage requirements. Also for reasons of presentation clarity I choose a directmodeling of streams with objects. In cases where the space requirements ofthe object model presented in section 8.10 on the following page are pro-hibitive, one can evade to more compact representations. Streams can, for

Page 148: A Functional Pattern System for Object-Oriented Design

130 8 Lazy Object

instance, maintain a shared generic array of elements and use an internal cur-sor for their actual position. Access suspensions could then be much more ef-ficiently be represented as an internal counter that increases with tail accessesand has to be decreased — inducing function evaluation — before elementaccess.

8.10 Sample Code

We will now consider the implementation of lazy streams in detail. Again, notethat a stream is just an example for Lazy Object and that there are many otherapplications for Lazy Object besides streams (see section 8.4 on page 119).

The most important issue to establish upfront is the stream interface9 to clients:

deferred class Stream[G]

featureitem : G is deferred endtail : Stream[G] is deferred endlast : Boolean is deferred end

feature {Stream}isNotSuspended : Boolean is deferred endtoValue : StreamVal[G] is deferred end

feature {StreamTail}forcedTail : Stream[G] is deferred end

end

StreamFunc

NatFuncarg = 1

contFunc

Figure 8.7: Naturalnumber stream

Class Stream plays the role of LazyObject (see figure 8.4on page 121) with the exception that it defers creation of sus-pensions to its heirs. For now, we do not care about the non-public features of Stream besides noting that toValue al-lows converting a stream element, which no longer needs tobe suspended, into a value (see class Value in figure 8.4 onpage 121).

We start simple by trying to establish a stream of natu-ral numbers. In this case our Suspension (see figure 8.4 onpage 121) will be a function (represented by class NatFunc )that yields the next natural number. Heading for a generalscheme we introduce an indirection with StreamFunc thatuses any ContFunc to generate a stream element (see fig-ure 8.7) and make NatFunc an heir to ContFunc (see figure 8.8on the facing page).

9For brevity I omitted an out feature that is useful to display streams to the user.

Page 149: A Functional Pattern System for Object-Oriented Design

8.10 Sample Code 131

Client Stream

StreamSuspension

forceTail : StreamforceItem : G

tail : Streamitem : G

last : Booleantail : Streamitem : G

StreamTailStreamFunc

StreamValstep : Currentvalue : G

ContFunc

eos : Boolean

NatFunc

Figure 8.8: Using streams

A client then creates a StreamFunc by passing a NatFunc for initialization. Theclient can now access stream elements via the Stream interface. Requesting an itemfrom Stream is — in this case — handled by StreamFunc . StreamFunc inherits theimplementation of item from StreamSuspension :

deferred class StreamSuspension[G]inherit Stream[G]

featureitem : G isdo

if itemCacheFlag=False thenitemCache:=forceItem;itemCacheFlag:=True;

end;

Result:=itemCache;end;

Page 150: A Functional Pattern System for Object-Oriented Design

132 8 Lazy Object

tail : Stream[G] isdo

if not tailIsValue thenif tailCache/=Void then

if tailCache.isNotSuspended thentailCache:=tailCache.toValue;tailIsValue:=True;

end;elseif not last then

!StreamTail[G]!tailCache.make(Current);end;

end;

Result:=tailCache;end;

last : Boolean is deferred end

feature {Stream}isNotSuspended : Boolean;

toValue : StreamVal[G] isdo

If valueCache=Void then!StreamVal[G]!valueCache.make(item, tail);

end;

Result:=valueCache;end;

feature {StreamTail}forcedTail : Stream[G] isdo

if not isNotSuspended thentailCache:=forceTail;isNotSuspended:=True;

end

Result:=tailCache;end;

Page 151: A Functional Pattern System for Object-Oriented Design

8.10 Sample Code 133

feature {None}itemCache : G;itemCacheFlag : Boolean;

tailCache : Stream[G];valueCache : StreamVal[G];tailIsValue : Boolean;

forceItem : G is deferred endforceTail : Stream[G] is deferred end

end

Method forceItem (called by item ) is implemented by StreamFunc :

class StreamFunc[G]inherit StreamSuspension[G]creation make

featuremake(f : like contFunc) isdo

contFunc:=f;end;

feature {None}contFunc : ContFunc[G];

forceItem : G isdo

Result:=contFunc.value;end;

forceTail : Stream[G] isdo

!StreamFunc[G]!Result.make(contFunc.step);end;

last : Boolean isdo

Result:=contFunc.eos;end;

end

Page 152: A Functional Pattern System for Object-Oriented Design

134 8 Lazy Object

In our example the invocation of value on contFunc (see implementation offorceItem ) will return the current natural number:

class NatFuncinherit ContFunc[Integer]creation make

featurearg : Integer;

make (a : like arg) isdo

arg:=a;end;

value : like arg isdo

Result:=arg;end;

step : like Current isdo

!!Result.make(arg+1);end;

end

NatFuncarg = 1

StreamTailsuspensiontail

StreamFunc

contFunc

Figure 8.9: Suspension of a stream tail

Requesting the tail of a Stream usually results in the creation of a StreamTail (seefigure 8.8 on page 131 and figure 8.9). An item call on a StreamTail object forcesthe generation of a tail from suspension and delegates the item request to the resultof this operation. Again, the implementation of item in StreamSuspension will becalled and — this time — will use forceItem from class StreamTail :

Page 153: A Functional Pattern System for Object-Oriented Design

8.10 Sample Code 135

class StreamTail[G]inherit StreamSuspension[G]creation make

featuremake(s : like suspension) isdo

suspension:=s;end;

feature {None}suspension : Stream[G];

forceItem : G isdo

Result:=suspension.forcedTail.item;end;

forceTail : Stream[G] isdo

Result:=suspension.forcedTail.forcedTailend;

last : Boolean isdo

Result:=suspension.forcedTail.last;end;

end

StreamFuncitem = 2

StreamFuncitem = 1

tail

NatFuncarg = 2

contFunc

Figure 8.10: Streamfunction unfolding

Delaying the computation of streamelements with StreamTail is crucial forself referring streams such as the ham-ming numbers stream. Not before thehamming number represented by thehead of the suspension is accessed isit safe to access any numbers immedi-ately required by the tail of the suspen-sion (see section 8.3.2 on page 116).

Yet, given a suitable stream genera-tion function and the optimization pre-sented in section 8.9 on page 127, a di-rect “stepping” of the current NatFuncobject can be achieved. The optimizedforceTail method of StreamFuncOpt directly calls the step method of its continuation

Page 154: A Functional Pattern System for Object-Oriented Design

136 8 Lazy Object

function and creates a new StreamFuncOpt with the obtained result. Hence, theintermediate creation of a StreamTail object is avoided — in this case without lossof generality.

Calling tail in the situation of figure 8.7 on page 130 creates the scenario in fig-ure 8.10 on the preceding page. In this diagram — as well as in the following objectdiagrams — creation lines are drawn were requests on objects (e.g., tail) induce thecreation of objects. Presenting the actual circumstances would unnecessarily com-plicate the pictures.

Further tail calls will finally replace StreamFunc s with StreamVal s (see fig-ure 8.11).

NatFuncarg = 4

item = 2StreamFunc

item = 1

tailStreamVal

tailStreamFunc

item = 4

tailStreamVal

item = 3

contFunc

Figure 8.11: Streamvalue insertion

StreamVal objects, i.e., stream values are not suspended stream elements andallow discarding obsolete StreamFunc objects while representing their cached val-ues.

Summarizing, we may observe that StreamTail s will be replaced by StreamFunc-tion s, which in turn will be replaced by StreamVal s.

With our just established stream of natural numbers it is not very difficult togenerate a stream of prime numbers. We make use of the Sieve of Erathostenes(see definition 1.5 on page 15) and obtain primes by filtering naturals. This time,however, we do not explicitely create a StreamFunc object but obtain the stream ofprime numbers by applying a Sieve function on naturals:

nats : StreamFunc[Integer];primes : Stream[Integer];sieve : Sieve;...!!sieve.make;primes:=sieve @ (nats.tail);

Class Sieve encapsulates the stream function creation process and provides a niceapplication syntax. Its purpose is to create a StreamFunc instance that refers toa SieveFunc (see figures 8.12 on page 138 and 8.16 on page 147). The resulting

Page 155: A Functional Pattern System for Object-Oriented Design

8.10 Sample Code 137

situation is exactly analogous to the NatFunc case. I deliberately did not use anintermediate application syntax function for natural numbers in order to make thepresentation of the underlying mechanisms as clearly as possible. However, clientsare released from dealing with StreamFunc objects and are given a more naturalway to deal with streams when provided with such adapter classes like Sieve .

For this purpose, class Pipe provides the generation of StreamFunc objects:

class Pipe[G, H]inherit Function[Stream[G], Stream[H]];creation init

featurepipeFunc : PipeFunc[G, H];

init(f : like pipeFunc) isdo

pipeFunc:=f;end;

infix "@" (s : Stream[G]) : Stream[H] isdo

!StreamFunc[H]!Result.make(pipeFunc @ s);end;

end

Class Sieve simply inherits Pipe and initializes pipeFunc to SieveFunc on creation(see also figure 8.16 on page 147).

The client code from above (sieve @ (nats.tail) ) produces the situationof figure 8.12 on the following page. Here, natural numbers are not using the op-timized StreamFunc in order to show that caching of tail values achieves that boththe first natural number and the sieve function refer to the same stream suspension.

Figure 8.13 on page 139 shows the same structure after several item and tail calls.It can clearly be seen that SieveFunc employs FilterFunc objects to filter the stream

of naturals:

class SieveFuncinherit PipeFunc[Integer]feature

value : Integer isdo

Result:=stream.item;end;

step : like Current islocal filter : expanded Filter[Integer];

divides : expanded Divides;

Page 156: A Functional Pattern System for Object-Oriented Design

138 8 Lazy Object

StreamTailsuspension

StreamFunctail

StreamFunc

SieveFuncstream

NatFuncarg = 1

contFunc

contFunc

Figure 8.12: Stream of primes

notf : expanded NotF[Integer];do

Result:=Current @(filter @ (notf @ (divides @ value)) @

stream.tail);end;

end

A FilterFunc will be generated by the application of Filter . Note how the three func-tion objects are declared to be of expanded type in order to safe explicit creation.

SieveFunc makes use of the PipeFunc abstraction that specializes a continuationfunction to a function with one stream argument. Heirs to PipeFunc profit from apredefined eos function and are immediately subject to the nice application syntaxprovided by Pipe (see above). Providing application syntax and specializing thecontinuation function interface, PipeFunc is an heir to both Function and ContFunc :

deferred class PipeFunc[G, H]inherit inherit Function[Stream[G], PipeFunc[G, H]];

ContFunc[H] redefine eosendfeature

Page 157: A Functional Pattern System for Object-Oriented Design

8.10 Sample Code 139

tail tail

stream

streamFilterFunc!divides = 2

NatFuncarg = 5

suspensionStreamTail

tailStreamFunc

item = 3

StreamFuncitem = 3

StreamFuncitem = 2

StreamFuncitem = 5

contFunc

streamSieveFunc StreamFunc

item = 5

FilterFunc!divides = 3

StreamFuncitem = 5

contFunc

contFunc

StreamFuncitem = 5

contFunc

Figure 8.13: Unfolded primes

stream : Stream[G];

init (s: like stream) isdo

stream:=s;end;

Page 158: A Functional Pattern System for Object-Oriented Design

140 8 Lazy Object

infix "@" (s : like stream) : like Current isdo

Result:=clone(Current);Result.init(s);

end;

eos : Boolean isdo

Result:=(stream.last);end;

end

See also figure 8.16 on page 147 for all relations between Client , Pipe , ContFunc ,and PipeFunc .

Requesting yet another prime from the structure of figure 8.13 on the page be-fore results in figure 8.14 on the facing page. Starting from the bottom it can beobserved that the current natural amounts to seven rather than five. The interme-diate six has been filtered out. Note that the StreamTail object between the factorthree and factor two filter has vanished and a new one was introduced right after anewly inserted factor five filter.

The same stream in a normalized state — resulting from some more item re-quests — is visible in figure 8.15 on page 142. Compared to figure 8.14 on thefacing page,

1. a StreamFunc object has been converted to a StreamVal object,

2. a chain of StreamFunc objects has been reduced to one StreamFunc object,

3. and the natural number source has been advanced to number eleven.

From the dynamics of stream implementation — which is well hidden from theclients of streams — back to the static structure of the encountered participants:Figure 8.16 on page 147 shows all classes involved in the presented prime numbersexample. Here, Client demonstrates the two ways to interact with stream functions.On the one hand it creates both NatFunc and StreamFunc and on the other handjust uses Sieve . The diagram also depicts that Pipe and PipeFunc support functionapplication syntax by inheriting from Function .

It is interesting to follow the creation chain from Sieve via SieveFunc to Filter ,which takes a predicate argument in order to create Filter1 that in turn creates aFilterFunc object and, finally on receipt of a stream argument, will create a Stream-Func object (via class Pipe ).

Note that the Stream subtree at the right part of the diagram exactly correspondsto the stream supporting classes of figure 8.8 on page 131. An abstracted version offigure 8.16 on page 147 with a client that deals with streams via intermediate streamfunctions only is presented in figure 8.17 on page 148. This diagram depicts the rich

Page 159: A Functional Pattern System for Object-Oriented Design

8.10 Sample Code 141

tailtail tail

streamSieveFunc

stream

StreamFuncitem = 5

tail

stream

streamFilterFunc!divides = 2

NatFuncarg = 7

contFunc

StreamFuncitem = 7

contFunc

StreamFuncitem = 7

FilterFunc!divides = 3

contFunc

StreamFuncitem = 7

suspensionStreamTail

StreamValueitem = 3

StreamFuncitem = 2

StreamFuncitem = 5

StreamFuncitem = 7

contFunc

item = 7StreamFunc

contFunc

FilterFunc!divides = 5

Figure 8.14: Expanding the sieve

Page 160: A Functional Pattern System for Object-Oriented Design

142 8 Lazy Object

tailtail tail

streamSieveFunc

stream

stream

streamFilterFunc!divides = 2

NatFuncarg = 11

contFunc

suspensionStreamTail

StreamValueitem = 3

StreamFuncitem = 2

StreamValueitem = 5

StreamFuncitem = 7

contFunc

item = 7StreamFunc

contFunc

FilterFunc!divides = 5

StreamFuncitem = 11

contFunc

StreamFuncitem = 11

FilterFunc!divides = 3

contFunc

StreamFuncitem = 11

Figure 8.15: Normalized sieve

Page 161: A Functional Pattern System for Object-Oriented Design

8.11 Known Uses 143

design of stream functionality that is available and ready to be used. Only the twoclasses in the lower left corner of the diagram with the “Concrete ” prefix need tobe supplied in order to introduce a new stream function. A new ConcretePipeFunchas to provide implementations for the value, step, and (only if deviating from thestandard behavior) eos methods. The new ConcretePipe simply initializes classPipe ’s pipeFunc attribute to the new ConcretePipeFunc .

Often, it is not even necessary to introduce a new stream function but a standardfunction can be applied to a whole stream by using the stream function Map. Itsimplementation is as simple as this:

class MapFunc[G, H]inherit PipeFunc[G, H]creation makefeature

mapFunc : Function[G, H];

make (f : like mapFunc) isdo

mapFunc:=f;end;

value : H isdo

Result:=mapFunc @ (stream.item);end;

step : like Current isdo

Result:=Current @ (stream.tail);end;

end

8.11 Known Uses

The UNIX operating system owes much of its flexibility to stream basedcommands that can be interconnected via pipes [Ritchie84]. The com-mon format of line separated ASCII characters allows the free compo-sition of services. For instance, to uncompress a bitmap, convert it toPOSTSCRIPT10 , and to finally view it, one can build a pipe of four commands:cat bitmap.pbm.gz | unzip | pbmtolps | ghostview - .

SATHER [Omohundro94] supports a restricted form of coroutine called an“iter” [Murer et al.93b]. Iters yield elements to be used in iterations. Their pri-

10PostScript is a registered trademark of Adobe Systems Incorporated.

Page 162: A Functional Pattern System for Object-Oriented Design

144 8 Lazy Object

mary difference to streams is the bounded lifetime, which ends with the iterationloop.

ALGOL 60 [Backus et al.63, Landin65] features call-by-name parameter passing.This mechanism enables delayed evaluation, save the “evaluate only once” strategyof call-by-need.

Programming languages with static array sizes (e.g., PASCAL [Rohlfing78]) forceus to specify an array size that is often not know at the time of array creation.Programming languages that enable dynamic array sizes (e.g., EIFFEL) — in a way— allow for a lazy size specification for arrays. A force operation enlarges anarray whenever necessary, so the initial size given does not restrict future arrayuses.

Graph algorithms in general are a popular target for lazy evaluation. DavidKing and John Launchbury present algorithms with linear-time complexity that areconstructed from individual components, which communicate via lazily generatedintermediate structures [King & Launchbury93]. A lazy depth-first search tree of agraph will not cause the whole graph to be visited, when only part of the tree is re-quested. Even possible side-effects, such as marking nodes as visited, will only beperformed as necessary [Launchbury & Jones94, Launchbury & Jones95]. In func-tional programming it is common to express, e.g., tree traversal problems first byflattening the tree into a list, and then by processing the list. The intermediate listcan then provide a channel of communication between standard components. Lazyevaluation successfully avoids the transient creation of a large intermediate list andof large intermediate structures in general [Launchbury93].

Lazy graph algorithms also promise to be suited for multiprocessor architec-tures. The absence of side-effects and the memory friendly behavior of data bind-ings, as opposed to data structures, contributes to their suitability for parallel pro-cessing [Kashiwagi & Wise91].

The Convex Application Visualization System (AVS) is a visualization packagesthat organizes various modules, such as file readers and data processing, to a net-work of interconnected components [Convex93].

The Lazy Propagator pattern [Feiler & Tichy97] uses updates on demand in anetwork of distributed computations. A network node is updated only when re-quested for one of its values. A single request thus may cause a wavefront of back-ward updates until the system is just enough updated to answer the initial request.

Attribute grammars with lazy attributes can be specified without regard to thenature of dependencies (e.g., left-right, or even circular). Attributes will automati-cally be resolved in the order of their dependencies [Mogensen95].

For further examples the reader is referred to the literature given in section 8.2on page 115.

Page 163: A Functional Pattern System for Object-Oriented Design

8.12 Related Patterns 145

8.12 Related Patterns

8.12.1 Categorization

Iterator: The interface of Iterator from the Iterator pattern is very similar to thatof Stream . Both internal iterators and streams allow separating the iterationloop and collection navigation from the iteration body. Streams, however, donot provide a first method. As opposed to external iterators, streams are notstateful objects. To re-iterate a stream, one has to keep a copy of the stream’sstart.

Stream, Pipeline,Pipes and Filters: are architectural views on the stream concept which can

be provided by lazy evaluation [Ritchie84, Edwards95, Meunier95b,Buschmann et al.96, Posnak et al.96, Shaw96].

Singleton: A Singleton is a Lazy Object since it is created at most once. If created atall, every accessor will be given the same instance, which exactly correspondsto call-by-need semantics.

Co-Routine: A stream can be regarded as a co-routine. Resuming happens throughtail calls and one can think of a series of subsequent continuation functionsas co-routine incarnations distributed over stream element functions. The co-routining behavior of lazy objects is vital for their ability to avoid large in-termediate structures. Calculation suspension allow building a pipeline thatprocesses elements one by one rather then causing whole transient structuresto be constructed in between.

8.12.2 Collaboration

Function Object: Function Object can be used for lazy evaluation as well. A func-tion object represents a suspended calculation until requested to evaluate itsresult.

Streams are connected to function objects through class Map. It allows apply-ing standard function objects to streams. Ergo, it is easy to obtain a streamfunction, if a corresponding a single element function already exists.

Translator: The result of a translation obtained using Translator can be a stream.

Transfold: Transfold processes lazy streams, that function as a lingua franca betweencollections and iterators.

Serializer: Serializer could be used to flatten data in order to transfer it via a stan-dard stream channel. Instead of using Serializer on may alternatively simplyuse a specialized object stream.

Page 164: A Functional Pattern System for Object-Oriented Design

146 8 Lazy Object

Composite: A stream append operation (appending f and g yields h = f ◦ g) canbe regarded as a stream composite [Gamma et al.94, Manolescu97]. One mayalso use a Component to encapsulate the splitting of one stream into multiplebranches and their re-joining into one stream again. For instance, one mayfilter various information types from a multi-media stream, compress themindividually, and re-compose them to a compressed multi-media stream.

8.12.3 Implementation

Memoization: Being a technique rather than a pattern, memoization neverthelessis used by Lazy Object in order to perform calculations only once. Furtherrequests are served by accessing a cache [Keller & Sleep86].

Value Object: A lazy object may use Value Object (see chapter 9 on page 149) toproduce immutable cached values of calculated results, e.g., stream elements.

Command,Function Object: StreamTail can be regarded as a function object with one argument

(suspension) and three entry points with no further arguments. Its task is toseparate the creation of an operation and its actual invocation in time, verysimilar to the Command pattern.

Factory Method: If clients defer the creation of stream functions to continuationfunctions — that know whether to choose a StreamFunc or StreamFuncOpt— the corresponding method is a Factory Method [Gamma et al.94].

Void Value: The behavior of a stream’s end may be implemented with Void Value(see chapter 11 on page 191).

Page 165: A Functional Pattern System for Object-Oriented Design

8.12R

elatedPatterns

147

Pipe

Sieve

Divides

SieveFunc

Filter1

FilterFunc

NotF

Client

step : Currentvalue : G

ContFunc

eos : Boolean

eos : Boolean

PipeFunc

Stream

StreamSuspension

forceTail : StreamforceItem : G

tail : Streamitem : G

last : Booleantail : Streamitem : G

StreamTailStreamFunc

StreamVal

Function

Filter

NatFunc

Figure 8.16: Sample structure

Page 166: A Functional Pattern System for Object-Oriented Design

148 8 Lazy Object

Pipe

Client

StreamVal

ConcretePipeFunc

PipeFunc

eos : BooleanConcretePipe

Function eos : Boolean

value : Gstep : Current

ContFunc

Stream

StreamSuspension

forceTail : StreamforceItem : G

tail : Streamitem : G

last : Booleantail : Streamitem : G

StreamTailStreamFunc

Figure 8.17: Stream structure diagram

Page 167: A Functional Pattern System for Object-Oriented Design

149

9 Value Object

Even in change, it stays the same.– Heraklit

9.1 Intent

Use immutable objects with generator operations for protection against side-effectsand aliasing.

9.2 Also Known As

Immutable Object [Siegel95].

9.3 Motivation

Mutable objects are the bedrock of object-oriented design and programming. Theability to dynamically instantiate objects, change their value, retrieve their state,and share them among multiple users for communication allows the developmentof efficient software with a close relationship to real word models (see chapter 2 onpage 29).

Yet, another bedrock of object-orientation is encapsulation (see section 2.2.2 onpage 32). Encapsulated objects provide us with the peace of mind that every changeto an object is controlled by the object itself. Obviously, an inconsistent or unex-pected change of object state can be caused by the object itself only. Unfortunately,this is not quite true.

9.3.1 Problem

Surprisingly many object-oriented languages do not provide complex numbers ei-ther as primitive types or as a library component. Luckily, we can extend the exist-ing library and provide a complex number class as if it has been part of the stan-dard1. The straightforward approach is to write a class with a set of operations,

1As always the devil lurks in the details. See Matthew Austern’s account on the difficulties ofintroducing a Complex class with EIFFEL.

Page 168: A Functional Pattern System for Object-Oriented Design

150 9 Value Object

e.g., add, mult, etc., that modify their receiver. There are countless examples of thisapproach in object-oriented libraries, especially for container classes. The changeto an internal state is done by the most competent instance — the object itself —and it is the most efficient way to implement updates.

A partial implementation, therefore, could be:

class Complexcreation makefeature

re : Real;im : Real;

make(r : Real; i : Real) isdo

re:=r;im:=i;

end

plus(other : Const_Complex) isdo

re:=re + other.re;im:=im + other.im;

endend

Where is the problem? Let us have a look of a typical code fragment using thenew class:

...local

a : Complex;b : Complex;

do!!a.make(1, 2);

b:=a;a.plus(b);

io.putstring(b.out);end;

Two variables of the new type Complex are declared and one is created with thevalue 1+2i. It is only natural to move complex numbers between variables whichhappens in b:=a . Then a is changed by adding the value of b, i.e., it is effectivelydoubled. As the following print statement reveals, the value of b is not 1+ 2i any-

Page 169: A Functional Pattern System for Object-Oriented Design

9.3 Motivation 151

more, but reflects the change that happened to a! Alas, what happened?2 One theone hand nothing unusual took place: The value of a complex number variablewas changed. Since another reference (b) to the value of a was created before, anychange can be seen trough b as well.

On the other hand, our intuition about dealing with numbers has been severelyhurt. The keyword to resolve this disturbing confusion is reference. We imple-mented complex numbers with reference semantics, i.e.,

1. variables are pointers to complex number objects,

2. operations change object state, and

3. assignment copies references only.

mutator_procedures

accessor_functionsmake

NonMutatingAccessor ImmutableInterface

MutableInterface

Figure 9.1: Providing a “const” interface

As a result, aliasingwith unwanted effectsis the standard behav-ior.

One way to con-trol such introducedside-effects is to disal-low the application ofoperations that mutateobject state. Hence, itappears useful to in-troduce an immutableinterface to complexnumbers [Wilson95].

For instance, with

class Complexinherit Const_Complexcreation makefeature

plus(other : Const_Complex) isdo

re:=re+other.re;im:=im+other.im;

end;end

any caller of plus can be sure that the passed argument will still be unchanged afterthe execution of plus. This scenario assumes a hierarchy as depicted in figure 9.1.An attempt to assign other to a variable of type Complex , i.e., an mutable interface,results in a type error.

2Anyone not feeling irritated about this behavior is probably spoiled by commonplace referencesemantics in object-oriented languages.

Page 170: A Functional Pattern System for Object-Oriented Design

152 9 Value Object

An immutable interface (i.e., the Constant Object pattern) is a powerful meansto control side-effects and in

...local

a : Complex;b : Const_Complex;

do!!a.make(1, 2);

b:=a;a.plus(b);

io.putstring(b.out);end;

one can be reasonably3 sure that the value of b cannot be altered by passing it toplus. As already stated it is also not possible to modify the value of b by obtaininga mutable interface to it, e.g., by a:=b .

Unfortunately, the above code (using b:=a ) will still compile and produce theunwanted effect. One might be tempted to reverse the hierarchy of figure 9.1 onthe page before and make immutable interfaces inherit from mutable interfaces.Apparently, statement b:=a; would be illegal then. But,

• reversing the hierarchy of figure 9.1 on the preceding page is not sound interms of subtyping. Class ImmutableInterface would have to hide operationsfrom MutableInterface destroying substitutability. Therefore,

• it is no longer possible to pass mutable objects to servers with read-only in-terfaces. The const modifier property of immutable interfaces is lost.

• of course, it is still possible to create aliasing, e.g., with a:=b; .

In conclusion, immutable interfaces do not provide a suitable solution to refer-ence semantics induced problems with complex numbers. After all, it should notbe necessary to declare variables to provide read-only access in order to avoid theabove aliasing effects. Moreover, we want to be able to modify complex numbers.There is no point in installing a safety mechanism that inhibits modification of val-ues to such an extent.

9.3.2 Solution

Complex numbers are values, i.e., they are timeless abstractions and therefore un-changeable [MacLennan82]. They deserve to be implemented with value seman-tics. Instead of modifying the receiver of operation add, the receiver should be left

3Of course, a server may use a reverse assignment attempt to gain a mutable interface to anobject passed through an immutable interface. Anyone doing so, is hopefully aware of the surprisespossibly introduced.

Page 171: A Functional Pattern System for Object-Oriented Design

9.3 Motivation 153

untouched and a new instance of a complex number should be returned. In anal-ogy, there are no operations that change integers. An expression like 41.inc doesnot make sense, since 41 is a unique abstraction, rather than an object with iden-tity [Khoshafian & Copeland86]. While it could be used to arrive at the abstraction42 by adding 1 it is not possible to change it.

A version of Complex with value semantics is:

class Complexcreation makefeature

...infix "+",plus(other : Complex) : Complex isdo

!!Result.make(re + other.re, im + other.im);end;

end

Note that now it is now possible to use the more natural infix "+" notation foraddition of complex numbers. It was added as a synonym for plus which we justretained for better comparison with the previous version of plus. The client codeneeds a bit of adaption to reflect the change —

...local

a : Complex;b : Complex;

do!!a.make(1, 2);

b:=a;a:=a + b;

io.putstring(b.out);end;

— and will, finally, work as expected. When a:=a + b; is executed, a is reat-tached to a new instance of a complex number. Variable b continues to point to theold value of a. A change to a finally no longer affects the value of b.

It is worthwhile noting that immutable interfaces remain useful for classes withreference semantics but are absolutely superfluous for classes with value seman-tics (e.g., method plus above does not need a Const Complex argument interfaceanymore). There is no way a server could change a complex number anyway. Anymodification results in a new complex number, leaving any passed instance intact.In other words, values do not require immutable interfaces.

Page 172: A Functional Pattern System for Object-Oriented Design

154 9 Value Object

9.4 Applicability

• Modeling. When clients are interested in values only and never care aboutidentity, use Value Object for the object in question. Types representing val-ues, i.e., abstractions with no identity for which creation, change, and shar-ing have no meaning, should be implemented with value semantics. Besidesnumbers, strings are a typical example. Sharing of string contents easily oc-curs with a straightforward implementation but is certainly unexpected4.

• Aliasing protection. When a type should be immune to aliasing effects,replace its mutator operations with generator operations. For in-stance, a matrix multiplication using destructive updating of the receiverworks fine until someone calls: aMatrix.mult(aMatrix); . Thismay destroy parts of the matrix that are still needed for input. WithaMatrix:=aMatrix.mult(aMatrix); there is no such danger sincetarget and source matrix are different.

• Small objects. Use Value Object preferably for small data structures. The largerthe structure, the greater the overhead through duplication of unchangeddata when creating a new instance in response to an operation. Using ValueObject for larger structures can still be reasonable to avoid aliasing and doesnot necessarily have to be inefficient (see 9.9 on page 158, Copy on demand),but is most appropriate for objects with a small memory footprint.

• Efficient value passing. Creating new instances when changes occur is appro-priate when the frequency of passing and assigning values is higher thantheir update frequency. If updating values is the predominant operationthen consider to implement a copy-on-passing scheme (see 9.9 on page 158,When to copy values) or use a language’s built-in support for value types (e.g.,expanded types in EIFFEL or non-pointer variables in C++).

9.5 Structure

Client

make

generatorsaccessors

ValueObject

Figure 9.2: Structure diagram

4One of the few things the JAVA library got right: String objects are immutable and sharing canbe achieved with StringBuffer only.

Page 173: A Functional Pattern System for Object-Oriented Design

9.6 Participants 155

9.6 Participants

• Client

– calls an accessor or generator operation of ValueObject .

• ValueObject

– returns a value on access.

– creates and returns a new object when asked for a modified version ofitself.

9.7 Collaborations

aValue*aClient

generator

aValue

Figure 9.3: Interaction diagram

• A client performs an operation on a value object.

• The value object creates a new instance containing the information accordingto the requested operation.

• The value object returns the freshly created instance.

9.8 Consequences

• Simplicity. Code using values can be much simpler than code using objects,since there is no need to be cautious about side-effects and aliasing. All ad-vantages of dealing with immutable values apply (e.g., validation of correct-ness, understandability, transformability (see section 1.3.1 on page 17)). Valueobjects do not offer mutator operations, but use generator operations to pro-vide new instances. Note that object encapsulation is still preserved. Anychanges to the object are still under the regime of the object itself. Plainly thetarget of the updated information is different to a conventional destructiveupdate.

Page 174: A Functional Pattern System for Object-Oriented Design

156 9 Value Object

• Encapsulation. While direct aliasing (as presented in the example of section 9.3on page 149) might be mastered with effort, indirect aliasing is a notoriouslyhard issue [Hogg91, Almeida97]. For instance, even expanded types in EIF-FEL use a shallow copy on assignment only. As a result, components areshared and are subject to change while the owning object is not in control.Value Object provides full state encapsulation even for groups of objects.

• Equality. Testing value objects for equality cannot be accomplished by usinga built-in equality operator such as “=”5. The standard operator will comparepointer values, which can be different for otherwise equal values (but seealso 9.9 on page 158, Sharing of values).

The solution is to use a (usually already setup) equality method, like in

if equal(aComplex1, aComplex2) then...

or using a custom infix operation:

if aComplex1 #= aComplex2 then...

Often, as in EIFFEL, the setup equality method will perform as desired: It willcompare all value components for equality. Nonetheless, it can be useful tooverride the equality method (is equal in EIFFEL) in the value class, in or-der to achieve equality checking of abstract state rather than concrete state.For instance, a custom equality method could appreciate the equality of twocomplex numbers, even if one of them is represented with cartesian coordi-nates while the other uses polar coordinates.

• Subtyping. Usually “is-a” or specialization inheritance is at odds with sub-typing. Although a set of integers “is-a” set of numbers it is not possible topass it to a method expecting a set of numbers. This restriction is sound sincethe method could legally insert any number, e.g., a real number, violating thecaller’s integer set constraint.

However, if the method’s parameter is declared to be a value set of numbersthen calling the method with a set of integer values is valid and safe. Thereis no way in which the called method could corrupt the passed set, since it isimmutable.

Other examples of “is-a” relationships that usually must be avoided but workin the context of value types are Ellipse being subclassed by Circle and Rect-angle subclassed by Square . In both cases some mutations, e.g., stretching inone direction only, would throw subclass objects into trouble6.

5Every JAVA programmer must discover that comparing strings with “=” does not work.6Unless they can dynamically change their type, as with SMALLTALK’s become: method.

Page 175: A Functional Pattern System for Object-Oriented Design

9.8 Consequences 157

Note that it is even save to pass a mutable square, i.e., one that offers destruc-tive updates, to a method with a square value object interface (see figure 9.4on the following page). Inside the method the mutator operations are not ac-cessible and therefore subtyping is provided. Anyway, a generator methodmight return a result whose type is different from that of the receiver. Forinstance, stretching a Square yields a Rectangle .

Sadly, while subtyping with values actually works with the latter examples,there is no way to pursue EIFFEL’s type system to believe that an integer in-stantiation of a generic class can be passed to a number instantiation. Thetype system rightfully assumes invalidness in the general case and cannot behinted to the value semantics constraints that make it safe in this particularcase (see section 14.5 on page 243).

• Heterogeneity. Reference types and value types cannot be used uniformly byclients. Besides the need for a different equality check7, clients must eitheruse

aComplex.add(aNumber);...aComplex:=copy(aNumber);aComplex.negate; -- keep aNumber intact

or

aComplex:=aComplex + aNumber;...aComplex:=aNumber.negate; -- keep aNumber intact

This may appear unfortunate at first sight but it should be pointed out thatvalue semantics — in this case for numbers — is not motivated historicallybut deeply roots in the fundamental difference between abstract values andchangeable objects [Eckert & Kempe94]. Modeling of types and the corre-sponding syntax should reflect this difference.

• Efficiency.

+ As value objects are immutable they can be shared safely, i.e., imple-mented with pointer semantics. Parameter passing and assignment are,therefore, cheap operations. Safe sharing of objects may also vastly sim-plify concurrent and parallel designs [Siegel95].

+ Value objects can share subcomponents as long as these do not change.This reduces the amount of memory needed and speeds up copying ofunchanged data to new instance created by generator operations.

− Creating a new object instead of modifying an existing one, increasesmemory demand and adds strain to the garbage collector.

7C++ with an overloadable “=” operator being an exception.

Page 176: A Functional Pattern System for Object-Oriented Design

158 9 Value Object

− Generator operations on large value object structure are expensive. Val-ues with large components must recreate these even if only a tiny partof them changes. Changing a small part of a large value object is, thus,highly inefficient.A mixed scheme — mixing generator and mutator operations — is pos-sible (see figure 9.4), allowing mutator operations whenever safe. Unin-tentionally passing a value object to a method with a mutable argumentinterface, will be caught as an error by the type system.If, on the other hand, the method uses an immutable argument(ValueObject ) interface, callers are protected against changes to the ar-gument, whereas code inside the method will be checked against unin-tended mutation attempts.

Client

make

generatorsaccessors

ValueObject

MutableObject

mutators

Figure 9.4: Optional mutators for Value Object

9.9 Implementation

• When to copy values. Value Object creates new values when values are gener-ated. Another choice would have been to create copies whenever the possi-bility of aliasing is created, i.e., whenever a new access thread is created byparameter passing or assignment.

In figure 9.5 on the next page an arrow pointing downwards denotes the cre-ation of a new value by a generator operation. Indexes and shading indi-cate the generation of a value, i.e., the count of copy operations in its history.Note that the situation in figure 9.5 on the facing page is in favor of copy-on-passing rather than copy-on-change, but with less updates and more passingor assignments the score is reversed.

Most programming languages implement their basic types with copy-on-passing. And in most languages we have no choice but implement Value Ob-

Page 177: A Functional Pattern System for Object-Oriented Design

9.9 Implementation 159

ject with copy-on-change, because implementing copy-on-passing requires tomodify the semantics of assignment and parameter passing. However, copy-on-change seems to be the better alternative in most cases, since assignmentand passing should be more frequent than updates and it is more intuitive toexpect a cost when operations are performed.

copy on passingcopy on change copy on demand

1v 1v

v2

v2

v2v2

v2

v4

v3

v3

v1

v

v

1

1

v

v1

v

1 1v v

1

1

Figure 9.5: Copy strategies

• Value Object versus language support. Some languages offer support for the ad-dition of types with non-reference semantics. Typically, however, anothermotivation was the driving factor, for instance, the ability to specify ob-ject containment rather than object referencing [Madsen et al.93, Meyer92].Consequently, value semantics is often, e.g., in EIFFEL, not sufficiently sup-ported [Kent & Howse96]: Expanded types, for instance,

• do not allow polymorphism.

• cannot be specified with deferred classes.

• make shallow copies on assignment only.

• Abstract state. Value objects must preserve equality only modulo their respec-tive equality operation. For instance, fraction numbers could decide to nor-malize their representation after addition but not after multiplication. Hence,

156

+156

=153

while156∗2 =

306,

but comparing the two results would yield that they are equal.

Page 178: A Functional Pattern System for Object-Oriented Design

160 9 Value Object

• Deep copy. If a value object has subcomponents consisting of standard refer-ence type objects, it has to create a deep copy of them in case it producesan altered copy of itself. Otherwise unintended sharing of subcomponents isinevitable.

• Copy on demand. A (deep) copy of a value subcomponent is not necessary ifthat component does not change due to the requested operation. At least forcomponents a value object can implement the copy-on-demand strategy offigure 9.5 on the page before. Only if a subcomponent changes, a copy has tobe created before any changes are made.

• Sharing of values. Ordinarily, values are generated without regard whether anequal instance of the result already exists. If every result is first looked up ina repository of already existing values, then

• memory can be saved by avoiding multiple value instances with thesame value.

• equality testing can be achieved by pointer comparison again. This ismuch more efficient and allows to use standard syntax for comparison.

• a repository has to be kept8 and looking up equal values consumes time.

If values are more frequently compared than transformed than it pays offto use the above — so-called Singular Object [Gamma95] or Identity Ob-ject [Smith95] — strategy.

9.10 Known Uses

It is needless to emphasize that functional programming languages treat their com-putational “objects” as values. Many object-oriented languages split their typesinto basic types (with value semantics) and reference types. Even in SMALLTALK,where everything is an object, numbers are correctly treated differently, i.e., they donot provide mutator but generator operations.

Ian Poole et al. use a combination of Lazy Object and Value Object, i.e., im-mutable lazy values to represent complex computation networks [Poole et al.98].The benefits of their Lazy/RT9 pattern (e.g., modular reasoning, parallel evaluationmultiple level undo, exception recovery, etc.) is a combination of the consequencesof both Lazy Object (see section 8 on page 115) and Value Object.

8In SMALLTALK such a collection can readily be obtained by sending allInstances to an ob-ject.

9Lazy and referentially transparent objects.

Page 179: A Functional Pattern System for Object-Oriented Design

9.11 Related Patterns 161

9.11 Related Patterns

9.11.1 Categorization

Constant Object: A value object interface to a mutable object fulfills the same pur-pose as an immutable interface (see section 9.3 on page 149). In addition toaccessor functions, a value object interface, furthermore, provides generatoroperations, which can be used to create new values without altering the orig-inal.

Identity Object,Singular Object: This pattern emerges when Value Object or Constant Object is

implemented with unique value instances as described in section 9.9 onpage 158. For instance, the datatype Symbol in SMALLTALK is implementedas an Identity Object.

Unshareable Object: Another way to prevent aliasing it to allow single references toan object only [Minsky95, Minsky96]. While values can be motivated froma modeling point of view, it is unclear what the real world equivalent of anunshareable reference is. Unshareable references are not copied but moved,i.e., the source of an assignment statement will be nil afterwards. A usefulanalogy might be a physical object that can only be moved but not shared ora unique item that is allowed to occur only once, e.g., a token in a network.

9.11.2 Collaboration

Function Object: If function objects accept arguments through a value object inter-face, they do not need to copy arguments in order to be safe from futurechanges. This, however, does not hold if there is also a mutating interfaceavailable (see figure 9.4 on page 158) through which arguments could be cor-rupted.

Strategy,Function Object: The equality operation can be parameterized with a function ob-

ject predicate. Depending on the application, several “views” on equalitycould be implemented (e.g., books which differ in publishers only could beconsidered equal for a certain purpose).

Void Value: Default values (e.g., 0+0i for complex numbers) can be provided withVoid Value.

Transfold: Due to the absence of aliasing, it is less error prone to generate new val-ues from a collection of values than dealing with shared objects. A valueaggregate can be iterated without concerns for changes occurring during iter-ation.

Page 180: A Functional Pattern System for Object-Oriented Design

162 9 Value Object

Lazy Object: Pattern Lazy Object (see chapter 8 on page 115) may be used to lazilyinitialize value object components.

Decorator: A decorator could be used to provide a value semantics interface to stan-dard reference types. A decorator must first copy the receiver to a new in-stance, then update that instance according to the operation, and finally re-turn the new instance.

9.11.3 Implementation

Lazy Object: A value object may return a lazy result. Especially, if the calculation ofa new value is costly and frequently only parts of the result are actually usedthen it is worthwhile to defer the calculation until the result is accessed.

Transfold: A value object may implement equality by using Transfold to comparereference type collections in subcomponents.

Translator: Extremely complex values may implement sophisticated operations —such as providing a certain view or an interpretation of a value — by translat-ing their subcomponents.

Page 181: A Functional Pattern System for Object-Oriented Design

163

10 Transfold

I can’t understand why a person will take a year or two to write a novelwhen he can easily buy one for a few dollars.

– Fred Allen

10.1 Intent

Process the elements of one or more aggregate objects without exposing their rep-resentation and without writing explicit loops.

10.2 Motivation

Collections play a very important role in software design. They allow us regardinga bunch of objects as a single abstraction. Clients pass and store collections as sin-gle entities and defer the responsibility of reacting to a message to the collections.A single request to a collection may mean to forward the request to all collectionmembers (e.g., most Composite services in the Composite pattern), find a memberbest suited to deal with the request (e.g., Chain of Responsibility pattern), extractone member with particular properties (e.g., a detect method), etc. Hence, withthe exception of a few Dispenser types like Stack and Queue , that restrict access toone end, it is common to access collection members one by one until a conditionis met or full coverage has been achieved. In other words, a frequent operation oncollections is iteration.

10.2.1 Problem

It is clearly not an option to let clients iterate over collections — or more generally,aggregates — using knowledge about an aggregate’s internals. In

...list : List[Integer]l : Link[Integer];

dofrom l:=list.first;until l=void

Page 182: A Functional Pattern System for Object-Oriented Design

164 10 Transfold

loopio.putint(l.item);l:=l.next;

end;...

the client is committed to a linked list implementation of List . If the implementationof List changes to a balanced tree representation, e.g., to make searching a fasteroperation, each client iteration as above is subject to change. Even C++ code ascommonplace and innocent looking as

...for (i=0; i<count; i++)

cout << a[i];...

is almost certainly more specific than necessary. Instead of relying on a to be anarray or indexable, the above code should assume no more than the properties ofan ordered collection in order to protect itself from future changes.

Evidently, an iterator abstraction is necessary that allows accessing the elementsof a collection or the components of aggregates independently of their respective in-ternal representation. With the decision for a dedicated iteration abstraction, how-ever, the following issues must be resolved:

• How to combine iterator and action? How do we combine an iteration algo-rithm (e.g., a simple loop) with a particular function or action (e.g., print anelement)? The iterator client may do it by calling both, the iterator could besubclassed for each function, or the iterator “takes-a” function.

• Who knows how to iterate an aggregate? Where is the best place to put the itera-tion logic? Shall the aggregate explore itself or is it better to externalize suchfunctionality?

• Who controls the iteration? Who is in control of advancing the iteration andwho may decide to stop prematurely, i.e., avoid a full exploration?

• How to support several iteration strategies? Non-linear aggregates (e.g., trees orgraphs) support different traversal strategies like breadth-first-search anddepth-first-search with variations such as pre-order, in-order, and post-ordertraversal. How do we allow a choice between alternatives without bloatingthe aggregate’s interface?

• How to allow several iterations in parallel? In which way can we support mul-tiple iterators using a shared aggregate? Two or more clients may want toprocess the same aggregate with interleaving execution. It may even be thecase that a read-iterator follows the results of a write iterator.

Page 183: A Functional Pattern System for Object-Oriented Design

10.2 Motivation 165

• How to deal with alterations during iteration? What means are available tomake iteration over aggregates which are changed during iteration a safeand unambiguous operation? The further discussion will not deepen thequestion of how to make iterators robust [Kofler93], since it is orthogonal tothe issues of higher interest here.

Some proposed solutions can be easily dismissed:

• Combining iterator and iteration function with inheritance [Madsen et al.93,Meyer94b, Martin94] does not scale with respect to the number of traver-sal alternatives, iteration functions and implies other problematic is-sues [Kuhne95b] (see also section 7.3 on page 93).

• Equipping aggregates with a cursor that allows iterating the whole aggregate(e.g., lists [Meyer88, Omohundro & Lim92]) is problematic in the presence ofmultiple iterations. Even if interference is prevented by providing a cursorstack, which clients use to push and pop cursors, the resulting scheme is in-elegant and error prone [Kofler93]. This suggests that the state of iterationshould be kept outside the iterated aggregate.

• Schemes relying on language support such as co-routines or specializationsthereof [Liskov & Guttag86, Murer et al.93b], are not easily applicable in lan-guages without these mechanisms.

We are left with two fundamentally different approaches:

1. External iterators place the iteration logic outside of aggregates and provideclients with an interface to start, advance, and inquire the actual element ofan iteration.

2. Internal iterators are typically a part of the aggregate’s interface. When givenan iteration function they autonomously perform the traversal, thus, releasingthe client to provide a control structure.

External and internal iterators are also referred to as active and passive [Booch94],with respect to the client’s role. Internal iteration corresponds to functional map-ping, i.e., the parameterization of higher-order iterators with iteration functions.

There are a number of arguments in favor of internal iteration:

“The code for initializing, updating, and testing iteration variables is oftencomplex and error prone. Errors having to do with initialization or termina-tion of iteration are sometimes called “fencepost1” errors and they are verycommon[Murer et al.93b].” – Sather Group

This suggests to “Write a Loop Once” [Martin94], i.e., code the traversal controlonce inside the aggregate and let all clients rely on it. Hence, the duplication ofvirtually identical code in clients for stepping through a structure is avoided. Aparticular expensive incident was caused when the Mariner space-probe was lostdue to an error in a loop [Neumann86].

1While genuine fencepost errors are avoided with external iteration as well, it is neverthelesseasy, e.g., to forget to advance the iteration or doing it at the wrong place.

Page 184: A Functional Pattern System for Object-Oriented Design

166 10 Transfold

DO 3 I = 1.3Code to be executed with I=1, 2, 3.

and if it had been C++, maybe . . .

for (i=1; i<3, i++);Code2to be executed with I=1, 2, 3.

Figure 10.1: Code example “Goodbye Mariner”

In the FORTRAN code of figure 10.1 the dot should have been a comma. As ithappened, just the value 1.3 was assigned to I without causing any iteration at all.An internal iteration might not have been applicable, but at least the above exampledemonstrates that (fatal!) errors can be introduced in even the most simple loops.

Traversal strategies often rely on internal aggregate details for stepping throughaggregates [Murer et al.93b] and it is easier to use a recursive method for descend-ing a structure than to memorize an access path externally [Gamma et al.94].

All the above observations argue in favor of making iteration an autonomousoperation of the aggregate, but

“External iterators are more flexible than internal iterators. It’s easy tocompare two collections for equality with an external iterator, for example, butit’s practically impossible with internal iterators. Internal iterators are espe-cially weak in a language like C++ that does not provide anonymous functions,closures, or continuations like SMALLTALK and CLOS. But on the other hand,internal iterators are easier to use, because they define the iteration logic foryou. [Gamma et al.94].” – GOF Group

A number of other authors agree that either built-in closure support is neededto use internal iterators [Baker93, Kofler93, Gamma et al.94] or that internal iter-ators are inflexible to the extent of disallowing the comparison of two data struc-tures [Murer et al.93b, Kofler93, Gamma et al.94, Budd95, Norvig96].

Also, it seems that clients know best when an iteration can be stopped, e.g.,when an element has been found, and internal iterators do not account for this3.

The Iterator pattern [Gamma et al.94] implements external iteration and re-solves a number of issues. The combination of iterator and iterator function is triv-ial, since the client calls both in a dedicated loop. Pattern Iterator gives the clientfull control of iteration advancement and termination. It, furthermore, allows mul-tiple traversal strategies and multiple iterators on the same aggregate. It is weak onrequiring clients to duplicate control structures, time and again. It also may forceaggregates to provide a (possibly protected) interface to allow their efficient scruti-nization for traversal. Finally, an external iterator has to keep track of the iteration

2Can you spot all three C++ errors?3This is not true for SMALLTALK where blocks returning a value cause control to be passed back

to the iteration client.

Page 185: A Functional Pattern System for Object-Oriented Design

10.2 Motivation 167

state — which may involve record keeping of paths into tree-like structures — in-stead of allowing a self managed exploration which, in case of a recursive method,uses the method calling stack for storing the access path.

Diametrically, an internal iterator is strong on concentrating the control to oneloop, information hiding of aggregate internals, and competence of aggregate ex-ploration. Unfortunately, it requires closures for combining iterator and iterationfunction, takes termination control out of the client’s hands, and allows only oneiteration at a time. Furthermore, multiple traversal strategies cause the aggregatesinterface to be bloated with iteration methods. Table 10.1 summarizes the compari-son of external with internal iteration again. The last point in table 10.1 refers to the

Iterator kind

external internal

combination of iteration and action is trivial ✓ (✓)∗

record keeping of iteration state is straightforward ✓

no special access interface to aggregate is required ✓

no explicit loop needed for client iteration ✓

client may stop iteration early ✓ ✓†

iterating multiple aggregates in lock-step is easy ✓

traversal alternatives do not bloat aggregate’s interface ✓

multiple iterations sharing one aggregate ✓

no parallel hierarchy of iterators and data structures ✓

∗Support for closures required.†By inelegantly passing a continue? -flag from function to iterator.

Table 10.1: External versus internal iteration

fact that external iterators typically depend on the properties of the data structuresthey traverse (e.g., trees call for different iterators than linear structures). There-fore, it is common to observe a class hierarchy of iterators paralleling that of thedata structures [Gamma et al.94, Meyer94b].

While internal iterators seem to be more faithful to software engineering mat-ters they apparently let clients down in terms of straightforward (active) use andflexibility.

10.2.2 Solution

An elegant way to resolve the forces is to

• provide a way to flatten aggregates to a stream of data and

• iterate passively over (multiple) streams.

Page 186: A Functional Pattern System for Object-Oriented Design

168 10 Transfold

DataAggregate

Main TraversalStrategy

TraversalCommitment

Streamed Data

Iteration Continuation

Transfold

Figure 10.2: Iteration topology with lazy streams and Transfold

For instance, a tree is first tranformed into a linear stream and then this streamis consumed by an internal iterator. An element of the intermediate stream mightcontain a lazy exploration of further parts of the aggregate, i.e., an iteration contin-uation (see figure 10.2). The stream consumer, hence, can decide which parts are tobe explored next. Therefore,

• the client does not need to provide a control structure.

• the aggregate keeps the record of its exploration by being responsible forstream generation.

• a client may stop iteration early, since it is in control of stream consumption.As streams are generated lazily, no unnecessary exploration will take place(Figure 10.2 shows some eagerly produced data elements for illustration pur-poses only).

• traversal (consumption) alternatives are defined outside the aggregate and donot bloat it’s interface.

• it is possible to share an aggregate for multiple iterations by the independentconsumption of a shared stream.

• no parallel class hierarchies evolve since stream consumption is invariant andstream generation is defined in data structures.

Two crucial points remain unresolved: How to circumvent the need for closuresupport and how to avoid the criticism of inflexibility towards internal4 iterators?

Of course, the first problem is immediately solved by referring to the FunctionObject pattern [Kuhne97] (see chapter 7 on page 93).

The second problem — the inability of internal iterators, e.g., to compare twoaggregates with simultaneous iterations — requires a small but very effective idea.Indeed, it is practically impossible to consider a second iteration while an internaliteration focuses on its sole aggregate iteration. One possible solution consists ofusing a function object, curried with the second stream, that compares the element

4Note that “internal” now only characterizes the passive iteration style rather than the locationof the iteration interface with regard to the aggregate.

Page 187: A Functional Pattern System for Object-Oriented Design

10.2 Motivation 169

passed from the iteration with the corresponding second stream element. Yet, theuse of a stateful function object that consumes one of its arguments on application,is at most an escape route, but cannot be considered to be a solid software engi-neering answer to the original problem.

=?

..., 7, 4, 3, 1

..., 7, 5, 3, 1

Figure 10.3: Iteration of two aggre-gates in lock-step

The problem, however, is easily re-solved by generalizing internal iterationfrom one to many aggregates (see fig-ure 10.3). An internal iterator, consumingtwo number streams, for instance, takes afunction with two parameters and appliesit to the two foremost numbers. Then, bothstreams are advanced simultaneously andthe next application will be to the two fol-lowing numbers (Chapter 13 on page 221demonstrates the application of Transfoldto a well-known problem of simultaneous iteration, the samefringe problem).

A functional programmer would probably first transform two list of numbersto one list containing 2-tuples of numbers, and then iterate over the resulting list.For instance, producing a list of sums of the respective elements of two argumentlists can be expressed as:

sumlists xs ys = map f (zip(xs,ys)5) (10.1)where f(x,y)6 = x+y.

Instead of defining f in sumit would be better to make it an argument of sumlists.Passing a different function then allows calculating the list of products, etc.

While two iteration arguments are sufficient for comparing and the above sum-mation example, in general, it would be desirable to iterate over an arbitrary num-ber of iteration arguments. The problem with tuples, however, is that it is not possi-ble, for instance, to pass sumlistsa function that will sum up the all the componentsof its argument tuple, regardless of the component number. In order to be able topass functions that work independently of the number of iteration arguments, theargument type of the iteration function has to be changed from tuple to list.

Of course, a generalization of zip is now required, i.e., a function that takes n in-put lists and transforms them to a list containing sublists of length n (see figure 10.4on the following page). The length of the result list is determined by the shortestlength of the n argument lists. Since the type of transposeand accordingly any othervariable iteration argument function, should not change with the number of itera-tion arguments, we pass the n iteration arguments as elements of one argument listto transpose. Given this function — whose definition shall be

trans[ ] = [ ]trans xss = if fold ((||)◦ ([ ] ==)) False xss then[ ]

else(map head xss) : trans(map tail xss) (10.2)6Tuple construction.6Tuple pattern matching.

Page 188: A Functional Pattern System for Object-Oriented Design

170 10 Transfold

1 3 5

4 6 8

transpose=⇒

1 4

3 6

5 8

Figure 10.4: Transposing lists

— it is now possible to generalize sumlists to a variable number of iteration argu-ments. Let us first re-implement sumlistswith transpose,

sumlists xs ys = (map f) (transpose[xs ys])where f zs= fold (+) 0 zs

and then — while making the iteration function f an argument — change its num-ber of input lists, from two to an arbitrary number in one input list:

sumlists7 f = (map f) ◦ transpose

It is now appropriate to call sumlists with a more general name. Let us usetransmap, because it maps a function to a transposed argument list. Passing a func-tion that computes the product of its argument list, i.e.,

transmap(fold (∗) 1) [[1 3 5] [4 6 8]] = [4 18 40]

creates a list of products (compare with figure 10.5) Now, it would be only a matter

1 3 5

4 6 8

transmap(fold (∗) 1)=⇒

4

18

40

Figure 10.5: Computing a list of products

of summing up the values in the result list (vector) to obtain the inner product ofthe two argument lists (vectors). To further motivate the extension of transmaptotransfoldlet us investigate how far we get, using transmapto perform the equalityoperation of figure 10.3 on the preceding page (see figure 10.6 on the next page).Note that a lazy transposition allows terminating the exploration of the (possiblyinfinite) argument lists when a non-equal argument pair has been found.

7No argument list is explicitely mentioned, because it would be just passed on to transpose, i.e.,we made an η-reduction.

Page 189: A Functional Pattern System for Object-Oriented Design

10.2 Motivation 171

1 3 4 . . .

1 3 5 . . .

transmap all equal=⇒

True

True

False...

Figure 10.6: Transmapping equality

Obviously, we need to reduce the result list with the logical And (&& ) operatorto obtain a single equality result. Likewise, the result list of figure 10.5 on the facingpage requires reduction with “+” to obtain the final inner product.

Therefore, transfoldis defined to be

transfold f a g = (fold f a) ◦ (transmap g)or

transfold f a g = (fold f a) ◦ (map g) ◦ transpose (10.3)with type transfold :: (b→ c→ c)→ c→ ([a]→ b)→ [[a]]→ c (10.4)

See table 10.2 for type and meaning of transfold’s parameters.

Para-meter type purpose

f b→ c→ c function that finally reduces the intermediate resultof element type b to a result of type c, using the ini-tial element.

a c the initial element for producing the final result,used as the induction base for an empty list.

g [a]→ b the function that is applied (mapped) to each row ofthe transposed argument, transforming a row [a] toan element of the intermediate result of type b.

[[a]]→ c resulting type of transfoldafter all arguments but thelast are supplied. Transforms a matrix (list of lists)with element type a into a result of type c.

Table 10.2: Transfold’s arguments

For instance, with all equal xs= fold ((&& )◦ ((head xs) ==)) True(tail xs)8:

transfold(+) 0 (fold (∗) 1) [[1 3 5] [4 6 8]] = 62, and (10.5)transfold(&& ) True all equal[[1 3 4 . . .] [1 3 5 . . .]] = False. (10.6)

8This function appears to be overly complicated but a moment of thought reveals that one cannotjust reduce with ==, since the latter has a boolean result type, unsuitable for further comparisons.

Page 190: A Functional Pattern System for Object-Oriented Design

172 10 Transfold

Note that we achieve reduction with fold which can be thought of being com-posed of a mapand a reducefunction, i.e.,

fold (⊕ ◦ f ) a xs ≡ reduce⊕ ((map f xs) ++ [a]) (10.7)

Here, reduceplaces its binary operator ⊕ between the elements of a list. This opera-tion is also referred to as fold1, whereas fold uses an additional element9 to accountfor lists with less then two elements. Anyway, fold is clearly more versatile than asimple reduce(see also section 10.3 on the facing page, Versatility).

To illustrate the flexibility of transfold, which is reflected in the intermediate typeb (see table 10.2 on the page before), let us calculate the sum of all row productsfrom a magic square (see figure 10.7). We use

transfold(+) 0.00(fold (∗) 1.0) (10.8)

where the input matrix contains integer elements, 1.0 denotes a real, and 0.00 de-notes a double. Hence we establish the mapping

[a 7→ integer, b 7→ real, c 7→ double].

8 1 6

3 5 7

4 9 2

≡8

1

6

3

5

7

4

9

2

transpose=⇒

8 3 4

1 5 9

6 7 2

map(fold times1.0)=⇒

8∗3∗4∗1.0

1∗5∗9∗1.0

6∗7∗2∗1.0

≡96.0

45.0

84.0

fold plus0.00=⇒ 96.0+45.0+84.0+0.00 ≡ 225.00

Figure 10.7: Transfolding the inner product of a matrix

Folding does not have to imply reduction, though. Using functions reverseandadd back, that establish the mapping [a 7→ integer, b 7→ [integer], c 7→ [[integer]] ], wemay transpose a matrix along its minor axis (see figure 10.8 on the next page).

Quite similar to the situation in definition 10.7, it is possible to get rid oftransfold’s parameter g by passing a different function to parameter f , i.e.,

transfold∗ ( f ◦ g) a ≡ transfold f a g.

9Typically the right-identity value for ⊕.

Page 191: A Functional Pattern System for Object-Oriented Design

10.3 Applicability 173

8 1 6

3 5 7

4 9 2

transpose=⇒

8 3 4

1 5 9

6 7 2

map reverse=⇒

4 3 8

9 5 1

2 7 6

fold add back[ ]=⇒

2 7 6

9 5 1

4 3 8

Figure 10.8: Minor axis matrix transposition

I felt, however, that it is clearer to separate the imaginative horizontal andsubsequent vertical processing functions explicitely. First, the combined functionf ◦ g can become hard to read, e.g., see definition 10.5 on page 171 repeated withtransfold∗:

transfold∗ ((+) ◦ (fold (∗) 1)) 0 [[1 3 5] [4 6 8]] = 62.

DataAggregate

DataAggregate

Transfold

Transfold

Figure 10.9:Transformationchain

Second, special cases can be pre-defined with default param-eters (e.g., the net effect of either function is often just the identityfunction; see section 10.7 on page 176, Separation). Furthermore,frequently occurring functions (e.g., reversal) can be reused andintermixed without performing function composition first.

10.3 Applicability

• Abstraction. Transfold allows accessing the elements of acollection or the components of an aggregate without ex-posing its internal representation.

• Concentration. Use Transfold if you want to release clientsfrom the duty of providing a control structure.

• Multiple traversals. When a structure is to be iterated bymultiple clients in alternation, Transfold allows sharing thestructure’s stream for independent consumption by multi-ple clients.

• Connectivity. If you want to cascade transformationsand/or want to convert collections into each other, possiblywith intermediate processing, use stream producing trans-folds and collection constructors with stream argumentsfor a flexible interconnection scheme (see figure 10.9).

Page 192: A Functional Pattern System for Object-Oriented Design

174 10 Transfold

• Separation. If, on the one hand, you want to assign the responsibility of explo-ration to the data structure itself, but on the other hand, do not want to bloatthe structure’s interface with iteration methods, use Transfold’s intermediatestream interface.

• Polymorphic iteration. The same transfold object can be used for a number ofalternative traversal strategies (e.g., diverse tree exploration orders). By usingan abstract Function interface for the passed stream processing functions, it ispossible to dynamically dispatch on the traversal alternatives.

• Versatility. Use Transfold to implement a wealth of operations, for instancefor lists: sum, product, length, average, max, min, map, filter, reduce, reverse, copy,append, exists, all, variance, horner10. According to [Waters79], 60% of the codein the Fortran Scientific Subroutine Package fits neatly into the maps, filters,and accumulations (i.e., transfold) paradigm.

Do not apply Transfold in case of tight memory and time constraints, where theoverhead of an intermediate stream and emulation of lazy evaluation is not tolera-ble (see also the counter indications to Lazy Object in section 8.4 on page 119): Man-agement of stream elements consumes time. Stream suspensions and lazy functionclosures represent a memory overhead. In most but a few cases, however, systemperformance should be absolutely no problem.

10.4 Structure

Aggregate

asStream : Stream

ConcreteFunction

Client

Transfold

Stream

Function

ConcreteStream

Figure 10.10: Structure diagram

10For a list representing a polynomial.

Page 193: A Functional Pattern System for Object-Oriented Design

10.5 Participants 175

10.5 Participants

• Client

– requests an Aggregate to provide a Stream of itself.

– passes two Function s and a value as parameters to Transfold .

• Aggregate

– provides a ConcreteStream , containing a flattened version of itself.

– uses a lazy Function to produce a ConcreteStream .

• Function

– provides an application interface for all functions including the streambuilding function, the Transfold parameters, and Transfold .

• Stream

– provides an interface to access any concrete streams.

– implements a lazy, infinite list semantics.

• Transfold

– takes two Function s and a value as processing parameters.

– transforms its input (a Stream of Stream s) to an arbitrary result type.

10.6 Collaborations

• A client requests an aggregate to flatten itself to a stream.

• The aggregate’s asStream method and a lazy function mutually call each otherto explore the aggregate lazily, while producing a stream.

• The client uses or creates two function objects, which it passes — along withan initial value — to a transfold object.

• The transfold object lazily accesses the aggregate stream, applying the passedfunctions accordingly.

Page 194: A Functional Pattern System for Object-Oriented Design

176 10 Transfold

asStream

@ (aFunction2, aValue, aFunction3, aStream)

access

@

@

anAggregate

@ (Current)

aFunction1aClient

Str

eam

Gen

erat

ion

aStream aFunction2 aFunction3 aTransfold

Str

eam

Con

sum

ptio

n

Figure 10.11: Interaction diagram

10.7 Consequences

• Inverted control. Clients do not repeatedly call iterator operations, but a trans-fold repeatedly calls functions supplied by the client. Therefore, a client doesnot have to provide a control structure to drive the iteration (see section 10.8on page 180 for a comparison of transfold implemented with external and in-ternal iteration). For the reason that there is only one iteration loop, used byall clients, loop-related errors are much easier to avoid and to discover. If theMariner-loop (see figure 10.1 on page 166) had been used by another client,tests of that client might have revealed the problem. More time can be spenton the validation of a single loop11 and any errors are removed for all clients.

• Traversal alternatives. Since iteration (stream consumption) is external tostructures it is easy to support a variety of traversal strategies and to dynam-ically dispatch on these. The stream consuming process (a transfold object)is in command of the exploration order, because the stream contains iterationcontinuations, which may be invoked in any order (see bullet Separation).

• Multiple traversals. Each transfold manages its own stream consumptionprogress and, therefore, enables multiple pending iterations on a shared struc-

11By referring to “loop” we also include the stream generation processes.

Page 195: A Functional Pattern System for Object-Oriented Design

10.7 Consequences 177

ture. All transfolds share the same exploration stream. Hence, any explo-ration effort by the structure is beneficial to all consumers. A once exploredsubpart, does not need to be traversed again due to the call-by-need semanticsof streams (see chapter 8 on page 115).

• Flexibility. The combination of function object passing, (variable) stream gen-eration, and possibly parallel consumption of multiple streams makes Trans-fold a truly flexible tool:

+ A particular operation can be performed by just passing function ob-jects, without requiring inheritance or client control structures. See sec-tion 10.3 on page 173, Versatility, for an impression of operations express-ible with folding.

+ Both stream consumption (traversal) and generation strategies (see sec-tion 10.8 on page 180, Stream creation) are easy to vary.

+ By supporting the parallel processing of multiple structures, the for-merly observed big inflexibility problem becomes a small equation: Ageneral operator to compare n structures for equality is

eq = transfold(&& ) True all equal.

Note that the definition of eq does not make any assumption about itsargument, except requiring elements to be comparable. All functionsobtained by purely combining other functions, expose this desirablegeneric property.

• Separation. The Transfold pattern successfully separates a number of con-cerns:

• Termination control. Both transfold and most importantly the client arein control of iteration advancement and termination. Through the use oflazy stream processing functions, the structure exploration is completelydemand driven (see section 10.9 on page 182). When a stream processingfunction does not evaluate its second argument — e.g., an And does notneed to examine the second argument, if the first is already False— thewhole transfold process stops. The same happens, if a stream returnedby transfold is not fully consumed. This scheme is far more elegant thanletting an iteration function return a continue? -flag, as designed in theinternal version of the Iterator-pattern [Gamma et al.94].

• Exploration & Consumption. The Transfold pattern is able to to combinethe best of both external and internal iteration worlds, by separating theexploration of a structure and the subsequent consumption of the explo-ration result.

+ Since iteration (consumption) is defined outside aggregates, their in-terfaces can be kept small. The only trace of an iteration is a asStream

Page 196: A Functional Pattern System for Object-Oriented Design

178 10 Transfold

method, which is of general interest anyway (see bullet Streamablecollections).

+ The responsibility to explore a structure is assigned to the most com-petent instance, the structure itself. The structure may use all itsinternal knowledge and recursive calls — thereby memorizing anexploration stack — to perform its exploration.It is possible to separate consumption and exploration without hav-ing the overhead of a full exploration, because the intermediatestream has lazy semantics, and is lazily produced. Note, that with-out such an intermediate structure any iteration type, like transfold,would have been defined for each structure separately.

+ Streams work as a lingua franca, between aggregates and iterators.As a result, iteration schemes, such as transfold, must be definedonly once, for all streamable structures.While different stream processing functions are required for differ-ing stream types (e.g., varying in the number of iteration contin-uations), there is no parallel hierarchy of iterators and data struc-tures. This is achieved by letting structures explore themselves andusing streams to uniformly communicate to iteration schemes. Spe-cial traversal orders may depend on stream organizations but not ondata structures, which is a useful indirection to decrease coupling.

• Functions. As mentioned in section 10.2.2 on page 167, a transfold objecttakes two function objects. This corresponds to a separation of horizontaland vertical processing of the “stream matrix” argument. Again, thanksto laziness, there is no drawback in execution overhead, compared to asingle function transfold, since no horizontal processing will take placewithout being demanded by vertical processing.The horizontal processing function was deliberately not constrained to afold, in order to allow the passing of already existing stream processingfunctions without a need to cast them into the form of a fold first. The firstfunction, however, is predefined to be a fold to avoid tediously passinga fold every time it is needed anyway, to make the user of transfold pass(and think of) a function operating on a stream rather than a stream ofa stream, and to increase the utility of transfolding, by making it morespecific.

• Reuse.

+ Instead of providing an iteration function in a self provided loop body(external iteration), the user of transfold is forced to write a function ob-ject, thus, making it available to other clients as well.

+ The developer of a data structure simply provides a way to stream thestructure and immediately gets a bunch of iterators (and especially theirinstantiations to operations (see section 10.3 on page 173, Versatility) forfree.

Page 197: A Functional Pattern System for Object-Oriented Design

10.7 Consequences 179

• Streamable Collections. The asStream method of data structures can also beused for many other purposes, such as a persistence or net-transfer protocolmechanisms.

Collections may be transfered into each other — streaming a Bag to a Set isan elegant way to remove duplicates — by means of an intermediate stream.No special mechanisms, e.g., the Serializer pattern [Riehle et al.97], will beneeded anymore.

That also implies that there is a uniform way to construct collections, e.g.,from constants. Any collection type, that allows manifest constants in thesyntax of a language (e.g., arrays), could be used to be transformed to thedesired collection type.

• High-level mind-set. A capable, high-level operation like transfold enables toapproach problems with a much more powerful decomposition strategy, com-pared to a procedural paradigm, restricted to e.g., array indexing.

Timothy Budd tells an anecdote of a FORTRAN programmer, designing athree-level nested loop to find a pattern repetition in a DNA sequence. Thecompeting version of an APL programmer ran much faster, although APLis interpreted, whereas FORTRAN is compiled. This was caused by a differ-ence in algorithm complexity, being O(M ∗N2)12 for the FORTRAN programand O(M ∗N log N) for the APL program [Budd95]. The difference can beexplained by differing programmer mind-sets. The FORTRAN programmer ispredetermined to think in terms of loops and array access. The APL program-mer used high-level operations like vector to matrix conversion, sorting, andmatrix reduction (all akin to and expressible with Transfold). As the anecdotesuggests, high-level operations allow approaching problems from a different,valuable perspective. Another supporting example is the task to swap twoareas A (ranging from 0 to i) and B (ranging from i + 1 to n) of different sizein a sequence without using any extra storage. A programmer who thinksof sequence reversal as a single operation is more likely to arrive at the veryelegant solution BA= reverse(reverse A) (reverse B). An implementation willbest use a procedural swap-elements reverse approach, but the initial sparkto the solution is most likely to be initiated by a high-level mind-set.

• Choice of style. In cases where no predefined iteration scheme, like transfold,seems appropriate, it is possible to consume a structure’s stream with an ex-ternal iterator, i.e., to write a control loop which consumes the stream.

• Robust Iteration. Transfold does not in particular contribute to the notion ofrobust iterators. Yet, a so-called iterator adjustment scheme [Kofler93], is par-ticular well implementable, since the structure controls its own explorationand, thus, may adjust an exploration process according to element removal

12M = pattern length; N = sequence length.

Page 198: A Functional Pattern System for Object-Oriented Design

180 10 Transfold

or insertion. Consequently, no registering of active iterators [Kofler93] is nec-essary.

In cases where updates should have no effect on the iteration process, it ispossible to simply iterate on a copy of the structure.

• Lazy Functions. A pre-requisite to achieve a demand driven semantics oftransfolding is to use lazy, i.e., non-strict, stream processing functions as ar-gument to transfold. For instance, a non-strict Andwith a first argument valueof False, will cause termination of an exploration process, since the latter is in-voked only if And evaluates its second argument (see the definition of fold insection 10.8). Unfortunately, stream processing functions have to treat theirlazy arguments differently. If, for instance, the argument is made lazy bypackaging it into a function object, it is necessary to apply the function ob-ject to a dummy argument (see section 8.8 on page 122, Information hiding).Hence, a standard Times function object can not be used, because it assumesits second argument to be a number, rather than a delayed number.

10.8 Implementation

The remarkable separation of iteration and demand driven structure exploration re-lies on lazy intermediate stream semantics. It is, therefore, vital to use lazy streams(see chapter 8 on page 115) and both lazy generation and consumption functions.

• Stream consumption. Whenever we referred to fold, we meant foldr [Bird86]and not its counterpart foldl. The foldr function —

foldr f a [ ] = a

foldr f a (x : xs) = f x (foldr f a xs) (10.9)

— has the nice property that the passed function f controls the recursive callof foldr, i.e., determines the extent of structure exploration. An implementa-tion aiming at true laziness must, consequently, wrap the recursive call (theexpression in parentheses) into a lazy object, e.g., a function object with adummy parameter. For the reason that fold may produce any result type froma stream, it is unfortunately not possible to design it as a stream pipe like map.This underlines the importance of language supported laziness, which wouldmake the difference between strict and non-strict values transparent to clients(see section 14.6 on page 246).

• Stream creation. Streams may be created eagerly but typically the structurewill return a stream containing iteration continuations. In other words, thestructure exploration method will create values but also stream elements, thatwhen accessed invoke further exploration of the structure. As explained inchapter 8 on page 115, this is transparent to the consumer who only sees a

Page 199: A Functional Pattern System for Object-Oriented Design

10.8 Implementation 181

sequence of values. Yet, it means that by skipping a stream element, a sub-part exploration will not be executed until and only if the skipped elementis accessed later. The structure exploration method and the exploration func-tion it uses for stream generation will, therefore, call themselves in a mutualrecursion scheme.

By steering the evaluation order of stream elements the client may implementa variety of traversal orders but if, for instance, a stream of a list starts with thefirst element and provides only forward continuations, then a backward iter-ation can not be achieved (see figure 10.2 on page 168, defining the locationsof main traversal strategy and subsequent order commitment). As a result,the structure should provided a parameterized asStreamWithFunction method.With the passed function any exploration order may be achieved. The stan-dard asStream method should satisfy the most frequent needs, though.

• Keyword parameters. Two important settings for transfold’s arguments arecons and the empty list for the vertical processing and the identity functionfor the horizontal processing respectively, since their net effect is the identityfunction (leaving transfold to simply transpose its argument). Using these asdefaults, keyword parameters allow passing just one and do not bother aboutsupplying the default case for the other. For instance,

Result:=transfold.foldWith(count, 0) @ yss

(counting rows without row processing) or

transfold.mapWith(innerProduct @ xs) @ yss.

(transforming rows into inner product results without subsequent reduction).

• Iterator usage. The purpose of this pattern description was to present the me-chanics of Transfold. This is why the client had the burden to ask the aggre-gate for a stream and feed it into a transfold. It would be more convenient tocall a client method that automatically sets up an appropriate transfold objectand returns the result. Using a C++ template method this is a feasible ap-proach. With EIFFEL, however, there is a problem how to specify the type ofthe return value. The type depends on the functions passed passed to trans-fold and it is unfortunately not possible13 to express that dependency in themethod’s signature. The usual way out is to use the same generic variable forconstraint values. However, this implies to add such generics to the aggregateclass, since it contains the method that must enforce the constraint. Withoutdebate, this presents a true kludge and the best one can achieve with EIFFEL isa convenience function, that takes transfold’s parameters (except the streammatrix) plus an aggregate, and then applies transfold to a streamed versionof the aggregate.

13Not even with anchored types.

Page 200: A Functional Pattern System for Object-Oriented Design

182 10 Transfold

10.9 Sample Code

To calculate the inner product of a matrix (see figure 10.7 on page 172) we code theinner product product operation (see definition 10.8 on page 172) in EIFFEL as:

...local

ip : Function [Stream [Stream [Integer]], Double]...

ip:=transfold @ plus @ 0.00 @ (fold @ times @ 1.0);...

We use the same type progression (integer, real, double) for the intermediate resultsas in figure 10.7 on page 172. Hence, times and plus must promote from integerto real and real to double respectively, but — apart from that — are standard func-tion objects. In case of a fully lazy treatment — which we omit here for the sake ofclarity — times and plus must respect the laziness of their second argument.

The new function ip can be applied to a matrix, so presuming

vec1, vec2, vec3 : Stream [Integer];vecs : Stream [Stream [Integer]];...vec1:=cons @ 8 @ (cons @ 1 @ fromconst (6));vec2:=cons @ 3 @ (cons @ 5 @ fromconst (7));vec3:=cons @ 4 @ (cons @ 9 @ fromconst (2));

vecs:=conss @ vec1 @ (conss @vec2 @ (conss @ vec3 @ void));

— note that cons produces an integer stream, while conss produces a stream ofan integer streams — the examples

io.putdouble (ip @ vecs);

io.putdouble (ip @ vecs.tail);

will produce the output as given in figure 10.12.

225 = IP @vecs

8 1 6

3 5 7

4 9 2

}IP @vecs.tail = 71

Figure 10.12: Inner product application

The horizontal processing function is a fold declared as

Page 201: A Functional Pattern System for Object-Oriented Design

10.9 Sample Code 183

fold : expanded Fold[Integer, Real].

The use of expanded is an idiom to save the otherwise necessary explicit attach-ment of an object to fold (see section 7.9 on page 105, Creation). Apart from theargument-collection classes, Fold is implemented with

class Fold2[E, A]inherit Function[Stream[E], A];

creation make

featurefunc : Function[E, Function[A,A]];init : A;

make(i : like init; f : like func) isdo

init:=i;func:=f;

end;

infix "@" (stream : Stream[E]) : A islocal fold : expanded Fold[E, A];do

if stream=void thenResult:=init;

elseResult:=func @ stream.item @

(fold @ func @ init @ stream.tail);end;

end;

end

and, thus, shares a striking similarity to its functional counterpart (see defini-tion 10.9 on page 180).

With the help of fold and map (which was established in chapter 8 8.10 onpage 143), the class implementing the body of transfold is quite straightforward:

class TransFold3[E, I, A]inherit Function[Stream[Stream[E]], A]

StreamUtility[E]creation make

feature

Page 202: A Functional Pattern System for Object-Oriented Design

184 10 Transfold

foldFunc : Function[I, Function[A, A]];init : A;mapFunc : Function[Stream[E], I];

make(f : like foldFunc; i : like init;m : like mapFunc) is

dofoldFunc:=f;init:=i;mapFunc:=m;

end;

infix "@" (streams : Stream[Stream[E]]) : A islocal

map : expanded Map[Stream[E], I];fold : expanded Fold[I, A];

doResult:=fold @ foldFunc @ init @

((map @ mapFunc) @ transpose(streams));end

...

The application method directly corresponds to the functional transfold (see def-inition 10.3 on page 171). I intentionally left out the transpose method of classTransFold3 , because it nicely demonstrates the difference between a high-level,functional, internal iteration style —

transpose(streams : Stream[Stream[E]]) : like streams is...do

newRow:=mapToHeads @ head @ streams;tails:=mapToTails @ tail @ streams;

if (fold @ oneEmpty @ False @ tails) thentails:=void;

elsetails:=transpose(tails);

end;

Result:=consS @ newRow @ (tails);end

— and its “von Neuman14”, procedural, external iteration style:

14Referring to the critique of John Backus towards a “von Neumann [programming]Style” [Backus78].

Page 203: A Functional Pattern System for Object-Oriented Design

10.9 Sample Code 185

transpose(streams : Stream[Stream[E]]) : like streams is...do

fromrows:=streams;!!rowArray.make(1, 7);

until rows=voidloop

rowCount:=rowCount+1;rowArray.force(rows.item, rowCount);

rows:=rows.tail;end;

fromuntil shortestEndsloop

fromrow:=rowCount;newRow:=void;

until row=0loop

newRow:=cons @ (rowArray @ row).item @ newRow;rowArray.put((rowArray @ row).tail, row);shortestEnds:=shortestEnds or

((rowArray @ row) = void);row:=row-1;

end;

Result:=consS @ newRow @ Result;end;

end

The latter version uses nested loops to construct new rows by repeatedly collectingheads and checking for the shortest row. The functional version (implementing def-inition 10.2 on page 169) replaces this nesting with normal mapping and a recursivecall.

The procedural version uses an array to memorize the respective positions ofthe partially consumed rows. The functional version avoids this by transformingthe argument matrix into the desired residual matrix.

It is noteworthy to observe that the procedural version features one loop count-ing downward, rather than upwards to get the element order right. No such detailmust be respected in the functional version15.

15Admittedly, on other occasions one has to think about whether to add to the front or to the backof a stream.

Page 204: A Functional Pattern System for Object-Oriented Design

186 10 Transfold

Also, although both versions do not transpose matrices with an infinite numberof rows (infinite rows are handled), the functional version is easier to extend to-wards this property. First, because it is simpler and easier to understand. Second,because the recursive call to transpose exactly defines the extension hot spot:The strict recursive call must be replaced by a lazy stream generation, using theframework of StreamFunc s, presented in chapter 8 on page 115.

A final example, hinting at the expressiveness of transfold (see section 10.10 onthe next page for a comparison with APL), shall be the implementation of matrixmultiplication with transfold. To show the result of the multiplication of a magicsquare with another matrix, as depicted in figure 10.13,

8 1 6

3 5 7

4 9 2

⊗1 1 0

1 0 1

0 1 1

=

9 14 7

8 10 12

13 6 11

Figure 10.13: Matrix multiplication with a magic square

one simply calls

showMatrix(matMult(vecs, rows));

using

matMul(xss, yss : Stream[Stream[Integer]]) : like xss islocal

transMapInner : expanded TransMapInner[Integer];map : expanded Map[Stream[Integer], Stream[Integer]];

doResult:=map @ (transMapInner @ yss) @ xss;

end;

We exploit the fact that the elements of the result matrix are defined by

zi j = inner product xi ytj ,

i.e., the inner products of all rows of the left matrix x with all columns of the rightmatrix y determine the result matrix.

To implement this, we process each row of the left matrix with a func-tion transMapInner , that applies the inner product operation to its argumentrow of the left matrix with each column in the right matrix, i.e., the body oftransMapInner is:

Result:=transFold.mapWith(applyInner @ xs) @ yss;

Page 205: A Functional Pattern System for Object-Oriented Design

10.10 Known Uses 187

Each row (xs ) of the left matrix is transfolded with the right matrix, i.e., combinedwith each column of the right matrix, and the inner product operation is applied toeach such pair.

The applyInner function simply constructs a two row matrix out of its twostream arguments and applies the inner product operation to it:

...ip:=transFoldInner @ plus @ number.zero @

(fold @ times @ number.one);Result:=ip @ (cons @ xs @ (cons @ ys @ void));

The use of number.zero 16 achieves that the function is generic, since either 0, or0.0 would create a type-mismatch with the wrong generic instantiation of matrixmultiplication17.

Summarizing, matrix implementation was implemented by mapping, and twotransfolds, one realizing the inner product operation.

10.10 Known Uses

Although C++ usually promotes external iteration, there is an example of an in-ternal iterator (foreach ) interface in the Borland C++ libraries [Borland94]. Sinceit builds on passing function pointers, it must use an extra, unsafe void type forpassing parameters, though.

Folding is commonplace in functional programming [Bird & Wadler88], butalso used in SCHEME [Abelson & Sussman87] and available in the SMALLTALK li-brary [LaLonde94]. It is not a frequently used operation in SMALLTALK [Smith95],which can be attributed to the peculiar name (inject: into:), but also to the unfamil-iarity of most SMALLTALK users with the nature of that operation. The SMALLTALKcollection library even contains a with: do: method, allowing to iterate two struc-tures in parallel, which represents a special case of transfolding. SMALLTALK alsouses streams to efficiently implement concatenation of collections. The caching ef-fect of streams avoids to repeatedly generate the prefix of sequenced concatenationslike coll1 + coll2 + coll3 + coll4 + ... .

APL [Harms & Zabinski77] is well-known for its high-level operations on vec-tors, matrices, and structures of even higher dimension. Three of its four primitiveextension operators18, reduction (f/ A), scan (f\ A), and innerProduct (Af.g B), candirectly be expressed with transfold. The fourth, outerProduct (A◦.f B), is express-ible with a combination of transfoldand map. The matrix multiplication examplefrom section 10.9 on page 182, builds an outer product of the matrices’ rows andcolumns, and then reduces it with an inner product.

16number is a variable declared to be of the generic matrix element type.17Although an escape was possible, one would wish that simply 0 has had sufficed.18Extend an operation to a collection.

Page 206: A Functional Pattern System for Object-Oriented Design

188 10 Transfold

10.11 Related Patterns

10.11.1 Categorization

Function Object: Transfold’s interface to clients is that of a Function Object.

Iterator: Transfold realizes internal iteration, whereas the Iterator pattern uses ex-ternal iteration [Gamma et al.94]. There is also an internal variant of the Iter-ator pattern [Gamma et al.94], but it uses inheritance for iterator and actioncombination and exhibits the normal one-structure-iteration inflexibility.

Bridge: The stream between structure and iterator is like a Bridge [Gamma et al.94],separating a consumption interface from the implementation of a structure(including the exploration implementation).

Serializer: The use of an intermediate stream resulting from structure explorationis akin to the Serializer pattern [Riehle et al.97], whose purpose is to flattenstructures for persistence.

10.11.2 Collaboration

Lazy Object: Transfold is very well suited to transform one lazy structure into an-other.

Value Object,Composite: Iterators are often applied to recursive (possibly value) structures like

Composite . Furthermore, Composite [Gamma et al.94] may be implementedwith Transfold to enumerate its children.

Translator,Visitor: Structure consuming patterns may defer their structure exploration to

Transfold.

Chain of Responsibility: An event may be transfolded over a collection of handlersto find the most appropriate handler.

Observer: An Observer [Gamma et al.94] may use Transfold to iterate over itsdependents-collection.

Keyword Parameter: Transfold may use the keyword parameter variant of FunctionObject (see section 7.10 on page 107) to ease supplement of its parameters.

10.11.3 Implementation

Lazy Object: Transfold uses a lazy stream as well as lazy generation and consump-tion functions.

Page 207: A Functional Pattern System for Object-Oriented Design

10.11 Related Patterns 189

Void Value: A void value may be used to define the base case of the explorationprocess.

Function Object,Strategy: A structure may use a Strategy [Gamma et al.94] or a Function Object to

allow variation of its exploration process. Function Object is used to provideclosures for function parameter passing and is also employed to implementTransfold’s functionality.

Page 208: A Functional Pattern System for Object-Oriented Design

190 10 Transfold

Page 209: A Functional Pattern System for Object-Oriented Design

191

11 Void Value

I’m not completely worthless! I can still serve as a bad example.– Mark Twain

11.1 Intent

Raise Nil to a first-class value. This allows to treat void and non-void data uni-formly, is a way to provide default behavior, facilitates the definition of recursivemethods, and enables to deal with error situations more gracefully.

11.2 Motivation

Classes are often regarded as representing abstract data types (ADTs). Typically,the empty constructor of an ADT is translated to Nil. For instance, an empty Bina-ryTree is represented by Nil and a tree leaf is represented by setting both child at-tributes to Nil. It is tempting to identify empty constructors with the special pointercontent Nil, since there are some reasons in favor of it:

• Operations are often partial due to empty, well accounted for by Nil.

• Nil does not waste any storage.

• Most languages initialize references to Nil, i.e., provide the empty case bydefault.

It is, for instance, suggested to represent an unknown book author with a voidreference [Meyer97].

11.2.1 Problem

However, such a design decision puts a great burden on the clients of the ADT.Before the client can apply operations on a tree, it must check whether it is voidor not. Otherwise, invoking a method through a Nil reference would produce aruntime-exception or even -error. Typical (EIFFEL) code1 is depicted at the upperpart of figure 11.1 on the next page.

1All code examples in this chapter have been extracted from a commercially available library;identifiers have been adapted for presentation, though.

Page 210: A Functional Pattern System for Object-Oriented Design

192 11 Void Value

Result:=(tree /= Void) if tree /= Void thenand then io.putstring(tree.out);tree.has(v);

Result:=tree.has(v); io.putstring(tree.out);

Figure 11.1: Code example: Robust behavior

11.2.2 Solution

The code in the upper part of figure 11.1 can be replaced with the code at thelower part of figure 11.1, if we abandon Nil (Void) and replace it with an extraclass (VoidTree ) (see figure 11.2).

VoidTree MutableTree

has(v : Any) ...out

has(v : Any) = falseout... ...

Client Tree

has(v : Any), out, ...

Figure 11.2: Empty tree as void value

Now we can define VoidTree to return false on a has message and to produceno output on out. In fact, we applied the general pattern to replace case analysis(testing for Nil) with dynamic binding (provision of an extra type constructor Void-Value ). As a result, many such if statements as above can be removed from clientcode.

An important special case of robust behavior is a Null iterator (also see Itera-tor [Gamma et al.94]). Instead of external iteration (upper part of figure 11.3 on thenext page) one should use internal iteration, e.g., and let void sets do nothing oniteration (lower part of figure 11.3 on the facing page).

Answering queries to void values with default values again allows replacingthe upper part of figure 11.4 on the next page with its lower part. This obviouslyimproves the treatment of trees from a client’s perspective, but there is more togain. Let us count the number of nodes in a tree.

Page 211: A Functional Pattern System for Object-Oriented Design

11.2 Motivation 193

if set /= Void then

loop over all set elements

setIterator.do_all

Figure 11.3: Code example: Null iterator

if tree /= Void thenmaxPlane:=tree.maxPlane

elsemaxPlane:=DefaultMaxPlane

end

maxPlane:=tree.maxPlane

Figure 11.4: Code example: Default Behavior

The recursive calls in the upper part of figure 11.5 must be guarded with if state-ments when tree leafs are represented with Nil. If void tree values return 0 as theircount, then the code becomes much more readable and better expresses the func-tion’s spirit (see lower part of figure 11.5).

Result:=1;if left /= void then

Result:=Result+left.count;endif right /= void then

Result:=Result+right.count;end

Result:=1+left.count+right.count;

Figure 11.5: Code example: Base case definition

The provision of base cases by void values is a special case of the general schemeto distribute cases. Complicated nesting, like depicted in the upper part of fig-ure 11.6 on the following page becomes much clearer code when distributed toVoidAny and AnyObject class definitions. Note that the use of obj.is void isdifferent to obj = Void . The former tests for voidness as well, but allows the rep-resentations for void objects to be changed. This is useful for multiple void valuesas well as for standard objects which just happen to be in an empty or void state.

Page 212: A Functional Pattern System for Object-Oriented Design

194 11 Void Value

equal(me: ANY; you: ANY): ...Result:=(me=Void and you=Void)or else ((me/=Void and you/=Void)

and then me.is_equal(you))

Result:=you.is_void (in class VoidAny )Result:=is_equal(you) (in class AnyObject )

Figure 11.6: Code example: Case distribution

It is worth mentioning, that we may provide void trees, -nodes, and -leafs in-dependently with possibly differing behavior. See section 11.10 on page 199 for ananalogous example when this is useful. Also note that while void values are a nat-ural concept for container classes their applicability extends to all types of classes.For instance, the subordinates method of a VoidEmployee can return an empty list.Its salary method may create a no-valid-employee exception, return 0, or a VoidSalaryinstance, depending on the desired semantics. All three possibilities may coexistin separate void values. Application parts can individually choose to inject theappropriate void value into server calculations.

11.3 Applicability

• Uniformity. Clients benefit from Void Value by uniformly treating void andnon-void data. Inquiring information and invoking behavior can be doneindependently of the data’s void state, without the eventual need for creatinginitial instances first.

• Default behavior. Void values allow the specification of default behavior. Incontrast to Nil, a void value may specify results and behavior for any of itsinterface methods. This is useful for providing reasonable behavior for unini-tialized data and void result values.

• Error handling. A void value can be used just like an exceptionalvalue [Cunningham94] when most of the code should not deal with er-ror situations. An exceptional value may either behave properly indicatingan error situation (e.g., by describing the error when by default displayedto the user) or can be caught with error checking code by the application’stop-level code.

• Termination. Use Void Value to relieve recursive definitions from testing thebase case. A recursive call can then be made regardless of possibly void argu-ments. Accordingly, the definition for the base case can be given at the basevalues (i.e., void values) instead of one step before. Here, Void Value playsthe role of a first-class terminator.

Page 213: A Functional Pattern System for Object-Oriented Design

11.4 Structure 195

11.4 Structure

Client ObjectInterface

is_voidoperation

MutableObject

is_void = false

VoidValue*

is_void = true

VoidValue

is_void = trueoperationoperationoperation

Figure 11.7: Void Value structure diagram

11.5 Participants

• Client

– accesses both MutableObject and VoidValue through ObjectInterface .

• ObjectInterface

– provides the common interface for MutableObject and VoidValue .

– may provide common abstract2 behavior to MutableObject and Void-Value .

• MutableObject

– defines the standard object behavior.

– introduces attributes for object state.

– does not care for recursion base cases, default- and error behavior.

• VoidValue

– replaces Nil.

– defines base cases for recursion, default- and error behavior.

2Concrete methods do not rely on state (attributes), but on an abstract interface, only.

Page 214: A Functional Pattern System for Object-Oriented Design

196 11 Void Value

11.6 Collaborations

• Clients use the ObjectInterface to manipulate void, non-void, default, and er-ror objects. If MutableObject extends the interface of ObjectInterface , clientsmay maintain specialized references to MutableObject , provided they areguaranteed to receive objects of type MutableObject only.

11.7 Consequences

• Abstraction. Void Value abstracts from the implementation detail to representvoid or uninitialized data with Nil. Clients, therefore, are relieved of the needto treat data with Nil differently from data which does not use Nil for itsrepresentation.

• Object-Orientation. Checking for Nil, i.e., case analysis, is replaced with dy-namic binding. This moves all case analysis from the program to a singledistinction between normal object and void value. Object behavior must bedefined at two places (object and void value). One the one hand, this allowscombining several void values with a single object. On the other hand, chang-ing a single method might mean to change two class definitions (object andvalue class).

• Efficiency. It is more storage friendly to use void values, instead of full blownobjects that simply have the state of being empty or uninitialized, but carrythe overhead of unused attributes. Note, however, that void values are not aseasily turned into objects again without support for “Implicit creation” (seesections 11.8 on the facing page & 14.7 on page 248).

Depending on the implementation of dynamic binding a calling overheadmay be incurred, compared to if statements. Note, however, that the tradi-tional solution at the upper part of figure 11.5 on page 193, unnecessarilychecks aritydepth−1

arity−1 nodes for being void, in addition to the following countcall.

• Immutability. If void values should not need more storage than Nil, they haveto be immutable. Mutable objects without attributes just claim code spacefor their methods once, and possibly a little storage for housekeeping mecha-nisms such as RTTI (Run Time Type Identification) and object identification.

• Separation. Program code relying on Void Value describes the standard caseonly. In analogy to language support for exception handling, framework codeor algorithm descriptions thus become easier to write and read (see code ex-amples). Handling of errors, denoted by void values, can be done in a fewplaces at the application’s top level. Lower level code can be designed tosmoothly pass error values around, until they are identified by the top level.

Page 215: A Functional Pattern System for Object-Oriented Design

11.8 Implementation 197

• Implicit initialization. In order to avoid any occurrences of Nil, one must tiethe creation of objects to the declaration of references. See section 11.8 forsuggestions how to automatically achieve initialization to void values.

• Initialization errors. Exceptions, caused by the application of operations onNil, are dealt with more gracefully. Instead of triggering an exception or halt-ing the system a possibly inappropriate behavior is executed, but users arestill able to continue the application. The downside of this is that a missinginitialization might go unnoticed, only to be discovered as unexpected appli-cation behavior very much later in the execution. According to the experi-ences of the UFO project [Sargeant93], however, unnoticed missing initializa-tion has not been found to be a problem in practice. Additionally, it is almostas easy to pass Nil around undetected. Error messages simply become moremeaningful with void values [Sargeant96b]. If the void value is one amongseveral alternatives for a type, its identity allows tracing back its origin.

Yet, care should be taken to never allow the misinterpretation of an erroneousvalue to be a valid result. Calculations must preserve the exceptional statusof void values to prevent the masking of errors.

11.8 Implementation

• Interface extension. In analogy to the Composite pattern [Gamma et al.94], Mu-tableObject might provide more operations than ObjectInterface , in order toprovide operations that make no sense to VoidValue . Each such extension,however, will minimize the applicability of VoidValue to play the role of anerror value. Clients using the extended MutableObject interface, will not beable to transparently work on void values too. In this context, it is better tohave the full interface at ObjectInterface and to define exception (e.g., errorreporting) methods in VoidValue respectively.

• Storage requirements. Any behavior that goes to ObjectInterface should relyon abstract state only, i.e. “Implement behavior with abstract state” [Auer94].Any attributes will incur a space penalty on VoidValue .

• Value initialization. If representation of a complex constant (requiring creationeffort) is more important than preserving minimal space requirements, thena void value may calculate the constant information (possibly taking creationarguments) and store it in attributes. Accordingly, VoidValue is best imple-mented as a Singleton [Gamma et al.94] in order to allow sharing of the con-stant data.

• Multiple inheritance. In statically typed languages VoidValue needs to multiplyinherit from its interface class and another void value class, if code reuse isdesired between void value classes. One of the inheritance paths is used only

Page 216: A Functional Pattern System for Object-Oriented Design

198 11 Void Value

for interface inheritance though, unless the interface classes implement com-mon behavior for VoidValue and MutableObject . In the latter case the languageshould properly support repeated inheritance of code (e.g., as in EIFFEL).

• Reference initialization. The value to be gained from Void Value partly de-pends on how thoroughly we can eliminate any Nil values from programexecution. To this end, one may replace standard references with default-references, akin to smart references [Edelson92].

Client ObjectInterface

VoidValue DefaultReference

makevalue constructor

MutableObject

Figure 11.8: Automatic reference initialization

A DefaultReference (see figure 11.8) is a value (e.g., non-reference type in C++,expanded type in EIFFEL, part-object in Beta, etc.). Consequently, it is initial-ized by declaration, i.e., cannot take the value Nil. After initialization it dele-gates any message to VoidValue by default. Hence, any usage of DefaultRefer-ence without prior initialization results in applications to VoidValue , insteadof Nil. Ultimately, DefaultReference will be initialized (e.g., using a methodcalled make) to reference a MutableObject . In the structure of figure 11.8 De-faultReference plays the role of an ObjectProxy . Implementation issues, suchas using overloading of the member access operator (-> ) in C++, are dis-cussed in the implementation section of Proxy [Gamma et al.94]. Note thatusing a DefaultReference involves a delegation overhead for each access toeither VoidValue or MutableObject . This might be a price too high to pay justfor gaining automatic initialization of references.

Of course, we can achieve automatic initialization of MutableObject by di-rectly declaring it to be a value, resulting in a much simpler structure andno delegation overhead. Yet, this is possible only if value semantics (e.g., nosharing, no efficient updates) is intended [MacLennan82].

• Implicit creation. SMALLTALK allows changing the type of an object withoutaffecting its identity. Ergo, a void value may change to an object and vice

Page 217: A Functional Pattern System for Object-Oriented Design

11.9 Known Uses 199

versa. If a void value cannot deal with a message it may delegate it to its as-sociated object and then become:3 the object itself. Hence, there is no need forexplicitly creating objects anymore, since void values can be made responsi-ble for this. In the absence of a become mechanism, implicit creation can stillbe achieved by “Reference Initialization” (see above).

11.9 Known Uses

NoController , NullDragMode , NullInputManager , NullScope are classes in the classhierarchy of VISUALWORKS SMALLTALK that are used as void values [Smith95].

11.10 Related Patterns

11.10.1 Categorization

Composite: Both Composite and Void Value provide uniform access to components(value & object) of a structure (type). A VoidValue plays the role of a spe-cial immutable component leaf. As a difference, Void Value does not involveissues of child management.

Value Object,Constant Object: A void value “is-a” value object, in that it does not offer mutator

operations. As value objects, void values lend themselves to be implementedwith Identity Object (see next bullet and section 9.11.1 on page 161).

Singleton: A void value is a Singleton [Gamma et al.94], in that it can be shared byall objects of a type. As it is immutable, there is no need to have multipleinstances.

Flyweight: VoidValue can be regarded as a ConcreteFlyweight , as it holds intrinsicdata and is shared among all clients that use it.

State: When used as described in Implicit creation at section 11.8 on page 197, Void-Value and MutableObject play the role of ConcreteStates , representing thevoid and non-void states of data.

11.10.2 Collaboration

Lazy Object: Pattern Lazy Object (see chapter 8 on page 115) may be used to lazilyinitialize void value components.

Command: Void Command may stand for default or idle behavior.

3SMALLTALK’s method name to replace objects.

Page 218: A Functional Pattern System for Object-Oriented Design

200 11 Void Value

Template Method: While subclass responsibility [Goldberg & Robson83] normally re-quires at least one concrete subclass, Void Value can be used as a default sub-class, allowing the execution of abstract methods. Of course, Void Value isjust a special concrete subclass, so this collaboration is a lot more dramaticwith language support for void values (see section 14.7 on page 248).

Memento: A Void Memento can be used as a default and reset state.

Factory Method,Abstract Factory: A void value can be used as a return value for not available prod-

ucts, e.g., unsupported widget types with regard to a specific window factory.

Iterator, Visitor,Chain of Responsibility: All these patterns are based on iteration and Void Value can

help to define their termination behavior, analogous to a base case in recur-sion.

State,Strategy: Void State may represent the initial or an error state. While used like Void

State, Void Strategy would allow StrategyContext creation without a Strategyselection.

11.10.3 Implementation

Proxy: DefaultReference (see figure 11.8 on page 198) works as a Proxy since itkeeps a reference to an object whose interface it shares.

Bridge: DefaultReference behaves like a bridge, in that it shields the clients from thetwo “implementations” VoidValue and MutableObject .

Page 219: A Functional Pattern System for Object-Oriented Design

201

12 Translator

The art of programming is the art of organizing complexity.– Edsger W. Dijkstra

12.1 Intent

Add semantics to structures with heterogeneous elements without changing theelements. Separate interpretations from each other and use local interpretationsthat allow for incremental reevaluation.

12.2 Motivation

Many operations on data structures can be viewed as homomorphisms, that is, asstructure preserving mappings from one domain into another. For instance, com-pilers typically map the abstract syntax of the source language into a specific ma-chine code language1. Other kinds of abstract interpretations (e.g., pretty-printingand type-checking) should be expressed as homomorphisms between source andtarget domain as well. The reason for this recommendation can be explained bymeans of an equation that holds, if a homomorphic relationship between two struc-tures exists:

φ(op(a,b)) = op′(φ(a),φ(b)) (12.1)

An interpretation φ on an operation op (from a source domain) with subcompo-nents a and b is defined as a new operation op′ (from a target domain) whosesubcomponents are determined by again applying φ to a and b [Wechler92]. Aninstance of this general equation for a compiler is, e.g.:

compile (assign (lhs , rhs )) =store (compile (lhs ), compile (rhs ))

Note how in the above equations an interpretation is shifted down from operatorsdown to operands. Also, the right hand side of the equations has a structure thatallows us to account for incremental modifications to the source structure. In caseof changing the left-hand-side (lhs ) of assign , there is no need to rebuild the

1Historically, homomorphisms are closely connected to syntax-directed transla-tions [Aho et al.86] and correspond to compositional definitions [Nielson & Nielson93].

Page 220: A Functional Pattern System for Object-Oriented Design

202 12 Translator

whole result term. One simply has to apply compile to the changed lhs and plugthe result into the first operand of store .

12.2.1 Problem

Consider a programming environment that represents programs as abstract syntaxtrees. It will need to perform various interpretations on the abstract syntax treelike type-checking, code generation, and pretty-printing. Figure 12.1 depicts twosample transformations.

"B"

IfThen

MOVE W(vars), R MOVE V(vars), R

Assign

"V" "W"

Pretty Print

TEST

BEQ <LABEL>

<LABEL> NOP

"V" "W"

":=""B"

"THEN"

"IF"

MOVE B(vars), R

n

n n

mMOVE R , R

n

"END"

abstract Syntax Tree

Assembler Code

Figure 12.1: Homomorphic translations of trees

The result of a mapping (dashed arrows in figure 12.1) depends on the inter-pretation (e.g., compilation) and concrete node type (e.g., assign) involved. Onemay put all various interpretations (type-check, pretty-print, etc.) into the nodeinterface in order to rely on dynamic binding. However, this is often not a goodidea:

• It leads to a system that is hard to understand, maintain, and change.

• Adding a new interpretation means changing and recompiling all node types.

Page 221: A Functional Pattern System for Object-Oriented Design

12.2 Motivation 203

• An interpretation cannot be added without changing the node interface.

• The interface of nodes will grow until it becomes bloated.

The first two arguments are also addressed by the Visitor pattern [Gamma et al.94]).Visitor also addresses the problem of adding functionality to each node-type (rep-resented by a class) in a conceptual hierarchy (e.g., abstract syntax, constructiondata, etc.) but does not aim at incrementality and demands node-types to knowabout external interpretations (see section 12.10 on page 217).

The last two arguments of the above list especially apply to data structures otherthan abstract syntax trees. Consider a data structure that represent the logical struc-ture of a building. It is probably only well after designing the interface to thatstructure that one wishes to perform some interpretation like computing the totalrent income. In this context, it is useful to differentiate between intrinsic properties(e.g., nodes have descendents) and extrinsic properties (e.g., pretty-print). There isno end to extrinsic properties and it does not make sense to lump all of them intoone interface.

Now, if we provide interpretations as external features we are facing a problemwith an implementation language that provides single-dispatch only2. As alreadymentioned, the code to be executed for each node when we traverse an abstractsyntax tree depends on two variabilities:

find-implementation(node-type, interpretation)

Note that we already rejected node-type.interpretation with the argu-mentation above. The reverse, interpretation.node-type , does not makesense, since unlike the interpretation type the node type always changes duringtree traversal; that is, dispatch isn’t required for the receiver but for the argument.

12.2.2 Solution

What we need is double-dispatch on both node-type and interpretation .Fortunately, there are ways to emulate double-dispatch and its generalizationmulti-dispatch, with a single-dispatch language. We opt for a solution which can becharacterized as external polymorphism (see section 12.10 on page 217 for Visitortype double-dispatch). Unlike Cleeland et al., however, we do not use a combina-tion of C++templates, Adapter, and Decorator [Cleeland et al.96]. We simply usethe natural notion of a generic function [Kuhne97].

When a generic function object is applied to a node, it determines the node’stype (by using runtime type information), creates the corresponding specializedfunction object, and returns the result of applying the specialized function object tothe node.

Figure 12.2 on the next page depicts how concrete element types (e.g., IfThen ) in-duce the creation of their corresponding specialized functions. A specialized func-tion knows the exact type of its argument and, therefore, can appropriately exploitthe argument’s full interface.

2Languages with multi-dispatch, e.g., CLOS, Cecil, or Dylan are not in widespread use.

Page 222: A Functional Pattern System for Object-Oriented Design

204 12 Translator

interpret Identifier

interpret Assign

interpret IfThen

generic Function

"B"

"W""V"

Interpretation

IfThen

Assign

abstract Syntax Tree

Figure 12.2: Generic interpretation on an abstract syntax tree

Note that it is not only natural to deal with generic functions to achieve double-dispatch, but also very natural to employ functions for translations. The approachof formally defining the semantics of a programming language called denotationalsemantics is entirely based on semantic functions, i.e., functions that transformphrases into denotations [Schmidt86].

Figure 12.3 on the facing page shows the structure diagram that corresponds tothe domains used in figure 12.1 on page 202. Only relationships relevant to Transla-tor have been included. For instance, language nodes like ToyIf will typically havean aggregation relation with ToyLang which is not shown here. Exclamation marksdenote places of possible extension (see section 12.7 on page 209, Extensibility).

Class Language in figure 12.3 on the next page is not required in general (seefigure 12.4 on page 206). Also, it is not required that ToyIf , ToyAss , etc. have a com-mon ancestor (like ToyLang ). Hence, one can define semantics on heterogeneouscollections where element types may come from different libraries.

12.3 Applicability

Use the Translator pattern for

• Adding semantics. When you want to add an interpretation (even withouthaving planned for it) to a number of classes that have different interfaces,Translator allows accessing the heterogeneous interfaces individually. Theclasses need not belong to same hierarchy or library.

• External functionality. Adding interpretations outside of elements avoidsbloating the elements’ interfaces with extrinsic concepts. Also, if interpreta-

Page 223: A Functional Pattern System for Object-Oriented Design

12.3 Applicability 205

!

!

!

Client

Function

Language

ToyAssToyIf ToyVar PpIf PpAss PpVar

GenFunc PfIf PfAss PfVar

PpFunctions

Functions

ToyLang PpLang

print(indent : Integer)

Figure 12.3: Sample structure

tions require additional servers (e.g., environment lookup for type-checking)the interpretations, as opposed to the elements, will depend on the servers, i.e.,require recompilation in case one server changes.

• Incrementality. When small changes to big structures should not cause reeval-uation of the whole structure, exploit the homomorphic properties of Transla-tor and use the intermediate structure (see figure 12.6 on page 209) for storingcomputed results.

Do not use the Translator pattern in case of

• Unstable elements. When new elements are frequently added to the sourcestructure it is probably better to define the interpretations in the elements.

Page 224: A Functional Pattern System for Object-Oriented Design

206 12 Translator

Otherwise, one has to constantly change all associated function packages (seefigure 12.4 and also section 12.7 on page 209).

• Space constraints. Unless you can make use of the benefits of a target struc-ture (see section 12.7 on page 209, Separation.), avoid the space overhead bydirectly translating to results.

12.4 Structure

Client

ConcreteFunctionPackage

Function

SourceElement

SourceElement

TargetElement TargetElement

GenFunc SpecialFunction SpecialFunctionFunctionPackage

TargetLang

evaluate

App

licat

ion

rela

ted

Patte

rn r

elat

ed

21

1

2

21

Figure 12.4: Structure diagram

Page 225: A Functional Pattern System for Object-Oriented Design

12.5 Participants 207

12.5 Participants

• Function (Function )

– declares an interface for function application. Its two type parametersspecify argument and result type respectively3.

– is used as the interface specification for both generic and specializedfunctions.

• Generic function (GenFunc )

– corresponds to a denotational function definition.

– uses function package Functions and runtime type information to chooseand then delegate to a specialized function.

• Specialized function (e.g., PfIf)

– corresponds to one pattern matching branch of a denotational functiondefinition.

– defines a local transformation for a source element (e.g., ToyIf ) to a cor-responding target element (e.g., PpIf).

– recursively transforms subcomponents of its argument as well.

• Function package (e.g., Functions )

– conceptually bundles related specialized functions.

– declares a generic package type for specialized functions to be refined byconcrete function packages.

• Concrete Function package (e.g., PpFunctions )

– defines a mapping from source elements to their corresponding special-ized functions.

– creates prototypes of — and then aggregates — specialized functions.

• Client

– creates or uses a source structure (e.g., ToyLang ).

– initializes or uses a function package (e.g., PpFunctions ).

– creates or uses a generic function (GenFunc ).

– applies a generic function to a source structure.

3As is the case with all generic functions of figure 12.4 on the preceding page.

Page 226: A Functional Pattern System for Object-Oriented Design

208 12 Translator

12.6 Collaborations

Figure 12.5 shows important object interactions. It refers to “@” for an infix func-tion application syntax (see sample code in section 12.9 on page 214 or design pat-tern Function Object at chapter 7 on page 93).

client ty_if pp_functions pf_var pf_ass pf_if pf_if* prettyPrint pp_if

make

init

make(pp_functions)

@(ty_if)

make

make

make

item("ToyIf")

clone

@(ty_if)

@(ty_ass)

@(ty_var)

make(pp_var, pp_ass)

Figure 12.5: Interaction diagram

• A client initializes a function package in order to create a generic functionfrom it. The client applies the generic function to the source structure in orderto obtain the translation result.

• The generic function consults the function package for a specialized functionthat matches the type of the argument. Then it applies a cloned exemplar ofthe specialized function to the argument.

• A specialized function recursively calls its associated generic function to thesubcomponents of its argument. Then it creates the target element while pro-viding it with the results of the subcomponent evaluation.

Page 227: A Functional Pattern System for Object-Oriented Design

12.7 Consequences 209

Note that the time line gaps in figure 12.5 on the preceding page denote poten-tial recursive mappings of subcomponents.

12.7 Consequences

Tradeoffs of Translator are:

• External functionality. Translator makes it easy to add interpretationsto data structures with heterogeneous elements. In contrast to Visi-tor [Gamma et al.94] there is no need to impose an Accept method onthe elements. Spreading interpretations over all elements (corresponding toobject-oriented design) would demand changing all elements when intro-ducing a new interpretation. Gathering all related behavior into one genericfunction (corresponding to functional design) — thus separating unrelatedbehavior (e.g., compilation from pretty-printing) — results in a clean partitionand allows to hide interpretation specific details (like interpretation specificdata structures and accumulated state) in generic functions.

v := w;

"THEN"

"END"

"IF"

Pretty-Print

END

IF b THEN

abstract Syntax Tree Pretty-Print Structure

"B"

"V" "W"

":="

EvaluationTranslation

IfThen

"B"

Assign

"V" "W"

Figure 12.6: Distinct interpretation phases

• Instability. When using Translator, adding new elements (e.g., changing theabstract syntax of a language) becomes difficult. Unlike Visitor, Translatordoes not demand that one extend all interpretations with a meaning for a newelement (e.g., compilation is not affected by adding a new type-declarationnode). However, updating of concrete function packages and creation of spe-cific functions is required. Note that the latter point must be done anyway,but if interpretations are element methods then their completeness can be en-forced by the compiler. Using Translator, runtime errors caused by an unde-fined mapping to a specialized function may occur.

Page 228: A Functional Pattern System for Object-Oriented Design

210 12 Translator

• Extensibility. It is easy to add new translations and/or target structures. Anew interpretation simply requires

1. defining a new target structure (top exclamation mark in figure 12.3 onpage 205),

2. defining specialized functions (rightmost exclamation mark), and

3. providing a function package that maps the source elements to their spe-cialized functions (leftmost exclamation mark).

The last action is a tribute to the emulation of generic functions.

• Flexibility. Elements to be translated need not be from one class hierarchy.In any case, the full individual interface can be accessed by the associatedspecialized functions.

• Broken encapsulation. Since specialized functions can access the public inter-face of elements only, the interface may be forced to be wider than neces-sary for other clients. A remedy is to use selective export or another kind of“friend” mechanism.

• Separation. The semantics of an interpretation is defined in terms of a targetstructure semantics (see figure 12.6 on the page before). Thus, a clear separa-tion between translation (mapping to a target) and target semantics (meaningof target) is achieved. Figure 12.7 depicts how an interpretation is split into a

Sourcetranslate- Target

Result?

evaluationinterpretation -

Figure 12.7: Splitting the interpretation

translation to a new Target structure and an evaluation function that producesthe final result. During the translation several simplifications are possible (seetable 12.1 on the next page).

A pretty-print, therefore, is not a sequence of side effects but, at first, a hier-archical structure of print-elements, combinators, and possibly layout func-tions. In a second step the, e.g., string representation of a pretty-print is pro-duced from this structure. Note that now it is perfectly alright to implementthe semantics of the target structure as member methods of the target struc-ture. Since that structure is meant for only one purpose, as opposed to the

Page 229: A Functional Pattern System for Object-Oriented Design

12.7 Consequences 211

Notation Meaning Example

φ(op1) = op′,φ(op2) = op′.

Map distinct source ele-ments onto one destinationelement.

Translate both assign andinitialize to store .

φ(op(a,b)) =op′(φ(a)).

Drop source operands. Do not consider type decla-rations for compilation.

φ(op(a,b)) = φ(a). Prune source operators. Compile procedure bod-ies only and forget aboutheaders.

φ(op(a,b,c)) = φ(b),if pred(a).

Perform static analysis toselect operands.

Compile if-then-elseto its then branch, ifthe boolean condition isalways true.

Table 12.1: Simplifications possible while translating

abstract syntax tree which has many interpretations, there is no drawback in-volved. Figure 12.8 shows the separation between interpretations and their

Tc

compile

compile- Rc

Stransc-

Tp

pretty

pretty-

transp-

Rp

Figure 12.8: Non-interfering semantics

associated target structures (Tc and Tp). The operation names inside the tar-get structure boxes denote their definition as member methods. There will beno new interpretations on target structures that would require to open andchange the whole set of their classes. The interpretation can be defined lo-cally for each element, exploiting inheritance and redefinition, without thedisadvantages mentioned in section 12.2 on page 201.

Besides the nice partition between translation- and semantic related code, thetarget structure also may serve as a logical structure for the final representa-tion. For instance, a mouse click onto a keyword could cause highlighting thewhole corresponding statement and its subcomponents. While this could beachieved by back-pointers into the original abstract syntax tree as well, it is

Page 230: A Functional Pattern System for Object-Oriented Design

212 12 Translator

cleaner and more appropriate (as no reinterpretation is necessary) to refer tothe pretty-print structure. This argument becomes more obvious in case ofinterpretations whose logical structure bear less resemblance to the abstractsyntax tree (e.g., type-checking information).

Also, assuming multiple users are working simultaneously on one abstractsyntax tree, multiple intermediate structures allow them to, e.g., use differentcompilation options to achieve various code results. If semantic results werestored directly in the source structure, this would be a cause for interference.

Furthermore, an intermediate structure is also helpful when aiming for incre-mental updates.

• Incrementality. Naturally, target structures are subject to fast incremental re-computation since they are produced by homomorphic mappings from theirsource structure (see section 12.2 on page 201). Assuming N to be the numberof elements in the source structure, the asymptotic amount of recalculationneeded to update the result of an abstract interpretation is reduced from O(N)down to O(logN). However, it is necessary to store previously computed re-sults somewhere. Our approach of non-intrusive addition of interpretationsforbids storing these at the source elements. The target structure, however,(see figure 12.6 on page 209; the small flash signs denote places of change)can serve as a store for incremental results. The target structure can play therole of an Observer [Gamma et al.94] that becomes notified by changes in thesource structure and starts the necessary recomputations. The target struc-ture, in turn, is observed by the final result.

Note that though a local source structure change will only cause a local tar-get structure change, the reinterpretation in general has to be done globally.Sometimes local changes to the interpretation result suffice (e.g., pretty-printof identifier), sometimes the path from changed location up to the root hasto be followed (e.g., context-relations [Snelting86]), and sometimes the wholetarget structure must be revisited (e.g., dynamic semantics). Also with regardto this aspect, the target structure serves as a physical anchor to distinguishbetween the two phases of translation and incremental reevaluation.

• Space overhead. If incremental evaluation is not an issue and space efficiencyis a priority, then the intermediate structure should be avoided in favor of adirect translation to the final semantics. In a distributed setting, however, wemay purposely want to trade space for speed. The intermediate structurescan be made local to their users, while the single source structure is accessedonly when needed. Ergo, frequent evaluations of target structures do not addto net traffic.

Page 231: A Functional Pattern System for Object-Oriented Design

12.8 Implementation 213

12.8 Implementation

Here are two issues to consider when implementing Translator (also see sec-tion 12.10 on page 217, Collaboration & Implementation):

• Mapping elements to functions. Translator uses runtime type information(RTTI) to determine the concrete type of a generic function argument. Thismechanism is very language dependent and may also vary with differentcompilers for one language. Usually, it is possible to obtain a string of theclass name or test for successful downcasting to a specific type. If no suchmechanism is available, one is left with explicitly programming a type in-quiry interface. This is, however, incompatible to the otherwise non-intrusivenature of Translator.

In any case, a generic function may also dispatch on values of objects as op-posed to their type. Consequently, you may represent musical notes and quar-ter notes by the same class. The corresponding objects will differ in a value,e.g., of attribute duration. Nevertheless, it is still possible to use a generic func-tion to dispatch on this note representation.

• Hiding function packages. It is easy to shield the client from the existenceof particular function packages, by providing a tailored generic functionthat creates a standard generic function with a fixed function package (e.g.,prettyFunctions ). The client code, i.e., using a generic function is simpli-fied to

source: ToyLang;prettyPrint: PrettyPrint;...!!prettyPrint.init;(prettyPrint @ source).display;

(compare to the version on page 215 of section 12.9). In order to achieve thiscode shortening and client encapsulation from function packages one simplyneeds to provide a tailored generic function like the one below.

class PrettyPrintinherit Switch[ToyLang, PpLang]redefine functions endcreation initfeature

functions : PpFunctions;

init is do!!functions.init;

end;end

Page 232: A Functional Pattern System for Object-Oriented Design

214 12 Translator

12.9 Sample Code

The following use of EIFFEL code should not conceal the fact that you mayuse Translator with any language featuring runtime type information such asSMALLTALK, C++, and JAVA.

Assume a toy source language with an if-statement (see figure 12.3 on page 205):

class ToyIftheninherit ToyLangcreation makefeature

exp, stat: ToyLang;...

end

The corresponding pretty-print element could be:

class PpIftheninherit PpLangcreation makefeature

pexp, pstat: PpLang;

make (e, s : PpLang) isdo

pexp:=e;pstat:=s;

end

display isdo

io.putstring ("IF ");pexp.displayio.putstring (" THEN ");pstat.display;io.putstring (" END")io.new_line;

end;end

Now we need the specialized function that maps an if-statement to its pretty-print element.

class PrettyFunctionIftheninherit Function[ToyIfthen, PpIfthen]creation make

Page 233: A Functional Pattern System for Object-Oriented Design

12.9 Sample Code 215

featuregenFunc: GenFunc[ToyLang, PpLang];

make(s: like genFunc) isdo

genFunc:=send;

infix "@" (ift: ToyIfthen) : PpIfthen isdo

!PpIfthen!Result.make(genFunc @ ift.exp,genFunc @ ift.stat)

end;end

The creation argument of type GenFunc specifies the generic function to be usedfor evaluation of subcomponents (exp and stat ). Its generic4 parameters denotethe function type to be going from ToyLang to PpLang .

The method for function application (@) simply creates the pretty-print elementwhile supplying the results of recursively evaluating the subcomponents (exp andstat ).

The client code for performing a full interpretation is:

source: ToyLang;prettyFunctions: PpFunctions;prettyPrint: GenFunc[ToyLang, PpLang];...!!prettyFunctions.init;!!prettyPrint.make (prettyFunctions);

(prettyPrint @ source).display;...

Prior to its usage, a function package must be initialized by calling init . Then,a generic function (prettyPrint ) is created by suppling a pretty-print functionpackage (prettyFunctions ). Next, the generic function is applied to the sourcestructure, yielding a target structure. The semantics are finally produced by invok-ing display on the target structure.

A concrete function package appears as follows:

class PpFunctionsinherit Functions[ToyLang, PpLang]

4This time generic means (static) parametric polymorphism, whereas we imply (dynamic) inclu-sion polymorphism in case of generic functions.

Page 234: A Functional Pattern System for Object-Oriented Design

216 12 Translator

creation initfeature

init islocal

pfVar: PfVar;pfAssign: PfAssign;pfIfthen: PfIfthen;prettyPrint: GenFunc[ToyLang, PpLang]

domake(3);!!prettyPrint.make(Current);!!pfVar;!!pfAssign.make(prettyPrint);!!pfIfthen.make(prettyPrint);put(pfVar, "ToyVar");put(pfAssign, "ToyAssign");put(pfIfthen, "ToyIfthen")

end;end

Each concrete package inherits from an abstract function package class which, inturn, inherits from Hash Table :

deferredclass Functions[Source, Target]inherit Hash_Table[Function[Source, Target], String]feature

init is deferred end;end

So, make(3) initializes the function package to a hash table with three entries.Next, a generic function is created in order to serve as the creation argument forthe three specialized function prototypes. Note that the Current argument in thecreation of the generic function causes the very function package that is currentlybeing initialized to become the argument for the generic function that is suppliedto the specialized functions. The function to print variables (pfVar ) does not needto recursively evaluate subcomponents, ergo it does not require a generic functionfor its creation. Finally, the specialized function prototypes are put into the hashtable using their corresponding source element class names as keys.

Therefore, the application method of the generic function definition —

class GenFunc[Source, Target]inherit Function[Source, Target];

Internal;creation make

Page 235: A Functional Pattern System for Object-Oriented Design

12.10 Related Patterns 217

featurefunctions: Functions [Source, Target];

make (fs: like functions) isdo

functions:=fsend;

infix "@" (source: Source): Target isdo

Result:=clone(functions.item(class_name(source)))@ source

end;end

— can simply access the class name of the source element (method class nameis inherited from the system class Internal ), use it to retrieve the correct specializedfunction prototype (call item on the function package)5, and then apply a clonedexemplar to its own argument. Instead of a hash table we also could have used adictionary or even a type case switching statement in order to achieve dispatchingon the type of arguments.

12.10 Related Patterns

12.10.1 Categorization

Function Object: Translator’s interface to clients is that of a function object.

Interpreter: Interpreter suggests inventing and representing small languages for re-occurring problems [Gamma et al.94]. Translator and Interpreter do not ad-dress parsing, i.e., already presume the existence of an abstract syntax rep-resentation. Translator is well-suited defining the interpretation part of In-terpreter which defines interpretations in member methods of elements (seediscussion in section 12.2 on page 201) or by using Visitor.

Visitor: Visitor [Gamma et al.94] has similar motivations as Translator. Yet, besidesthe fact that Visitor does not cover homomorphic and incremental transla-tions, it also uses a different means of achieving double-dispatch. Visitor re-lies on the straightforward technique of encoding an argument’s type intomethod names [Ingalls86]. However, several disadvantages are aligned withthis approach:

5At this point a runtime error may occur due to a missing specialized function. Some exceptionhandling or other kinds of gracefully dealing with such a situation would be appropriate.

Page 236: A Functional Pattern System for Object-Oriented Design

218 12 Translator

• A mutual dependency cycle is introduced between elements (abstractsyntax tree) and visitors (interpretations) [Martin97]. This has implica-tions on recompilation and regression tests.

• The elements are forced to know about interpretations because of theneed to provide an Accept method.

• One has to provide code for interpreting all elements of a hierarchy, al-though only a subset will actually be considered by certain interpreta-tions [Martin97].

Facet: Like Translator this pattern aims at supporting the addition of new and un-foreseen interfaces to existing classes without impacting clients that do not re-quire the new interfaces [Gamma97]. Thus, both patterns preserve the initialkey abstraction, i.e., allow element interfaces with intrinsic properties only.Also, both patterns allow for dynamic extensions of classes. Facet differs inthat it mainly aims at role modeling and does not take translations or incre-mentality into account.

External polymorphism: This pattern provides an alternative way (cleverly exploit-ing C++ templates) to achieve polymorphism with classes which have nocommon ancestor [Cleeland et al.96].

Acyclic Visitor: The dependency cycle in the original Visitor [Gamma et al.94] de-sign caused many suggestions for improvements such as Acyclic Visi-tor [Martin97] and Dynamic Visitor [Nordberg96]. Both alternatives also ad-dress the issue of partial visitations, i.e., when a particular interpretation doesnot need to be defined on all elements. Translator may deal with such a situ-ation as well, since there is no obligation to provide a complete set of special-ized functions.

Serializer: Because Serializer [Riehle et al.97] is a specialization of Visitor, it is alsorelated to Translator. One can think of Serializer as translating objects to aflattened form (e.g., for persistence). In fact, Translator might be more ap-propriate in some cases since it does not require objects to know about theirability to be serializable.

12.10.2 Collaboration

Transfold: The exploration of the source structure may be deferred to Transfold.

Composite: Translator can be used for interpretations on Composite struc-tures [Gamma et al.94].

Observer: On may use Translator to create a view presented with the Ob-server [Gamma et al.94] pattern.

Flyweight: Leaves of abstract syntax trees can be represented as Fly-weights [Gamma et al.94].

Page 237: A Functional Pattern System for Object-Oriented Design

12.10 Related Patterns 219

Void Value: A void function, i.e., a void value defining a default function, may beused to define the behavior in case a generic function can not determine anappropriate special function.

12.10.3 Implementation

Function Object: The specialized functions that map source elements to target ele-ments are function objects [Kuhne97].

The result produced by Translator may be a hierarchy of function objects, i.e.,a parameterized result. For instance, a pretty print result could be a func-tion (itself calling other functions) awaiting the indentation level of the cur-rent subtree. Such a structure corresponds to translucent procedures [Rozas93].Such a function structure is able to evaluate, but also subject to examinationand decomposition into individual functions again. Consequently, a transla-tor may also interpret such a translucent function structure.

Generic Function Object: The generic functions capable of realizing double- andmulti-dispatching interpretations on heterogeneous data structures aregeneric function objects [Kuhne97].

Lazy Object: Translator may employ Lazy Object to implement lazy interpretations,i.e., both intermediate structure and result may be lazy objects.

Prototype: Function packages contain function prototypes [Gamma et al.94]. Fur-ther instances are created by cloning and the associated generic function is apreset attribute value.

Singleton: Instead of using an attribute genFunc (see section 12.9 on page 214)in specialized functions, these may alternatively access a Singletonclass [Gamma et al.94] in order to retrieve their corresponding generic func-tion. EIFFEL allows for a particularly easy Singleton implementation, usingthe “once” mechanism.

Observer: A chain of Observers [Gamma et al.94] can be employed to account forthe data dependency between source structure, target structure, and targetsemantics.

Page 238: A Functional Pattern System for Object-Oriented Design

220 12 Translator

Page 239: A Functional Pattern System for Object-Oriented Design

221

13 Collaboration

Learn the patterns, and then forget ’em.– Charlie Parker

The previous chapters introduced six functional patterns. Each is of value on itsown. A language, design toolkit, pattern system, or paradigm is of special value,however, if the elements are orthogonal to each other and allow mutual combina-tion to the extent of being a generative1 system.

“A pattern system for software architecture is a collection of patterns forsoftware architecture, together with guidelines for their implementation, com-bination and practical use in software development [Buschmann et al.96].”

– PoSA Group

The presented functional pattern system exhibits a remarkable amount of in-terconnection and collaborations (e.g., compared against the 23 GOF pat-terns [Gamma et al.94]). Figure 13.4 on page 226 shows the full relationshipgraph for the pattern system, but it is more instructive to view partial diagramsfirst. According to our separation of the Related Patterns section, we organizepattern relationships into three2 items:

• Categorization. Which pattern “is-a” kind of another pattern?This relationship reveals what other pattern roles a pattern may adopt in aparticular use.

• Implementation. Which patterns do implement other patterns?Which patterns are useful to realize the inner mechanics of another pattern?

• Collaboration. Which patterns can collaborate with each other?This is a symmetric relationship where individual pattern strengths are con-nected like pieces in a jigsaw puzzle.

The following diagrams and explanations collects and concentrates pattern re-lationships to create new perspectives. For a full understanding and more examplerelationships, it may be required to (re-) consult the individual Related Patterns sec-tions.

1Pattern languages are also called “generative”. They allow generating a product by followingcourse patterns to finer grained patters through the language. Here, we mean bottom-up generationfrom composable components.

2Usually, the term “collaboration” subsumes both Implementation and Collaboration items.

Page 240: A Functional Pattern System for Object-Oriented Design

222 13 Collaboration

Value Object

Lazy Object

Void ValueFunction Object

Transfold Translator

InterfaceInterface

Prototype Identiy Object

Lazy ResultSuspension

Figure 13.1: Pattern categorization

Figure 13.1 illustratesthe pattern “is-a” hier-archy in OMT notation.Transfold and Translatorare both used through aFunction Object interfaceby clients. Clients applytransfolds and genericfunctions to structuresrespectively. A FunctionObject, which still awaitsarguments, represents asuspended calculationand, therefore, “is-a”Lazy Object. A FunctionObject also behaves likea Value Object, in thatit never alters its state(except Procedure Ob-ject, see section 7.10 onpage 107), but returns afreshly created instance,akin to a Prototype [Gamma et al.94].

Pattern Void Value is a Value Object, because it has an immutable interface,might provide generator methods (see chapter 9 on page 149), and shares theunique instance (Identity Object) property with Value Object.

Finally, the result of a Value Object generator method can be a lazy Value Object.Note that figure 13.1 does not include — to provide a clearer presentation — thatevery pattern except Lazy Object “is-a” Void Value: Function Object and its descen-dents may be default functions for a parameterizable component, hence, playingthe role of a Void Value. Even Value Object may be a Void Value, by defining adefault value for a value type. Note, that this does not introduce a cycle into thehierarchy, since a new Default Value “class/role” would inherit from both ValueObject and Void Value3.

The “is-a” hierarchy of figure 13.1 is helpful in understanding the individualpatterns and also allows keeping individual pattern descriptions shorter. For in-stance, pattern Void Value “is-a” Value Object, hence, the discussion about Singu-lar Object and Identity Object in pattern Value Object may also be applied to VoidValue. Or, using transitivity of the “is-a” relationship, a Transfold could be used asa Lazy Object calculation in a Function Object callback situation.

Figure 13.2 on the facing page depicts how patterns support each other in termsof implementing a pattern’s functionality. Transfold uses Function Object for struc-

3It is interesting, though, how multiple inheritance can establish a symmetric “is-a” relationshipbetween two inherited classes.

Page 241: A Functional Pattern System for Object-Oriented Design

223

Lazy Object

Function Object

Value Object

Transfold

Translator

value cache

local functions

streaminterm

ediate

generic,

Void Valueco

py, e

qual

ity

stre

am e

nd

inte

rpre

tatio

nla

zy

explore, transpose, fold

suspensionexploration end

views

Figure 13.2: Pattern implementation relations

ture exploration, stream transposition, and folding. Note that the association labelsin figure 13.2 are role names [Rumbaugh91]. They denote the service a patternprovides for the one that originates the association.

Also note that the responsibilities of Transfold could have been implementedby using other techniques, but Function Object exactly provides the properties andservices needed for the tasks. Function Object, furthermore, provides the localtransformation functions and the concept of a generic function to Translator, andimplements stream suspensions for Lazy Object.

Void Value can help both Transfold and Lazy Object to define the end of an ex-ploration or a stream respectively. Transfold, in turn, may support Value Object indefining copy and equality operations by iterating over value components. A com-plex Value Object may use a Translator to implement sophisticated interpretationson it. Value Object again, is used by Lazy Object to cache stream values. Lazy Ob-ject itself, provides lazy intermediate streams for Transfold and enables Translatorto implement lazy interpretations.

The diagram in figure 13.2 is useful for designers who want to apply a patternand look for supporting patterns to ease its realization (see the individual RelatedPatterns sections for possible non-functional pattern support). It should be clearthat even if a pattern is supported by the language, it may still use another imple-mentation pattern. For instance, even built-in lazy streams could still rely on VoidValue to define the behavior of a stream at its end. We do not count it as an collabo-ration, though, since making Lazy Object’s realization easier with Void Value doesnot benefit any client.

Page 242: A Functional Pattern System for Object-Oriented Design

224 13 Collaboration

It is the desirable property of a collaboration — in the the sense as we use theterm here — that it combines the strengths of two patterns to the benefit of clients,using the resulting product.

Value Object Translatorinterprets

Transfold Void Value

Function Object Lazy Object

interates

expl

ore

keyw

ord,

take

s-a

equa

lity

func

tionsafe param

eter

interates

inte

rpre

ts

prod

uces

cons

umes

,

initializestream functions

interprets, produces

Figure 13.3: Pattern collaborations

Figure 13.3 illustrates possible pattern collaborations. This time, the labels areplaced in the center of an association because they name a symmetric relationship.The arrows indicate the direction of reading the association, e.g., Function Objectmay collaborate with Value Object in order to avoid parameters changing theirvalues, after having been passed to a Function Object.

Value Object, in turn, may offer clients to change its equality checking functionwith a Function Object (see the hierarchy in figure 13.1 on page 222 to realize thatthe passed function could be a Transfold). Value Object and Void Value can bothhelp to define structures that are interpreted by Translator.

Translator itself, may employ Transfold to defer the structure exploration pro-cess and both interprets and produces Lazy Objects. As a special case of that, Trans-lator may interpret Function Object structures and produce parameterized Func-tion Object results. Pattern Transfold also consumes and produces Lazy Objects,while Lazy Object may collaborate with Function Object to offer clients functionalparameterization of stream functions. Clients similarly benefit from Transfold’s pa-rameterization with Function Object, and in particular also from Keyword Param-eter Function Object that facilitates managing Transfold’s parameters. As Transla-tor does, Transfold also iterates over structure containing Value Objects and VoidValues. Finally, both Value Object and Void Value may use Lazy Object to lazilyinitialize components.

Page 243: A Functional Pattern System for Object-Oriented Design

225

Again, it might be worthwhile to consult the hierarchy of figure 13.1 onpage 222, e.g., to see that both Void Value and Function Object are also safe pa-rameters to Function Object, since they are a kind of Value Object.

A collaboration diagram, like figure 13.3 on the facing page is a useful map forcreating a design with interacting and each other mutually enhancing patterns.

The high cohesion of the presented functional pattern system can be best seenin a diagram including all three covered types of relationships.

Figure 13.4 on the next page features three arrow types, corresponding to theassociations of the three former diagrams. An arrow pointing from pattern X topattern Y should be interpreted — in the order of the arrows in the legend of fig-ure 13.4 on the following page — as

1. X uses Y in its implementation.

2. X uses Y in a collaboration.

3. X plays the role and/or has the properties of Y.

Admittedly, the diagram is quite complex, but apart from providing a single-diagram overview, it also allows some interesting observations:

• All patterns have about the same number of edges, indicating high overallcohesion, but

• Function Object has the highest number of in-going edges, i.e., is heavilyused. It is a kind of three other patterns, demonstrating its versatility. Theremaining patterns are kinds of Function Object. All but Void Value use Func-tion Object, giving evidence to the ubiquitous presence of this key pattern.

• Void Value has almost only in-going edges (except one classification edge toValue Object and a collaboration with Lazy Object). This underlines its na-ture as a base case definition and suggests its realization as a basic, atomiclanguage feature.

• Although, Value Object could be suspected to be as primitive support as VoidValue, it is interesting how balanced its in and outgoing count is, that is, howwell it also benefits from other patterns.

• Lazy Object shares the highest number of collaboration uses with FunctionObject, i.e., composes well with other patterns to yield improved client ser-vices.

• Transfold exhibits more usage of other patterns than it is used itself. Thisis consistent with its higher-level conception and intended usage in librariesand client code.

• Translator has only one ingoing edge (denoting usage by Value Object forinterpretations). This confirms its characterization of a high-level, client ori-ented service.

Page 244: A Functional Pattern System for Object-Oriented Design

226 13 Collaboration

Translator

FunctionObject

ValueObject

LazyObject

VoidValue

Transfold

collaboration use role view (is-a)implementation role

expl

ore

initialization

(keyword) takes-a

itera

tes

interpres, produces

interprets

equa

lity

test

suspension

lazy interpretation

intermediate stream

views

explore, transpose, fold

defa

ult t

rans

fold

default translator

default value

immutable interface

is-aunevaluated function

lazy

res

ult

generic, local functions

default function

value cache

expl

orat

ion

end

interface

stream functions

copy, equality

iterates

consumes, produces

initi

aliz

atio

n

interprets, produces

interface

interprets

safe parameter

stream end

Figure 13.4: Pattern system relations

As a result, it comes to no surprise that Function Object and Lazy Object vividlyclaim language support due to their importance to other patterns. Pattern Trans-fold seems to be reasonably implementable as a principle of library and iterationorganization (see section 14.4 on page 240 for arguments to not just leave it to that).

Page 245: A Functional Pattern System for Object-Oriented Design

13.1 Example collaboration 227

However, it is interesting to see to how well Value Object — being of good useto other patterns — can benefit from the existence of other patterns. Hence, therespective patterns, maybe surprisingly, contribute to a “low-level” property likevalue semantics.

Figure 13.4 on the facing page does not claim completeness. First, some rela-tionships, e.g., Function Object “takes-a” Lazy Object, have been left out since theyseem not important enough. Second, there might be yet unexplored interactionsthat still wait to be discovered.

13.1 Example collaboration

This section demonstrates all patterns in a single example. A well-known, non-trivial problem consists of determining whether two trees contain the same ele-ments with respect to an in-order traversal. The challenge is to stop searching whena difference is found, i.e., to avoid unnecessary exploration of subtrees. This task isknown as the samefringe problem [Hewitt77] and has been used to both, argue in fa-vor of lazy lists [Friedman & Wise76], and also to demonstrate the inferior iterationmechanisms available with C++ [Baker93]4.

In this example (see figure 13.5 on page 229) we are comparing machine codeconsisting of instructions like load-data (LD data), load-address (LA address), etc.The machine code is generated by interpreting an abstract syntax tree, whose nodesare represented in conformity with the Void Value pattern. The interpretationto machine code is defined with the Translator pattern. Translator employs theGeneric Function Object pattern for its local transformations. The result of the ab-stract syntax tree interpretation is a tree of machine code instruction nodes, whichis flattened5 to a lazy stream of instructions using the Lazy Object pattern. LazyObject generates a lazy stream of instructions using the Value Object pattern tocache already generated results. Finally, two differently generated streams are com-pared using the Transfold pattern. Transfold itself, is parameterized according tothe Function Object pattern.

In addition to be a readily accessible design, the configuration depicted in fig-ure 13.5 on page 229 has several benefits listed in table 13.1 on the next page.

Note that Function Object alone would have sufficed to solve the samefringeproblem [Baker93, Laufer95], by defining two lazy functions to generate and com-pare two streams. Still, pattern Lazy Object helps to better understand the delay-ing aspect of Function Object and makes the stream concept explicit. Transfoldgeneralizes the parallel consumption of two streams to an iteration framework andTranslator allows the two tree arguments to be the result of complex interpreta-tions. Void Value and Value Object help to simplify and optimize the underlyingdata structures.

Ergo, by no means all patterns are required to solve the problem, but each con-tributes to the accessibility and extendibility of the presented design.

4This article did not consider the use of Function Object and, hence, did not tell the whole story.5Using a post-order traversal rather than the usual in-order traversal.

Page 246: A Functional Pattern System for Object-Oriented Design

228 13 Collaboration

Property Description Contributor

Case-lessprogramming

There is no need for a case analysis of treeleaves. Any related behavior, especially thatof non-existent children, is distributed to theleaves.

Void Value

Non-intrusivetraversal

Abstract syntax trees do not need to pro-vide support for their interpretation. Multi-dispatching operations can be implementedwithout affecting the source structure.

GenericFunction

Object

Hierarchical,incrementalinterpretations

Interpretations can be defined in terms of lo-cal transformations and support incrementalupdating.

Translator

Demand-drivencomputations

Flattening a tree causes its traversal only to acertain extend, depending on the amount offlattened data consumed. Tree contents canbe accessed through a defined and efficientinterface.

Lazy Object

Safe caching Stream elements are eventually representedas immutable values who can be safely usedfor caching computed results.

Value Object

Internaliteration

Multiple streams can be processed in paral-lel without the need to explicitely code con-trol structures. A powerful, general schemecan be parameterized to implement numer-ous useful operations.

Transfold

Parameterizedfunctionality

True closures allow parameterizing generalmechanisms in a black-box, plug-in fashion.Functions may be composed and exchangedat runtime.

FunctionObject

Table 13.1: Benefits of the functional pattern design example

Page 247: A Functional Pattern System for Object-Oriented Design

13.1 Example collaboration 229

LD "b"LA "a"

LD "b"

=?

LD "b"LD "a"

Add LA "c"

:=

c a+b

Void Value Function ObjectGeneric

LD "a"

Move

LD "b"

Lazy Object

Fu

nct

ion

Ob

ject

Transfold

Value Object

MoveTranslator

LD "b" Add

LD "d"

LA "a"

LD "d"

Add

Add

LD "c"

Figure 13.5: Functional pattern decomposition of “Samefringe”

Page 248: A Functional Pattern System for Object-Oriented Design

230 13 Collaboration

Page 249: A Functional Pattern System for Object-Oriented Design

231

Part III

Language design

Page 250: A Functional Pattern System for Object-Oriented Design

232

Page 251: A Functional Pattern System for Object-Oriented Design

233

14 Pattern Driven Language Design

Die Grenzen meiner Sprache sind die Grenzen meiner Welt.– Ludwig Wittgenstein

14.1 Introduction

The functional pattern system presented in the previous part makes atwofold contribution to language design. First, implementing patternsthat represent flexibility and reuse promoting designs, puts an imple-

mentation language to the test. Any encountered problems are likely to occur inother attempts to produce flexible designs as well. Second, most patterns claim tobe supported by the implementation language directly instead of being coding con-ventions. How are the suggested language concepts interrelated and what type ofindividual support should be provided? As the emphasis of this book is the func-tional pattern system itself, the intent of the following observations is to providean outlook to further developments, i.e., the aim is to identify impulses rather thancomplete solutions.

This part is organized as sections of patterns, each commenting on both abovementioned aspects. It concludes with an overall reflection and suggests to reevalu-ate the roles of languages and their associated environments.

14.2 Function Object

Undeniably, function objects enrich object-oriented programming with many use-ful properties. Function objects hide the number of both parameters and results toclients. This can be viewed as an aid to modularization, just like classes in object-oriented design [Parnas72] or higher-order functions and lazy evaluation in func-tional programming [Hughes87]. Accordingly, aggregation (“has-a”), inheritance(“is-a”), and behavior parameterization (“takes-a”) should be equally well-knownto designers. “Takes-a” realizes object-composition, as opposed to breaking encap-sulation with inheritance [Snyder86]. It is therefore a means of reaching the goalof component oriented software [Jazayeri95, Nierstrasz & Meijler95]. In combina-tion, inheritance and Function Object allow for flexible prototyping as well as safeblack-box composition. Indeed, function objects reintroduce some flavor of struc-tured analysis and design to object-orientation. This is definitely useful. While

Page 252: A Functional Pattern System for Object-Oriented Design

234 14 Pattern Driven Language Design

adding new objects to a system is caught by an object-oriented decomposition,adding functionality often is better handled by extending functional abstractions.The control-objects in Jacobson’s “use-case driven approach” [Jacobson et al.94] rep-resent such points of functional extendibility.

Obviously it is a tedious and superfluous activity to code a set of classes tosupport partial parameterization for function objects (see section 7.4 on page 98).While it is straightforward to support partial parameterization by a tool that —given just the final function object definition and a specification of all parametertypes — generates all necessary classes, it is quite clear that language support isdesirable. Language support, in general, should take care of

• a short syntax for function application and composition,

• implicit function object creation,

• garbage collection,

• support for partial parameterization,

• a means to define anonymous function objects, and

• a type system that allows specifying type constraints.

The following paragraphs expand on the above items. Clearly, a short syntax forfunction application dramatically increases code readability. There is no doubt that

(streamAdd @ hammings @ primes).show(9); -- Eiffel(streamAdd(hammings)(primes)).show(9); // C++

is superior to

streamAdd.apply(hammings).apply(primes).show(9); // Java

Neither should it be possible to forget the creation of functions objects norshould it be necessary to make an idiomatic use of a language feature, which wasmeant for a different purpose (see section 7.9 on page 105). Implicit creation offunction objects is a natural consequence with language support for Void Value(see section 14.7 on page 248).

Many arguments against closures are made on the grounds of their unprede-termined lifetime which requires heap rather than stack allocation. The usefulnessof closures partly stems from this property (e.g., making it possible to use themfor decoupling implementation from invocation and suspending calculations) andobject-oriented languages heavily use heap allocation for objects anyway. With-out a garbage collection support, implementation of the function object patternis still possible but more involved [Laufer95]. The SELF programming languagefeatures two different types of blocks (closures) with a different scope of life-time [Chambers92a]. This allows avoiding overhead for stack allocatable closures(downward-funargs). Certainly, one closure type only in a language is preferable

Page 253: A Functional Pattern System for Object-Oriented Design

14.2 Function Object 235

and the task to replace appropriate occurrences with a stack allocation based ver-sion should be left to the compiler.

Although PIZZA supports higher-order functions, partial parameterization isnot automatically supported [Odersky & Wadler97]. Chapter 7 on page 93, how-ever, clearly demonstrated the benefits of partial parameterization support forfunction objects. This decoupling and reusability promoting feature should beavailable for free without hand coding classes or resorting to curry-functionals.In an investigation about the impact of language features on reusability, one ofthe criteria used for evaluation was support for adapting components to theiruse-contexts [Biddle & Tempero96]. The ability to supply arguments to a function(component) in advance makes it possible to adapt it to its calling environment(context). Especially the useful combination of keyword parameters and partialparameterization should receive language support.

Often, for instance, for iteration purposes, only a short function needs to bepassed which is more conveniently done by passing an anonymous function ratherthan defining a class and then passing an instance of that class. SMALLTALK’sblocks are used for such purposes and also BETA allows passing pattern defini-tions, which are implicitly instantiated [Madsen et al.93]. Surprisingly, support forimplicit binding of free variables [Breuel88] is not the driving force for languagesupport. On the contrary, it has been shown that explicit passing of parametersbetter supports reuse of function objects (see section 7.9 on page 105). Even withanonymous function objects, changing a free variable name in the function objectcontext will imply only one change in parameter passing, as supposed to a possi-bly more involved change to all occurrences of the free variable in the anonymousfunction definition.

The possibility of using function objects for method simplifications, or for im-perative commands that support undoing (see section 7.4), or the idea to exploit in-heritance between functions [Schmitz92] all suggest language support in the spiritof patterns in the BETA language [Madsen et al.93]. A pattern can be used as a func-tion or a class definition and, thus, nicely combines both concepts. Although thisholistic approach is very useful and aesthetically pleasing, it does not help to re-solve the fundamental dichotomy between a functional and an object-oriented de-composition approach (see chapters 4 on page 55 and 12 on page 201). Like physi-cians have to eventually settle on a particle or wave interpretation of elementaryparticles in a given experiment, the software designer has to use a BETA pattern asan object or a function. If choosing objects one looses functional extendibility andif choosing functions one looses data extendibility. Research has been carried outfor finding guidelines as to which from a multitude of patterns is appropriate inspecific design situations [Coplien95]. Ideally however, no commitment would benecessary and either viewpoint would be possible in order to draw from object andfunction benefits while never be forced to suffer drawbacks. Fortunately, researchstimulated by pattern Void Value [Kuhne96b] leads to an approach based on so-called tiles, which can be assembled to represent functions or objects depending onthe most beneficial interpretation (see section 14.8 on page 252).

Language support for Function Object links to Void Value, Lazy Object and

Page 254: A Functional Pattern System for Object-Oriented Design

236 14 Pattern Driven Language Design

Value Object. First, automatic initialization of function objects could be elegantlyachieved by defining void function objects which return the desired result, i.e., typ-ically a function object instance awaiting further arguments.

Second, a lazily evaluated function could be passed as an argument withoutdemanding clients that they pass a dummy argument in order to evaluate the func-tion when in need of the result. For functions returning a structure that has to beaccessed (e.g., a stream) the difference does not matter (because access will trig-ger the evaluation only) but functions returning unstructured values like integersrequire language support for lazy evaluation to calculate their values lazily (seesection 8.8 on page 122).

Third, a type system supporting the notion of value objects would allow pass-ing a function object producing an integer to a client that expects a function objectproducing a numeric value. An EIFFEL compiler has to reject such a scenario sincethere is no way to tell it that, although generally invalid, in this case the use of amore specific actual generic argument is safe. If it were possible to specify that theresult type of the passed function can not be written to (i.e., is a value), the com-piler would be able to accept the more specific argument (see also sections 9.8 onpage 155 and 14.5 on page 243). In other words, covariant output types are soundwith regard to subtyping and there should be a means to tell the compiler when ageneric parameter occurs in an output position.

Any language aiming at supporting function objects must be equipped with asufficiently mature type system. That is why, the JAVA language fails to supportfunction objects properly [Kuhne97]. Although the EIFFEL languages provides thenecessary support for genericity, its type system is not completely satisfactory withregard to function objects. For instance the identity function type-wise transformsany type into exactly the same type, i.e., inherits from the abstract Function class,constraining both input and output type to be the same. With EIFFEL we, neverthe-less, have to equip class Identity with a type parameter, since there is no other wayto constrain the input and output type of class Function to be the same.

Indeed, the lack to internally introduce universally quantified type variableshas practical consequences: The natural place for a function composition methodwould be class Function since two functions are combined to produce a new func-tion. The function composition operator ◦,

f ◦ g = λx→ f (g x), has type◦ :: (b→ c)→ (a→ b)→ a→ c,

i.e., the second function’s result type (b) matches the first function’s argument type,and the resulting composition function goes from the argument type of the secondfunction (a) to the result type of the first function (c). Hence, we would like to codethe method using EIFFEL in class Function as follows:

class Function[B, C]...infix "|o|" (f : Function[A, B]) :

ComposeFunction[A, B, C] is ...

Page 255: A Functional Pattern System for Object-Oriented Design

14.2 Function Object 237

Unfortunately, this is not possible since the generic type A is not known withinFunction and there is no other way to introduce it than by adding it to the genericclass parameters. This, however, is not feasible because it would create the neces-sity to declare all functions with three types, regardless whether they are used forfunction composition or not. Inevitably, this means that function composition cannot be implemented as a method but must be modeled as a function object itself.

One approach to solve the above problem would be to allow the declarationof universally quantified type variables for individual methods, akin to C++’s tem-plate methods. Indeed, C++ does allow function composition to be implemented asa method and functional programming languages also successfully use parametricpolymorphism to statically type function composition and similar cases. Similarly,in contrast to EIFFEL’s explicit treatment of genericity, a C++ template implemen-tation for Fold would not force the user to multiply declare a fold function objectfor each different type instantiation as in

fold1 : Fold[Book, Integer]; -- count booksfold2 : Fold[Book, List[String]]; -- produce titlelist

The result type of a fold function varies with the type of the initial argument forfolding — effectively a new function type is created by each fold usage — but thetype could be inferred from the initial argument without requiring the user to ex-plicitly declare it up-front. Thus, a single fold declaration (just stating to fold overbooks) could be used for both applications above.

Another approach for greater expressiveness in the type system is to interpretthe result type of function composition to be dependent on the argument type offunction composition. In fact, by means of its covariant method redefinition sup-porting type system, EIFFEL from the start enabled to express the dependency ofargument types on receiver types. For instance,

is_equal (other: like Current): Boolean is ...

specifies that the argument type (of other ) varies with the receiver type. Amongthe innovations of EIFFEL, version three, was the possibility to express result typesin terms of argument types. For instance,

clone (other: General): like other is ...

anchors the result type of clone to its argument type. This feature, called “expres-sion conformance” [Meyer92], allows the declaration of clone in class Any1 onceand for all, while allowing statements like

local a, b : Book;...

a:=clone(b);

1Class General to be precise.

Page 256: A Functional Pattern System for Object-Oriented Design

238 14 Pattern Driven Language Design

In the above code the compiler can type-check the assignment since it does not relyon a static anchor to General but can assume the result type of clone to be same asthe type of expression b. Unfortunately, it is not possible to use “component types”for expression conformance, e.g.,

apply(a : Any; f : Function[like a, R]) : like R is ...

does not work although anchoring the argument type of function argument fworks, anchoring the result type of apply to the result type of the function argu-ment (R) does not. Again, the generic variable R is not known, although, by meansof expression conformance, could be taken from the actual argument to apply .

The next logical evolutionary step in enhancing EIFFEL’s type system, hence,would be to support the specification of dependent types including access to“component types” such as generic arguments or even attributes. The latteroption would lead to a powerful type system which coincidentally would pro-vide a satisfying solution for the covariant method redefinition problem in EIF-FEL [Shang94, Shang95a]2.

14.3 Translator

The most pressing issue with regard to language support for Translator is the pos-sibility of generic functions failing to find an appropriate specialized function atruntime. Since multi-dispatch is only emulated by using the Generic FunctionObject pattern, without further support there is no way that type safe applicationof generic functions can be ensured statically. Although the implementation pre-sented (see section 12.9 on page 214) avoids type-switch statements which requiremaintenence and even allows the addition of specialized functions at runtime, theintroduction of a new type in the generic function domain will cause a runtimeerror if a generic function encounters an instance of it.

In fact, historically multi-dispatch supporting languages have not beenstatically typed either [Bobrow et al.86b, Shalit et al.92, Chambers92b] al-though early work on combining multi-dispatch with static typing ex-ists [Agrawal et al.91, Mugridge et al.91]. Nevertheless, static type checking ofmulti-methods is highly desirable and possible solutions have been presented inthe meantime [Chambers & Leavens95].

Another common counterargument towards multi-methods is that they breakencapsulation by accessing internals of multiple abstractions. Especially the in-carnation of multi-methods in CLOS with its unprotected data abstractions3 andfreely addable functions exemplifies the softening of an object-centered, encapsu-lation promoting, single-dispatch approach. The CECIL language tries to maintainencapsulation by conceptually assigning one multi-method to all abstractions that

2Unfortunately, the creator of EIFFEL, Bertrand Meyer, can not imagine EIFFEL’s type systemto develop into that direction [Meyer95] and proposed an alternative that simply and, therefore,restrictively bans the coincidental presence of covariance and polymorphism [Meyer96].

3Their is a package mechanism, though.

Page 257: A Functional Pattern System for Object-Oriented Design

14.3 Translator 239

it dispatches on. In other words, encapsulated abstractions are retained and sharemulti-dispatching methods with other abstractions. In addition, however, it seemsonly natural to separate implementation detail access from method lookup. Thefact that the dynamic type of an argument is used for method lookup should notimply opening the encapsulation barrier of the formal argument type. It shouldbe possible, to specify arguments to be contributing to dispatch and/or openingencapsulation, independently from each other. Note that while it might be some-times useful to declare a method in a single-dispatch language to refrain from us-ing any implementation details of its abstraction, i.e., to use public features only,the need for such a change protection is much less needed in single-dispatch lan-guages. Therefore, these, reasonably unify the two aspects.

Language support for generic functions, i.e., functions that dispatch on thedynamic type of their argument(s), would make language support for algebraicdatatypes superfluous. The PIZZA language allows the convenient definition of al-gebraic datatypes and introduces a switch statement for constructor case analysis infunctions [Odersky & Wadler97]. Instead of introducing constructor switch state-ments it seems much more reasonable to code the switch statement as a genericfunction. Datatype constructors can be represented as concrete subclasses to theabstract datatype interface class and generic functions would automatically do thenecessary case analysis. The advantage of this proposed approach over the PIZZAsolution shows, when a new constructor is added to a datatype. The PIZZA so-lutions requires us to change existing code, e.g., the algebraic datatype definitionclass, to add a further constructor and any function containing a switch statement.With the generic function approach we just add another concrete class to the systemand add the corresponding specialized functions for each function on the extendeddatatype. Since code is added only, without the need to change existing code, thegeneric function approach to algebraic data types perfectly supports the open-closedprinciple of software engineering [Meyer88].

The initial motivation for algebraic types in PIZZA was to make operation ex-tension as easy as constructor extension. The latter is supported by object-orienteddesign and, for instance, allows easy addition of language constructs to a languageinterpreter prototype. Distributing interpreter operations (e.g., print and evaluate)over the constructors makes it easy to add constructs but relies on the fact that theoperations do not change and are not extended. If, alternatively, the language is al-ready mature but the aim is to experiment with new and/or alternative operations(e.g., various type checkers), it is more beneficial to regard language constructs asconstructors and define functions on them. As already explained above, genericfunctions cover the second case even better than support for algebraic types. Still,what if the language is immature and the environment experimental? Unequiv-ocally, both above mentioned decomposition strategies are desirable then. Whenchanging an operation one should be able to adopt a functional view and whenchanging a language construct the object-oriented interpretation is more beneficial.Again, a solution to that dilemma is offered by the tiles approach, presented insection 14.8 on page 252.

Page 258: A Functional Pattern System for Object-Oriented Design

240 14 Pattern Driven Language Design

14.4 Transfold

The Transfold approach to iteration holds a number of implications for client soft-ware and library organization. Viewing data structures as maps (with internal iter-ation) rather than indexable structures (with external iteration) increases the levelof abstraction [Pepper92]. If ordered iteration is necessary at all, it is deferred to theinternal iterator. Copy semantics for structures (including iteration results) mayhelp to avoid aliasing. For instance, implementing matrix multiplication with ex-ternal iteration and side effects may not work anymore if the target of the operationhappens to be one of the arguments.

The discussion in chapter 10 on page 163 actually developed a general frame-work for data interpretation, construction, and transformation. The concept ofstructural interpretations producing streams that are consumed by internal iter-ators, was found to extend to issues like creating data from manifest constants,transforming structures into each other, and data persistence.

In fact, assuming language support for all other functional patterns, no furthersupport for Transfold is required. This is not a reason, though, to exclude Trans-fold issues from a language design discussion. The borderline between languagesand library organization and frameworks respectively, has become more and morefuzzy. Especially SMALLTALK makes this evidentially clear. While it takes only ashort time to learn the language, it is a major achievement to aquire expertise on itsrich library. Learning the operations available on collections, for instance, is compa-rable to learning various loop constructs in other, less library oriented, languages.In the future, more extendible languages will further the trend to shift organizationprinciples from languages to libraries.

The uniformity by which Transfold lets us flexibly interpret data structures sug-gests to facilitate the “folding” of structures by a suitable library organization. Fromthis point of view, it seems natural to have an Inductive class very high in the hierar-chy. Data structures of various branching arity (such as linear structures, trees, andgraphs) but also abstractions like grammars, could be accessible via this interface.

The general nature of folding allows expressing many operations (see sec-tion 10.3 on page 173, Versatility) with just one iteration primitive. This is in contrastto EIFFEL’s plethora of iteration features (do all, do if, do while, do until, etc.) in itsiteration classes [Meyer94b] that still can not cover all cases. Although EIFFEL sup-ports only one language loop construct, its library approach to iteration apparentlydoes not allow such a minimalistic solution. While folding is not a computationallycomplete function4 it is sufficiently general to rely on it as the basic iteration prin-ciple. In fact, the combination of creating a structure from a source (unfold) andsubsequent processing (fold) — a so-called hylomorphism — is computationallycomplete [Pardo98b].

Unfold operations are needed when the computation is not driven by thestructure of the argument but by the structure of the expected result. For in-stance, parsing is a tree structured process although the consumed structure is

4Not every computable function is expressible as folding [Kieburtz & Lewis95].

Page 259: A Functional Pattern System for Object-Oriented Design

14.4 Transfold 241

linear only [Kieburtz & Lewis95]. The Translator pattern lends itself to imple-ment unfold operations. Indeed, structure explorations can be represented by un-folds [Pardo98a]. If the resulting streams are processed by folds — as realized inthe Transfold pattern — the resulting operation is a hylomorphism. Automaticprogram transformations even allow elimination of the intermediate list betweenunfold and fold to be constructed at all [Gill et al.93]. Eliminations like this, how-ever, critically rely on regular operations to be used to build and consume lists. Insummary, the above observations suggest to organize libraries according to the al-gebraic properties of their structures and to employ unfold and fold operations fortheir interpretation.

There are a number of reasons as to why it is beneficial to use a fixed set ofprefabricated functional forms rather than encouraging user defined recursion forinterpreting structures. This view emerged from the squiggolist school that aims at aformalism that allows banning user defined recursion in favor of functional formsthat are well controlled and amenable to program transformations5.

Relying on a set of functional forms (e.g., fold) is advantageous in many ways:

• Algorithms using the forms have a concise, readily understandable structureas well as a determined complexity in time and space. Any programmer fa-miliar with the functional forms will understand the algorithm by just lookingat the essential parameterized parts.

• Well-known laws for the functional forms may be exploited to transform pro-grams. For instance,

Result:=fold @ sum @ 0 @ (map @ (times @ 2) @ list);

can be transformed to

Result:=2*(fold @ sum @ 0 @ list);

using a free “fold-fusion” theorem that is derivable from the signature offold [Wadler89]. More laws can be found in [Meijer et al.91].

• The predetermination by the available forms may lead programmers to con-cise, elegant solutions (see section 10.7 on page 176, High-level mind-set).

• Using proven recursion schemes frees the programmer from caring abouttermination, memory demand issues, etc. Many small, albeit disastrous er-rors can be avoided this way (see section 10.2.1 on page 163, mentioning theMariner space-probe accident). The decreased number of structure explo-rations may even make their formal verification feasible. This would improvethe overall reliability of a program considerably, when compared to a scenarioof multiple, distributed external iterators.

5An idea rooted in John Backus’ FP system [Backus78].

Page 260: A Functional Pattern System for Object-Oriented Design

242 14 Pattern Driven Language Design

• Algorithms written as a combination of parameterized combinators canbe easily varied. For instance a tournament-sort combinator taking two re-duction strategies as parameters can express InsertSort [Bird & Wadler88],TreeSort [Floyd64], and ParallelTournamentSort [Stepanov & Kershenbaum86],just by using different combinations of the two reductions opera-tors [Kershenbaum et al.88]. ParallelTournamentSort, actually, has very de-sirably properties, which demonstrates that using standard combinatorsdoes not necessarily imply inefficient solutions but, on the contrary, mayhelp to discover better solutions. Another example supporting this claim isMergeSort. The naıve custom implementation, which divides a list into a leftand a right part, is far inferior to the solution with combinators based onparallel-reduce [Kershenbaum et al.88].

• Since functional forms can be defined on an inductive interface, the additionof a new datatype automatically equips it with the available functional forms.

Fortunately, the iteration framework suggested by Transfold easily allows intro-ducing new iteration schemes or utilize a customized scheme in case, e.g., primitiverecursion may achieve better efficiency than folding.

In general, internal iteration is better suited for parallel execution since, un-like a custom external iteration, the iteration process is guaranteed to be encap-sulated [Baker93]. In particularly, functional forms such as map or parallel-reduceallow parallel evaluation. In fact, there is a tradition in the area of parallel com-putations to employ fixed evaluation forms called algorithmic skeletons [Cole88,Cole89, Darlington et al.91, Boiten et al.93, Grant-Duff94, Darlington et al.95]. In-terestingly, an object-oriented language, enhanced with the functional pattern sys-tem, fulfills all requirements towards a skeleton supporting host language such asimperative, hence, efficient statements, higher-order functions, partial parameter-ization, and polymorphism [Botorog96]. Provided the library implements func-tional forms by exploiting parallel hardware, the algorithms using the forms auto-matically make good use of available parallelism.

An interesting, competitive iteration approach is set out by the SATHER pro-gramming language. Language support for iterators — in the form of a restrictedkind of coroutines — allows defining structure exploration within structures butstill allows flexible, external iteration style, consumption [Murer et al.93b]. Theopen questions are which style (passing functions or coroutine-loops) is moreexpressive and understandable, and whether possible code optimizations by theSATHER compiler justify the requirement for an additional language concept.

With regard to EIFFEL’s merit to support the functional pattern system, againthe need for dependent types or universally quantified type variables occurred.Whereas Transfold in EIFFEL has three generic parameters (see section 10.9 onpage 182), only one is actually required. The other two could be inferred fromtransfold’s arguments.

The highly generic nature of transfold also revealed the lack of a subtyping rela-tionship between the numeric types (e.g., integers should be subtypes of reals) and

Page 261: A Functional Pattern System for Object-Oriented Design

14.5 Value Object 243

the need for a class that specifies that all its descendents conform to class Numericand class Comparable . Using EIFFEL it is not possible to retrofit such a class into thelibrary. Already defined classes like Integer qualify as descendants of that class butcan not (and should not) be modified to inherit from the new class. This experienceprovides an argument for SATHER’s superclass concept that allows specifying whichold classes qualify as descendents for a new class.

In the attempt to write functions that operate polymorphically on numeric val-ues, EIFFEL’s special treatment of arithmetic types was encountered. Specifying theconstant 0 does not work for generic functions that can also be instantiated to beDouble functions because 0 is always interpreted as an integer and is never pro-moted to a real or double value. Anyone trying to integrate complex numbers intothe library also notices that EIFFEL’s balancing rule [Meyer92] gives more privilegesto the language designer than to its user. In the mentioned case I could actually de-fine the function generically by referring to number.zero and declaring numberto be of the generic type. Nevertheless, such experiences question the maturity ofarithmetic type treatment in EIFFEL.

Pattern Transfold strengthens the case for Lazy Object language support. Lazystreams are a prerequisite for efficient data exploration. If methods, in contrast toexplicit function objects, actually perform lazily, the need for a parallel hierarchyof iterators (or explorers to be precise) will be made redundant. With lazy meth-ods, structure exploration can be defined at the best possible location, inside andinternal to structures. Furthermore, functions passed to transfolds should not no-tice any difference when stream contents are lazy. There should not be the needto wrap unstructured values with argumentless functions in order to achieve theirlazy calculation (see 10.7 on page 176, Lazy Functions).

14.5 Value Object

The problems induced by reference semantics are well known. In SMALLTALK thesometimes unwanted and especially unexpected effects of aliasing are said to be abigger source of problems than the lack of a static type system. For instance, thecode

aPoint := button getTopLeft. "get reference location"aPoint y: (aPoint y - 3). "move three pixels up"aToolTip setTopLeft: aPoint. "set toolbar above button"

does not only place aToolTip three pixels above a button , but also, unintention-ally, moves the button [Harris97]. Of course, a more functional version would havebeen in order:

aPoint := button getTopLeft.aToolTip setTopLeft: (aPoint - 0@3).

The latter version works as expected since the minus operator produces a newvalue.

Page 262: A Functional Pattern System for Object-Oriented Design

244 14 Pattern Driven Language Design

Surprisingly, the prevention of aliasing, or advice how to manage systemstate does not seem to be an issue in the major software engineering methodolo-gies. Whenever state is (inadvertently) encapsulated, e.g., by subsystem refactor-izations [Wirfs-Brock et al.90], it is for the sake of decoupling interfaces, ratherthan controlling side effects. The Facade pattern [Gamma et al.94] may alsohelp to control subsystem state but is not advertised as such. Other state pat-terns [Gamma et al.94, Ran95, Dyson & Anderson96] discuss how to implementstate dependent behavior but do not touch the issue of controlling state in object-oriented software.

Several points make aliasing more severe in object-oriented languages:

• A method with two parameters of different types A and B may still sufferaliasing if B is a subtype of A or vice versa.

• The state of an object is not only determined by its internal parts but also bythe state of all objects it references. Object state, hence, is defined by the tran-sitive closure over object connectivity. Therefore, two objects are effectivelyaliased if their transitive state closures have a non-empty intersection.

• Object state, which is permanent as opposed to temporarily allocated proce-dure parameters, allows aliasing to become static rather than dynamic. Stan-dard procedure calls create only temporary aliasing that vanishes after callcompletion. With objects, references can be captured and interference mayhappen much later, with no direct indication of the problem source. Attemptsto control aliasing, in fact, address static aliasing only because its effects aremore severe [Hogg91, Almeida97].

• Ironically, object encapsulation may make it impossible to check for possiblealiasing between a variable and an object component. If the object does notallow access to the component of interest, even the aliasing aware client cannot prevent it [Hogg et al.92].

Clearly, the type system should support the notion of values. This would in-crease the opportunity for polymorphism since there are more subtype relation-ships between values than between mutable objects (see sections 9.8 on page 155,Subtyping, and 14.2 on page 233). Pattern Lazy Object could benefit, for instance,by letting an integer stream qualify as a numeric stream. An integer stream cannot play the role of a mutable numeric stream since non-integer additions are pos-sible. With a value interface to the numeric stream, however, the integer streamis perfectly suitable. Definitely, the type system should allow treating objects andvalues uniformly. JAVA “excels” in demonstrating the worst possible case, wherebasic types must be manually converted to objects and back to values for storingand retrieving them in reference containers, etc. Surely, if basic (value) types aretreated with special care for efficiency considerations then this should take placewithout letting users of the language notice.

Page 263: A Functional Pattern System for Object-Oriented Design

14.5 Value Object 245

Apart from value types other means to control aliasing are available(e.g., “const” declarations) or have been proposed (e.g., unshareable refer-ences [Minsky96]). Value types — as consciously used for modeling in theBETA language [Madsen et al.93] — however, seem to be the most natural con-cept. The difference between values and objects can be argued for philosophi-cally [Eckert & Kempe94] and seems much more natural than, e.g., approachesusing the type system to restrict multiple references [Wadler90]. Efficiency isnot necessarily endangered by the need to copy values occasionally6 as compileroptimizations are possible [Hudak & Bloss85, Schmidt85, Hudak92].

Programming languages should eventually overcome the notion of pointers,even when they disguise as object references [Kuhne96b]. Obviously, the intuitivesemantics for assignment is usually copying and the intention to achieve sharingshould be explicitely expressed as such. By the same token, the default semanticsfor “=” should be value, rather than pointer comparison. Choosing pointer com-parison, EIFFEL, JAVA, etc., force their users to bother about the difference betweenstrings and their references when the only reason why references come into play isthe language designer’s aim to achieve efficient assignment semantics.

The natural consequence for a language featuring immutable values and mu-table objects that aims at safe handling of objects, while allowing sharing whennecessary, seems to be the support of two different assignment statements. Thedefault statement preserves copy semantics and a less often used reference assign-ment achieves sharing. The view that should be adopted on the latter is less refer-ence sharing oriented but rather portrays the notion of establishing a broadcastingrelationship between an (publishing) object and all its (subscribing) references. Infact, SIMULA featured two different assignment statements for values and refer-ences to signal the difference in semantics [Dahl & Nygaard81]. However, sinceboth were applicable to their own domains only, the value of that concept was verylimited. Only the single type Text allowed both statements and, therefore, enableda choice of semantics. For all other types, a syntactic documentation of the seman-tics was achieved but — since redundant — was conceived rather a curse than ablessing. If both operators are applicable to both domains, however, it would bepossible to choose the appropriate semantics and the choice would be clearly docu-mented in program code. Contrast this to, e.g., EIFFEL’s solution of one assignmentstatement whose meaning changes for expanded types. First, the default (sharing)is often non-intuitive and second, the declaration revealing the actual semantics ismost likely not displayed in conjuction with the code in question.

C++ does make a syntactical difference between value and reference semanticsthrough the use of pointers. Unfortunately, values are not polymorphic, i.e., latebinding for method calls is not enabled. This renders the world of C++ valuespretty useless in an object-oriented context. The EIFFEL approach to value types inthe form of expanded types, has been criticized for not providing constructors andalso prohibiting subtyping [Kent & Howse96].

6The most economic scheme seems to be copy by demand, i.e., copy when a shared value ismodified (see figure 9.5 on page 159).

Page 264: A Functional Pattern System for Object-Oriented Design

246 14 Pattern Driven Language Design

14.6 Lazy Object

Theoreticians value lazy semantics for the property of yielding at least the sameresults as eager semantics but possibly more, and for the ability to apply “back-wards” substitutions — e.g., replacement of a constant 1 with if true then1 else f—which is not possible with eager semantics (see section 1.2.4 on page 14). Chapter 8on page 115, furthermore, showed a number of practical benefits resulting fromlazy semantics:

• The reputation of functional programming languages to enable a declarativeprogramming style, may deceptively explained by pointing out the patternmatching style of function definitions. Yet, the latter is, in effect, syntacticsugar only and true declarative programming style is enabled by

– not being forced to be explicit about artificial upper bounds,

– allowing up-front declarations, and

– being able to specify data dependencies only, without worrying for theirresolution (see section 8.4 on page 119).

Even statically circular definitions are possible as long as a dynamic resolu-tion is possible. Specifying dependencies, rather than implementing aggrega-tion relationships, may also help in achieving memory friendly behavior forparallel programming [Kashiwagi & Wise91].

• Data consumption can be separated from data generation. Still, terminationcontrol is available for both producers and consumers. Hence, traditionallyopposing design goals, efficiency and modularity, are effectively reconciledsince partial evaluation is enabled across module boundaries. Moreover,space efficient, interleaved execution of producer-consumer pipelines is au-tomatic.

• Lazy evaluation allows initialization code to be placed at the best possibleposition; the corresponding data abstractions [Auer94]. Additionally, lazyattribute evaluation opens up the possibility of overcoming current limita-tions in dealing with value types. EIFFEL does not allow recursive refer-ences in expanded classes, e.g., a Person referring to another Person as itsspouse, since that implies an infinite initialization sequence and space re-quirement [Meyer92]. Lazy initialization would unfold such potentially in-finite structures only as much as necessary. This is particular interestingfor Value Object and Void Value language support because of potential self-references.

• A system based on streams is determined by the types of modules and theinterconnections (streams) between the modules. Stream processes, suchas filters, may be executed in a threaded fashion thanks to the value se-mantics of streams. The increased modularization achieved by streams

Page 265: A Functional Pattern System for Object-Oriented Design

14.6 Lazy Object 247

can be exploited to distribute development efforts nicely into autonomousgroups [Manolescu97].

Lazy evaluation plays one of the key roles in the Transfold pattern. It enables theseparation between structure exploration and consuming iterators. Most intrigu-ingly, the combination of two functional concepts (higher-order functions and lazyevaluation) solves an as yet unsuccessfully tackled object-oriented design problem:How to have an iteration cake (safety by internal iteration) and eat it too (flexibil-ity through external iteration)? The answer is given by the Transfold pattern. Thispattern shows how lazy exploration streams, actually, use continuations7, withoutexplicitely involving any peculiar continuation passing style.

Typical objections against lazy evaluation are

• memory management overhead, caused by suspensions and

• aggravated debugging, caused by unexpected evaluation orders.

Both arguments seem to have less weight in an object-oriented context, though.First, garbage collection is needed for objects anyway (see section 14.2 on page 233).Second, the complexity of message sends along a typical network of interactingobjects is presumably not any simpler than a lazy resolution of dependencies.

The restriction of laziness to values and functions (see section 4.2.1.1 on page 57)nicely resolves the conflict between destructive updates and lazy evaluation. As aconsequence, value semantics for function parameters has been postulated, though.Otherwise, updates to objects after they have been passed as function argumentswould affect function results (see section 7.9 on page 105, Call-by-value). Researchin the area of functional programming, furthermore, shows the way to even aim atlazy effects (see section 4.2.2.1 on page 59).

Especially extensible languages require some means to defer expression evalu-ation. Lazy semantics would allow programmers, extending the language, defin-ing the evaluation strategy and order for their new language abstractions them-selves. This approach seems more aesthetically pleasing than the usual facilitieslike macros [Winston & Horn84] or meta architectures [Chiba & Masuda93].

With regard to language support for lazy values it was already mentioned thatan emulation with a dummy parameter for pattern Function Object, unit parame-ters in ML, and explicit delay s in SCHEME make the client of such functions awareof their laziness and forces different versions to be written for non-lazy arguments.Moreover, it is not possible to produce unstructured lazy results since these cannot defer their evaluation with access methods, as structured values may do (seesection 14.2 on page 233).

Returning to theoretical considerations made at the beginning of thissection, it is interesting to observe that both objects [Jacobs98] and lazystreams [Jacobs & Rutten97] can be formally explained by coalgebras. Both ob-jects and streams hide inner details and specify destructors only. To the best of myknowledge, however, objects and lazy semantics, as yet, have never been united ina single language.

7Stream elements are continuations to further structure explorations.

Page 266: A Functional Pattern System for Object-Oriented Design

248 14 Pattern Driven Language Design

14.7 Void Value

Following the virtue of making programs robust, i.e., let them react sensibly incase of unusual circumstances, previously simple code may become cluttered withsafety checks (e.g., see code examples 11.1– 11.6 starting on page 192). For this rea-son, the concept of exception handling was introduced. Interestingly, the reasonsthat may cause an exception to be raised in EIFFEL are:

(a) Hardware

1. Hardware signal for user requests, such as hitting a “break” key.

2. Memory exhausted, integer overflow, etc.

(b) Software

1. Programmer generated exception.

2. Assertion violation.

3. Applying an operation to an unattached reference [Meyer92].

Taking into consideration that the first item in category “Hardware” and the firsttwo items in category “Software” represent welcome aids, and that there is effec-tively nothing that can be done against hardware limitations, one is left with justone exception cause: Trying to call a feature on a void reference.

For certain, chapter 11 on page 191 demonstrated a better way to deal with suchsituations by avoiding them in the first place. A remaining discussion is how toavoid void references. There are two dual alternatives:

Void Value Provide a void class for each8 class in the system [Kuhne96b].

Nullcase Provide a nullcase for each function in the system [Sargeant93].

An example of using a nullcase in an UFO [Sargeant93] function definition is:

length: Int isnullcase: 0otherwise: 1 + self.tail.length

How do nullcases compare to pattern Void Value? As a consequence of the en-capsulation breaking properties of pattern matching, function definitions in UFO

are sensitive to the introduction and change of null values. For instance, if a set offunctions is designed to always operate on non-null lists and later it is discoveredthat indeed null lists may also occur, then all function definitions must be changedto include the nullcase branch. In contrast, methods of an object do not need tobe changed, since they operate on a non-void object per definition. The necessaryaddition here consists of adding one VoidClass .

8In analogy to an object root class, there should be a void value root class which is inherited byall void classes. Hence, only deviations actually need to be specified.

Page 267: A Functional Pattern System for Object-Oriented Design

14.7 Void Value 249

Similarly, it may occur that a former null value should be transformed intoa proper object, just as enumeration types are sometimes transformed into classtypes [Sargeant et al.95]. Consider a tree representing terminal leafs as void val-ues. Now, we want to enhance the tree with a display method and therefore addattributes to the leafs, which store the display position9. In fact, the example de-scribes the conversion of Void Value to Composite. A void class is easily convertedinto a standard class, but nullcases will not match object instances and must berepackaged into a class definition.

On the other hand, the introduction of a new function or the change of an ex-isting one is more local with the UFO approach. Nullcase and the regular case areedited at one place (the function definition) whereas one may need to visit bothvoid value and object class, in order to do the change. With both cases at one placeit is also easier to see what the function does as a whole.

Nevertheless, the extraction of the nullcases into one class description producesan entity of its own right:

• The void value class acts as a compact documentation of base case-, default-,and error-properties of a particular type.

• When trying to understand a type, looking at the void class immediately com-municates the set of methods that create exceptions or implement implicitcreation or delegate to other definitions, etc.

• With all null properties concentrated at one place it is easier to check for con-sistency, e.g., to make sure that a terminal node does not answer count withzero, but leaf with true.

• The void value class provides a place for (private) initialization functionsneeded and possibly shared by void behavior methods.

• Changing the void-behavior of a set of functions is regarded as providing newor changed void data, without changing function definitions.

• It is possible to have multiple void values and to dynamically choose betweenthem. With the nullcase approach, each time a new set of functions (that mustrepeat the regular case) must be provided.

An example for the utility of separately defining null- and regular cases is theaddition of numbers (assuming a void number shall behave like zero). The voidvalue method simply returns the argument (the number to be added), regardlesswhether the regular case would have used integer, or complex arithmetics. Voidcomplex values, thus may inherit this method from void integer values.

In turn, one may want to keep the regular behavior of a type, but vary the ex-ception or termination behavior. Folding an empty list may result in an error- ordefault value object, depending on which was given as the initial argument forfolding.

9If we do not want to treat leafs as Flyweights.

Page 268: A Functional Pattern System for Object-Oriented Design

250 14 Pattern Driven Language Design

UFO’s lacking ability to redefine null- and regular cases independently, is likelyto be removed [Sargeant96b]. However, inheriting null- and regular cases sepa-rately, implies the loss of locality of a function definition. As in the case of voidvalues, it will not be possible anymore to look at the full definition at once. Findingthe actual definition becomes harder if redefinition in a hierarchy of definitions isallowed10.

Even if inheriting and overriding of null- and otherwise clauses is possible, stillthe problem of consistently changing the behavior of null values remains. Considera list of books found during a library retrieval. An empty result at the end of anon-empty list displays nothing and provides no actions. If we want to changethat behavior into printing a help message (e.g., suggesting to broaden the scopeof search) we can provide the corresponding action (invoking the help system onclicking) in the same void value class definition. In other words, changes to voidbehavior that affect more than one function are still local.

Note that void values are not restricted to procedural abstraction only. Eitherby providing an is void method or by using RTTI it is possible to check argumentsor attributes for being void. That is, as in UFO, it is possible to use the “voidness”of arguments (i.e., inspect their constructor) in order to do optimizations, such asreturning the receiver of an append message if the argument to be appended isvoid11.

An argument in favor of nullcases is that they do not produce inheritance rela-tionships. In fact, there are good reasons for both approaches. Assuming anythingelse equal, there is also the motivation to use just one instead of two concepts fordynamically looking up definitions. A void value is simply a hierarchy sibling ofstandard objects and other base-, default-, and error values.

We have seen that trying to achieve automatic initialization of references to voidvalues involves some additional complexity (see Figure 11.8 on page 198) and intro-duces both storage (for DefaultReference ) and computation (for delegation) over-head (see section 11.8 on page 197, Reference Initialization). Consequently, languagesemantics should not initialize references to Nil, but to their associated void val-ues. As a result, it would become easier to force program execution to “stay inthe game”, i.e., stay within the domain of language semantics without creation ofruntime aborts or implementation dependent effects [Appel93]. With regard to thecaveat of unnoticed errors, introduced by not stopping on not explicitly initializeddata (see section 11.7 on page 196, Initialization Errors), one may still provide no, orexception generating behavior, for void values, in order to force a brute but earlydetection of unintended void values.

Applications for Void Value that go beyond what most languages allow to emu-late are “Initialization by declaration” and “Implicit creation”. Even if it is possibleto tie initialization (assigning useful default values) to creation (attaching an objectto a reference), Nil values still exist “between” declaration and initialization. Void

10EIFFEL, in particular, provides the flat format for classes that could be used to resolve all inher-ited and redefined methods of void value classes.

11With procedural abstraction only, one is forced to add each element of the receiver to the argu-ment in any case.

Page 269: A Functional Pattern System for Object-Oriented Design

14.7 Void Value 251

Value allows transferring the concept of automatic initialization known for valuetypes to reference variables, since even variables of abstract type can be attached toa proper void value. An abstract class definition thus not only provides an interfaceand template methods, but optionally also a void value description.

Beyond the implicit creation of void values, one may extend the concept to al-low automatic initialization to any object, i.e., implicit object creation. In analogyto the creation clause for methods in EIFFEL, one may select a creation subclass tobe used for implicit reference initialization. In contrast to void values, the thuscreated instances, could be used to capture and hold state right away. Instead offorcing the programmer to guarantee the creation of an object instance in advance,it appears very useful to allow implicit creation of object instances. Note that “Ini-tialization by declaration” refers to the automatic attachment of references to voidvalues, while “Implicit creation” refers to the automatic conversion of void valuesto objects. Whenever, a void value can not handle a message (e.g., state has to be ac-cepted), it replaces itself with an appropriate object. Such an automatic conversionwould make explicit creation calls superfluous (see code example 14.1).

if array.item(key) = Void then!!set.make;input_array.put(set, key);

end

array.item(key).put(data);

Figure 14.1: Code example: Initialization.

“Initialization by declaration” and “Implicit creation” can work together to im-plement Lazy Initialization, which avoids exposing concrete state and initializa-tion overhead, allows easy resetting, and provides a proper place for initializationcode [Auer94]. Ken Auer’s solution requires to test variables for Nil values, i.e.,for being not yet initialized. Automatic initialization of references, however, allowsapplying messages to void values (instead of Nil), whereas automatic conversionto objects allows the implicit creation of results on demand. The latter step, maynot even be necessary if the required results do not need to hold state. As longas no object functionality is needed, void values suffice and do not create storageoverhead for “empty” objects.

Summarizing, Void Value replaces void reference handling through type ab-straction with providing default cases through procedural abstraction. The formeris related to functional programming, while the latter is related to object-orientedprogramming [Reynolds75, Cook90]. While void values are seemingly superiorto nullcases, the latter concept has its merits as well. As previous sections in thischapter already observed, in general, both views are equally useful and should beavailable on request. The solution to this goal is presented in the next, final section.

Page 270: A Functional Pattern System for Object-Oriented Design

252 14 Pattern Driven Language Design

14.8 Conclusion

A language that doesn’t affectthe way you think about programming,

is not worth knowing.– Alan J. Perlis

The impact of the functional pattern system on language design, as detailed in theprevious sections, is summarized in table 14.1.

Namelanguage concept

Motivation / Effect

Function Objecthigher-order functions

user defined control structures, componentprogramming, functional extensibility.

Translatormulti-dispatch

heterogeneity problem non-intrusivelysolved, datatype extensibility in the presenceof functional extensibility.

Lazy Objectlazy evaluation

modularization, declarative programmingstyle, iteration framework, recursive valueinitialization, user defined control structures.

Transfoldalgebraic library

organization

library organization with inductive and coin-ductive types, emphasis on hylomorphisms(fold ◦ unfold operations), operation imple-mentation with parallel hardware.

Value Objectvalue semantics

default value assignment, additional broad-casting assignment, value subtyping.

Void Valuevoid behavior

void behavior instead of exceptions, referenceinitialization.

Table 14.1: Pattern implications on language design

Pattern Function Object adds functional decomposition (i.e., functional exten-sibility) to object-oriented design (see section 14.2 on page 233). It is an essen-tial means for the construction of components [Jazayeri95] and black-box frame-works [Johnson & Foote88]. Translator makes datatype extensions feasible in thepresence of a functional decomposition by means of extensible multi-dispatchingfunctions. Heterogenous data (in a structure or coming from, e.g., a database or net-work) can be dealt with, without imposing an interface on the respective datatypes(see section 14.3 on page 238).

Page 271: A Functional Pattern System for Object-Oriented Design

14.8 Conclusion 253

Pattern Lazy Object fosters a declarative programming style, enables the itera-tion framework described by Transfold, allows value objects to have recursive ref-erences, and gives designers control over the evaluation strategies for new controlabstractions (see section 14.6 on page 246).

Pattern Transfold builds upon the above concepts and provides good argumentsfor an algebraic library organization (see section 14.4 on page 240).

Value Object and Void Value collaboratively argue to abandon the traditionalnotion of a reference. The default semantics for assignment should be value se-mantics while sharing must be explicitly achieved with a broadcasting assignment(see section 14.5 on page 243). An ubiquitous Nil value is abandoned in favor oftype specific void behavior. References are automatically initialized with their cor-responding void values. Hence the asymmetry between values, which have usefulinitialization values, and references that do not, is removed (see section 14.7 onpage 248).

As a general observation, there is a demand for a type system that meets the flex-ibility which is expressible with the functional pattern system. In particular, sup-port for expressing type constraints and extended subtyping opportunities werefound to be necessary in addition to EIFFEL’s capabilities.

Furthermore, EIFFEL complicates a functional style by achieving object creationby a statement, rather than creation expressions. Consequently, a single line like

nonsense x y≡ (real ((x,y)∗ (y,x)), real ((x,−y)∗ (y,−x)))

expands to a small program:

nonsense (x : Number; y : Number) : Complex islocal z1, z2, r1, r2 : Complex;do

!!z1.make(x,y); !!z2.make(y,x);!!z3.make(x,-y); !!z4.make(y,-x);

!!r1.make(z1*z2);!!r2.make(z3*z4);

!!Result.make(r1.real, r2.real);end;

The rational for having a creation statement is to accommodate the fact that objectcreation is a side effect and an expression should not create side effects [Meyer95].I certainly second this argument but one should note that the “side effect” of acreation expression does not violate any existing invariant in the system. In otherwords, unlike typical side effects, object creation can do no harm to other clients.

The only unwanted effect could be an exception raised due to insufficient mem-ory. It does not seem convincing to reject creation expression on the basis that suchan exception should rather be raised by a statement.

Page 272: A Functional Pattern System for Object-Oriented Design

254 14 Pattern Driven Language Design

Of course, the above code examples do not only compare creation statementsto creation expressions. The notational economy of the functional definition alsolargely draws from the fact that values can be created with manifest constants. A(tuple representation of a) complex value in the functional definition is created withjust five characters, whereas the corresponding EIFFEL creation statement is muchmore verbose.

This suggests to provide at least one way to express constant values (e.g., syn-tax for manifest array constants) whose type can be converted into other datatypes(e.g., collections), that is, to adopt the SMALLTALK philosophy [LaLonde94]. Whilethis issue may appear insignificant, it extends to the before mentioned library or-ganization. Linear manifest constants could be used to create any other datatype,even branching ones, like trees. The already mentioned notion of an “unfold” canbe used as a creation method in datatypes. It would work as a stream reader thatconstructs any particular datatype by consuming input from a manifest constant,output of a different datatype, etc. For instance,

t := Tree[Integer].unfold({Node:{Node: {Leaf: 1} {Leaf: 2}}{Leaf: 3}

}).

With this point of view, “unfolds” are parsers, while “folds” are interpreters.Several of the previous sections in this chapter arrived at the conclusion that

neither datatype extensibility (object-orientation) nor functional extensibility (func-tional programming) are sufficient on their own (see sections 14.2 on page 233, 14.3on page 238, 14.7 on page 248). The number of attempts on the Visitor pat-tern [Gamma et al.94, Nordberg96, Martin97, Krishnamurthi et al.98] indicate theimportance and difficulty of adding functional extensibility to object-oriented de-sign. However, from the above versions only the last achieves to reconcile datatypeextensions with functional extensibility. All other versions require changes to func-tion code when new datatypes are added. This, however, means that one is left withan “either-or” choice. Without Visitor, functional extension is awkward, whereaswith Visitor, datatype extension is awkward.

The Extensible Visitor pattern [Krishnamurthi et al.98] avoids this by enablinglate binding on function creation. Thus, function creation in existing functioncode can be adapted to new requirements — i.e., to the new functions neededfor the datatype extensions. Using inheritance to override function creation, ex-isting functions can be extended. There is a need to create functions within visitors,and therefore the problem of prematurely fixing the function version, because itis sometimes necessary to pass different arguments to visitors for sub-traversalsor one even needs to change the visitor type (e.g., using identifier analysis dur-ing type checking). Unfortunately, Extensible Visitor suffers from problems withthe original Visitor version [Gamma et al.94], i.e., it implies the pollution of thedatatype interface, introduces a dependency cycle, and does not support partialvisitations [Nordberg96, Martin97].

Page 273: A Functional Pattern System for Object-Oriented Design

14.8 Conclusion 255

I argue that the notion of a multi-dispatching function, used by Translator, ismuch more natural then the explicit binding of data and functions via a visitor-styleAccept method. Translator does not require datatypes to feature an Accept methodand, hence, even allows functionally extending datatypes without source code ac-cess. Translator-style functional extensions do also cope with datatype extensibil-ity, since the (generic) functions used for traversals never change. New functionsare simply introduced by extending the set of specialized functions available to ageneric function (see chapter 12 on page 201), that is, the state of the traversal func-tion is changed, not its type. Ergo, no code needs to be updated in case of datatypeextensions.

Revising the above scenario, we have datatypes — i.e., an abstract interfaceplus concrete subclasses for each datatype constructor — which are interpreted bymulti-dispatching functions, i.e., generic functions with associated function pack-ages (see chapter 12 on page 201). While this achieves extensibility in both dimen-sions, (external) functions and datatypes exist in isolation. One can not look at adatatype and see what external functions it supports. Obviously, that implies thatexternal functions can not be changed in an object context, i.e., as pattern match-ing branches distributed over constructor objects. Conversely, it is also not pos-sible to alter object methods (internal functions) in a functional context, i.e., as amonolithic algorithm. In other words, functions implemented according to the In-terpreter pattern (i.e., internally) can not be treated as functions, whereas functionsimplemented according to the Translator pattern (i.e., externally), can not be treatedas being distributed over objects.

So, although we overcame the dichotomy between functional and object-oriented view for extensibility, we still suffer from the same dichotomy in termsof external (type abstraction) versus internal (procedural abstraction) function def-inition. An internally implemented function should be changeable as a single func-tion without the need to visit all constructor parts. Conversely, the change or evenintroduction of a datatype should not require to update many existing externalfunctions. One “object view” should gather all relevant pattern matching branchesfrom external functions. Ideally, no commitment to either view, i.e., paradigm,would be necessary and either viewpoint could be adopted when appropriate. Inother words, retooling (changing the paradigm) should not be expensive, althoughit usually is [Kuhn70].

The logical conclusion from the above is to adopt a more general decomposi-tion strategy that allows the dynamic generation of an object-oriented or functionalperspective. We already encountered this general view on decomposition in theform of table 4.2 on page 65. We simply construct a matrix (see table 14.2 on thenext page) with columns for datatype constructors (ci) and rows for functions onthe datatype ( fi).

Each tile within the matrix defines the result of applying one function to oneconstructor. Taking a vertical stripe from the table, gives us the object-orientedview (a constructor subclass with functions). A horizontal stripe, gives us the func-tional view (a function defined on constructors) [Cook90]. Using an analogy fromphysics, we represent elementary particles with a matrix and let observers take a

Page 274: A Functional Pattern System for Object-Oriented Design

256 14 Pattern Driven Language Design

Datatype︷ ︸︸ ︷c1 c2

f1 f1 c1 f1 c2

f2 f2 c1 f2 c2

Inte

rfac

e︷

︸︸︷

f3 f3 c1 f3 c2

Table 14.2: Tiles with object-oriented and functional dimensions

wave or particle view on the matrix.The collection of constructors (topmost row) is a datatype, whereas the collec-

tion of functions (leftmost column) is an interface. A datatype defines constructorsand an interface defines a set of functions.

Given just a datatype, we may add any interface, that is defined on a super-type12 of it, e.g., provide different function sets (e.g., stack or queue interfaces) onlists. This is a functional view, where we add functions to passive data.

Given just an interface, we may supply any datatype, that is a subtype to that as-sumed by the interface, e.g., implement a list interface with alternative constructorsets. This is an object-oriented view, were we supply implementations to abstractinterfaces.

In the envisioned — tile based — programming language, a programmer coulddeclare datatypes and interfaces independently and rely on late binding of themissing respective part. Hence, both paradigms would be available. The combina-tion of datatype and interface can be viewed from a functional perspective — thereis one datatype with an associated set of functions — or from an object-orientedperspective — there is one abstract class with with several, constructor implement-ing, subclasses. In any way, it seems to be appropriate to speak of a concrete type.

Extensions are now simply a matter of adding a horizontal or vertical stripe tothe table. That is, it does not matter whether we filled the table with horizontalstrips (functions) or vertical stripes (constructors) before, we simply add a functionor constructor. Maybe even more importantly, if we build the table column-wise(e.g., adding language constructs to a language), we can still alter an existing func-tion (e.g., operation on the language, such as compilation or type checking) witha functional view, by locally altering a row. Conversely, if we build the table row-wise (e.g., extending language operations), we may still concentrate on individuallanguage constructs, i.e., alter all functions for one constructor in an object context.

Again, using more conventional terms: The tiles approach makes it feasible toimplement an interpretation on an abstract syntax tree as tree member methodssince one interpretation could be edited as a single function, created by a func-

12The notions of supertype and subtype shall include the type itself.

Page 275: A Functional Pattern System for Object-Oriented Design

14.8 Conclusion 257

tional view on a definition distributed over many types. In turn, pattern Translatorcould be applied even if the visited structure is unstable since a change to one treemember object could be done simultaneously to all interpretations, gathered by anobject-oriented view on all interpretations.

All this, of course, depends on a supporting environment. A browser (tiler)must be able to provide each view on demand. Working on actual functions or ob-jects, then, is just an illusion since only a virtual view is manipulated, with changesbeing appropriately distributed to the corresponding tiles. Functions and objects,however, actually exist in software as declared entities.

I conclude this outline of an approach that uses environment support to gen-erated most appropriate views, with considerations about repeating the tiles con-cept on datatypes and interfaces respectively (i.e., the row and column types oftable 14.2 on the facing page). Previously, tiles defined one function behavior forone constructor. Now, each inner square in table 14.3 defines one interface behavioron one datatype.

Domain︷ ︸︸ ︷D1 D2

I1 I1 D1 I1 D2

I2 I2 D1 I2 D2

Inte

rpre

tati

on︷

︸︸︷

I3 I3 D1 I3 D2

Table 14.3: Concrete types with implementation and interface views

Alternative interfaces on one datatype (leftmost column) are different views ona datatype (e.g., a person or library member view on a student). In object-orientedterms they can be regarded as multiple abstract superclasses.

Alternative datatypes to one interface (topmost row) are implementationchoices for that interface. An interface, therefore, is like an abstract class in object-oriented programming that defines an interface for several concrete implementa-tions. Sets of implementers and views give rise to two concepts: Domains andinterpretations. A domain is a collection of datatypes that can implement an in-terface. An interpretation is a set of views on a datatype. Note, that a squarein table 14.3 corresponds to table 14.2 on the preceding page, i.e., is defined byediting and possibly adapting a tiles matrix. Table 14.3, hence, organizes concretedatatypes into combinations of interfaces with their implementations. The sameapproach that was useful to relate functions to data is now used to add implemen-tations, define new views or roles, find implementations for an interface, changeone implementation for several interfaces, etc. If we assume that one of the inter-faces is a State interface to several ConcreteState implementations (see the State

Page 276: A Functional Pattern System for Object-Oriented Design

258 14 Pattern Driven Language Design

pattern in [Gamma et al.94]) then a browser (tiler) invoked on table 14.3 on thepage before is an editor for the State pattern. Organizing interfaces and imple-mentations this way may also extend into the area of subject-oriented program-ming [Harrison & Ossher93], where client-specific interfaces (views) play an im-portant role.

Datatypes

Interfaces

Constructors

Functions

Domains

Interpreta

tions

Figure 14.2: Hierarchical tiles editing

If we further progress on the ab-straction level, i.e., again fold thewhole matrix to one square in anew matrix, we are in the posi-tion to define interpretations on do-mains (e.g., type-checking or com-pilation on a programming lan-guage). With just one sort of in-teraction (using a tiler) we, there-fore, may ascent or descent ab-straction levels and edit individualtiles (see figure 14.2). Note thata functions-constructors matrix onthe lowest level corresponds to aclass in object-orinted languages.A typical class browser does noteven support the organization onthe second level. Though travers-ing inheritance relationships is pos-sible, these may or may not corre-spond to interface-implementationrelationships. It is intriguing how useful the abstraction matrix on the top is addi-tionally. Together, the three levels organize software into a three abstraction levelhierarchy, which is amenable by one user interaction paradigm, that is, tile brows-ing. Note that nothing really prevents us from going further up the hierarchy. A setof interpretations could be called a tool and a set of domains might be a field, andso on. The further we proceed upwards from the bottom, the sparser the matrixeswill be filled. All axis labels lend themselves to be used as types for the declarationof software entities in programs.

The usefulness of the above notions, their details, and the approach in gen-eral remains to be researched. Since the main emphasis of this dissertation is thefunctional pattern system — with a view to its implications on language design —this relatively advanced outline of an envisioned language and environment modelmust necessarily be incomplete. Though many important details have been left out,I nevertheless hope to have drawn a convincing proposal for future language andenvironment research.

The idea to lever languages by the use of tools has, of course, been exploitedbefore. For instance, type inference can be added to languages with much sim-pler type system by means of context relations [Snelting86]. Monomorphic lan-guages (e.g., PASCAL) can be used polymorphically with the help of an additional

Page 277: A Functional Pattern System for Object-Oriented Design

14.8 Conclusion 259

fragment system [Snelting et al.91, Grosch & Snelting93]. SMALLTALK does not re-quire any syntax to separate class methods from each other since they are editedwith a browser only one at a time. SMALLTALK’s highly interactive environment isalso highly intertwined with its dynamic type system and interpretation approach.Only in conjuction, language and environment provide a paramountly productivedevelopment tool13. Since SMALLTALK is a single-dispatch language only, a toolhas been build to support the maintainance of multi-dispatching arithmetic oper-ations [Hebel & Johnson90]. Industrially supported programming environments,though, merely scratch the surface (e.g., with syntax-highlighting) and do not ex-ploit the available potential at all.

In conclusion, I showed that a well designed language, such as EIFFEL, makesthe subsumption of functional programming feasible and partially offers good im-plementation support. However, I also demonstrated that functional patterns addreal value and that there is plenty of room for improving support for functionalpatterns.

Ultimately, the functional pattern system induces a vision for a very ad-vanced programming language with features such as higher-order functions,multi-dispatch, lazy evaluation, value semantics with broadcasting, and void be-havior. The language uses an algebraically organized library and is based on thenotion of tiles. Software organization principles are deferred to an associated envi-ronment, therefore, allowing dynamic paradigm changes.

13Curiously, SMALLTALK — being the first object-oriented language after SIMULA — has manyfunctional characteristics, such as blocks, streams, internal iterations, and value (not reference) com-parison for objects being the default.

Page 278: A Functional Pattern System for Object-Oriented Design

260 14 Pattern Driven Language Design

Page 279: A Functional Pattern System for Object-Oriented Design

Epilogue 261

Epilogue

Not only will men of science have to grapple with the sciences that deal with man,but — and this is a far more difficult matter — they will have to persuade

the world to listen to what they have discovered. If they cannot succeedin this difficult enterprise, man will destroy himself

by his halfway cleverness.– Bertrand Russel

Afunctional pattern system is valuable in many aspects. The followingsections conclude about the many facets involved by the successful at-tempt to capture the functional programming paradigm with patterns

for object-oriented design.

Software design

Each of the six presented patterns is capable of improving today’s software de-signs. Even those patterns that do not represent completely novel approaches, forthe first time explicitely describe the applicability and resulting consequences ofeach technique in a functional pattern system context. To my knowledge, my workrepresents the first comprehensive attempt to examine functionally inspired tech-niques in terms of software engineering concepts.

Understanding object-oriented practice

In the context of the functional pattern system, many object-oriented practices ap-pear as special cases of more general functional patterns. For instance, call-backfunctions according to the Command pattern are function objects14 that do not takearguments after their creation. The acceptance of arguments after creation opens upa wealth of useful applications (see pattern Function Object on page 93). So-callediterator objects, coming into existence through the need to separate iteration actionrefinement inheritance from data structures [Meyer94b], may also be regarded asfunction objects with a non-standard application interface. Likewise, a visitor ca-pable of visiting several node types, is a function object with several entry points.

14Procedure objects, to be precise (see section 7.10 on page 107).

Page 280: A Functional Pattern System for Object-Oriented Design

262 Epilogue

Pattern Lazy Object describes what other benefits, apart from exchangeability,may be obtained from a pipes and filters architecture [Buschmann et al.96] (e.g.,decoupling). Moreover, knowledge of Lazy Object immediately provides insightinto the justification of the presence of streams in the SMALLTALK library. They helpto avoid repetition of computations, e.g., when copying structures. In addition,Lazy Object suggests to also use them for partial structure exploration.

The patterns Interpreter [Gamma et al.94] and Translator could be regarded asobject-oriented and functional incarnations respectively of the same idea: Abstractsyntax is used to reify meaning. Pattern Translator adds a framework for incre-mental evaluation additionally and — by its non-intrusive way to add meaning tostructures — is suitable for more interpretation applications, such as a persistencemechanism.

The Transfold pattern uses a very general scheme (folding) that allows express-ing all the usual iteration operations (e.g., mapping, filtering, finding, etc.) interms of it. Furthermore, it generalizes internal iteration to multiple data struc-tures, thereby providing a remedy for the inflexibility previously associated withinternal iteration.

Pattern Void Value widens the scope of using a default implementa-tion [Smith95] to library design and to a safe mechanism of reference initialization.

In sum, the functional pattern system can help to understand existing object-oriented solutions in a broader sense and, hence, enable their generalization andalso application to other areas. More fundamentally, it is hoped to make formerly“ingenious” designs understandable as relatively straightforward applications ofwell-known functional patterns. Patterns in general are, thus, a means to movedesign from an art towards engineering15.

Object-oriented fertilization

Although techniques similar to function objects and lazy object have been de-scribed before, they gave a partial view only.

Function Object

For instance, descriptions of techniques similar to function objects concentratedon the locality of function definition [Breuel88], discussed a weakly typed way ofachieving dynamic dispatch on functions [Coplien92], or used a non-uniform appli-cation syntax for algorithm parameterization [Hillegass93]. Apart from discussingthe software engineering consequences of first class functions, pattern FunctionObject introduces innovations such as returning functions as a result, type safe cur-rying of functions, keyword parameters, and generic function objects [Kuhne97].

15There is no doubt, however, that engineering itself is not a mechanical activity and genius willstill shine through exceptional masterpieces of engineering.

Page 281: A Functional Pattern System for Object-Oriented Design

Epilogue 263

Lazy Object

Precursors of Lazy Object descriptions described a “trick” only to avoid unneces-sary and repetitive evaluations [Smith95] or concentrated on the architectural as-pects of pipes and filters [Buschmann et al.96]. John Hughes’ investigation in thebenefits of higher-order functions and lazy evaluation [Hughes87] is the only workI know of, which is similar in spirit to the research performed for this thesis. Ofcourse, combined in a functional pattern system context, Function Object and LazyObject go far beyond the issues discussed by John Hughes.

Transfold

Neither external iterators [Gamma et al.94], nor internal iterators operating on asingle structure only [Goldberg & Robson83] provide a satisfactory general itera-tion framework. The availability of Function Object, however, makes internal it-eration feasible even for object-oriented languages without support for closures.The availability of Lazy Object makes the separation of structure exploration anddata consumption feasible. Combined with the idea of simultaneously processingmultiple structures during one internal iteration, the result called Transfold pro-vides the safety and economic notation of internal iteration while maintaining theflexibility and control of external iteration.

Translator

Without having been designed for this purpose, pattern Translator resolvessome problematic issues associated with Visitor [Gamma et al.94]. Other pro-posals have been made to avoid interface pollution and a dependency cy-cle [Martin97], to allow partial visitations [Nordberg96], or to provide extensi-ble visitors [Krishnamurthi et al.98]. But none of them introduces the elegant no-tion of an iteration with a generic function and coincidentally opens up the pos-sibility of incremental evaluation with the concept of homomorphic interpreta-tions [Kuhne98].

In sum, patterns Transfold and Translator yield remarkable solutions for previ-ously — with a pure object-oriented mind-set — unsuccessfully tackled problems.

Void Value

While there have been examples of default objects in the spirit of Void Value (e.g.,NoController in VISUALWORKS), the idea to completely abandon nil and providevoid values as a general principle is without precedent. The nullcases of UFOrepresent a corresponding idea in a functional programming framework. A com-parison between the two diametrical approaches found Void Value to be supe-rior [Kuhne96b].

Page 282: A Functional Pattern System for Object-Oriented Design

264 Epilogue

Value Object

Finally, pattern Value Object touches upon a much neglected topic in object-orientation: The taming of state. Software methodologies typically ignore thistopic generously and focus on interfaces. Other approaches take reference se-mantics for granted and try to establish restrictions in order to prevent alias-ing [Hogg91, Minsky96, Almeida97]. Value Object is a small, but maybe not thatunimportant, reminder of the fact that reference semantics is a natural solution forefficient execution but not for intuitive programming.

In conclusion, functional patterns have been shown to be superior to a numberof purely object-oriented solutions, e.g., using (multiple) inheritance and double-dispatch emulation.

The human side of patterns

One part of the pattern community believes that “patterns are not so much about formand technology as they are about aesthetics and decency, about improving quality of life,about human dignity and decency” [Coplien96a]. While the effect is indirect, I see agood chance that the presented pattern system will bring more comfort and qual-ity to software users. Pattern Function Object promotes software with dynamicallyexchangeable strategies. This may induce a change in human computer interfacedesign and the way software can be updated. A user of a word processor mightbe able to select between formating strategies and combine them with hyphenationalgorithms. Software updates might consist of single components only that, e.g.,add a formatting strategy to allow text to float around figures. This perspectiveprovides an interesting alternative to the current buy-all-or-nothing choice, wherefat software includes many unwanted features but almost certainly misses individ-ually interesting ones. With function-object-like components, users would be em-powered to configurate their own systems by buying components from the shelf.

The stream concept described in Lazy Object may help to work towards thesame goal. Streams are the foundation of a pipes and filters architecture that allowsconfiguration by the user. As a UNIX user creates new commands by combining ex-isting commands with pipes, an application user may freely combine and arrangecomponents that communicate with streams. This also would allow deferring de-cisions about application design to the user, creating more tailored and, thus, moreuseful and comfort providing software.

Transfold provides the most comfort to the programmer using it, but also usersbenefit from increased safety. However small the risk of introducing errors withexternal control structures is, it is there (see the Mariner incident in section 10.2.1on page 163) and internal iteration minimizes that risk to absolutely zero.

The contribution of Value Object to end user comfort is quite indirect, but itmay help to produce software containing less bugs (caused by aliasing) and allowsprogrammers investing more time in improving software quality, because they arefreed from tracking down tricky, state induced problems.

Page 283: A Functional Pattern System for Object-Oriented Design

Epilogue 265

Pattern Translator offers users the benefit of incremental computations and,therefore, faster system response. An incremental approach may make the differ-ence between annoying latencies and fluid interaction.

Last not least, Void Value can not prevent system malfunctions but allows han-dling such situations much more gracefully. Instead of frustrating users with un-nerving program — or even operating system crashes — Void Value causes initial-ization induced system malfunctions to emerge as a non-performing system. Usersare not delighted by not getting the answers they hoped for but at least they willsave the trouble of losing data due to crashes or spending frustrating hours withreboots.

None of the patterns in the functional pattern system are a prerequisite toachieve user friendly software but their inherent software engineering qualitiesfacilitates the creation of such software. Almost inevitably their contributions toflexibility and safety will shine through to improved human computer interactions.

Given a toolkit with such properties, a designer is less urged to emulateflexibility. For instance, user defined macros in a word processor are diffi-cult to provide with an implementation language that does not facilitate dy-namic extensions. As a result, inefficient interpretation techniques might be used.

LazyObject

Translator

Transfold

FunctionObject

VoidValue

ValueObject

Figure E.1: Pattern Cathedral

Hence, the functional pattern sys-tem may help to reduce the phe-nomenon of fat and slow applica-tion software resulting from ineffi-cient emergency routes taken to ac-count for an inflexible implementa-tion language.

Pattern system

Within the functional pattern sys-tem, patterns reinforce each otherdue to their manifold interactionsand collaborations. The analysis ofpattern relationships revealed thenature of individual patterns andallowed them to be placed in a con-ceptual space. Pattern Translatorwas identified as a high-level, clientoriented service. Transfold lendsitself to be implemented as a li-brary organization principle. Pat-terns Lazy Object and Value Object vividly claim language support due to theirsupporting potential, but are also very well able to benefit from other patterns.

The Void Value pattern is clearly the foundation stone for the functional patternsystem (see figure E.1) and any language designed according to it. Complementary,

Page 284: A Functional Pattern System for Object-Oriented Design

266 Epilogue

Function Object is also fundamental in nature but shapes the pattern system orlanguage from above, as a crucial framework element. As such, it can be regardedas the keystone16 of the functional pattern system.

I do not claim completeness with the presented six patterns. Especially manyfunctional programming practices wait to be documented. With regard to object-oriented software construction it seems worthwhile to further explore patternshelping to control state in the spirit of so-called Islands [Hogg91]. Nevertheless,the most crucial language concepts have been captured and described by the pre-sented functional pattern system.

Drawbacks

The most obvious drawback with respect to the functional ideal is the overheadin description complexity. Some services, such as lazy streams, can be coded onceand for all but, for instance, the tedious coding of dedicated classes for functioncurrying must be repeated for every new function object definition. Although thebenefits outweigh the required effort, the sometimes apparent need to code an em-ulation of fundamental mechanisms ultimately calls for language support.

Furthermore, object-oriented emulations of built-in functional concepts may in-cur efficiency penalties. While most systems will not be as time critical as to dis-allow functional patterns — and may in fact experience a speed up due to lazyevaluation, for instance — language support could significantly speed up execu-tion and would allow compiler optimizations.

Tool support

Tools could aid in using functional patterns in conventional object-oriented lan-guages from simple support such as automatically generating all necessary classesfor a function object, till sophisticated type checking to discover possible failure tofind an appropriate special function when a generic function is applied to a poly-morphic variable.

The most intriguing impulse for tool support, however, is initiated by patternVoid Value. Research, comparing nullcases to Void Value [Kuhne96b], led to theidea of moving responsibilities away from languages towards their associated en-vironments.

Tool support as described in section 14.8 on page 252, frees a language fromcommitting itself to either paradigm. The language just provides the notation forfilling in the tiles of figure 4.2 on page 65 (the implementation corresponding toa function and an argument type) while a browser generates either a functional(rows), an object-oriented (columns), or a global (full table) view. This approachopens up the way to a (tile based) language supporting both procedural and type

16Or “boss”, i.e., the last structural stone in the dome of a cathedral that imparts its grandeur thenecessary stability.

Page 285: A Functional Pattern System for Object-Oriented Design

Epilogue 267

abstraction extensions. The dichotomy between procedural and type abstractionis akin to the dual wave and particle interpretations of elementary particles inphysics. Wave and particle interpretation are two incompatible views on the sameentity. In a sense, hence, tiles are the elementary particles of an integrated func-tional and object-oriented programming language. The programmer, therefore, isan observer of a software “experiment”. The programmer may choose the one in-terpretation that suits the currently required adjustments most. A programmingenvironment, thus, is not anymore just a facilitator of editing, debugging, projectmanagement and class browsing, but actively provides optimal, and in particular,updateable views on a software system’s organization (see figure 14.2 on page 258).

While object-oriented successors to LISP [Bobrow et al.86b] also use atomicpieces of constructor behavior (methods) to extend (generic) functions and objects,to my knowledge, the “tiles” approach represents the first attempt to retain anobject-centered, encapsulation providing view [Chambers92b], while coincidentallyallowing a functional perspective. Furthermore, it seems to be the first time thatthe organization principle or decomposition strategy — in short paradigm — ofa language has been made dynamic by deferring it to its associated environment.Although much research into the tiles approach is still necessary, it seems fair toclaim that William Cook’s conjecture, about multi-dispatch languages being a pos-sible escape from the procedural and type abstraction dichotomy [Cook90], hasbeen validated.

Paradigm integration

When inquired for his opinion about object technology Timothy Budd felt positiveabout it but, referring to multi-paradigm programming, added:

“. . . the real revolution would arrive when we finally understood how tobring these diverse points of view together into one framework. [Budd95]”

– Timothy Budd

Without doubt the functional pattern system is a contribution to multi-paradigmsoftware and language design. Its basic idea, to express functional concepts withpatterns for object-oriented design with a view to holistic language design wasacknowledged to be a contribution against a proliferation of concepts and termi-nology which would leave everyone with an incomplete picture [Reynolds96]. Thefunctional pattern system advances the integration of the functional and object-oriented paradigms since

• an analysis on calculus level was performed to ensure the adequateness ofembedding functions into objects,

• an investigation into the conflicts and cohabitance of the paradigms yielded anew integration approach and provided the stimulus for a selection of func-tional concepts to be expressed as patterns,

Page 286: A Functional Pattern System for Object-Oriented Design

268 Epilogue

• functional and object-oriented solutions complemented each other leading tosynergistic effects (see section 4.2.3 on page 59), and

• each functional pattern integrates smoothly into object-oriented designs.

As a result, I demonstrated the reduction of functional programming to object-oriented programming, at least in John Hughes’ sense in that you can not addpower to a language by removing concepts (e.g., state). All the virtues of functionalprogramming enabled by adding powerful concepts like higher-order functions orlazy evaluation are feasible with an object-oriented language as well. However, rea-soning about programs, e.g., as necessary for functional program transformations,is not possible for object-oriented software, unless one relies on design discipline(self imposed renunciation of state) for parts of the system. Yet, there is no way todefine state away anyway. For instance, using monads in functional programmingimplies that the order of mapping a list suddenly does matter [Meijer & Jeuring95].A monad used for stateful programming introduces order in calculations and it, inthat sense, therefore, does not matter whether state is expressed in a language orsupported by a language (see section 1.3.2.2 on page 21).

The object-oriented paradigm on its own was already shown to defeat manycritical arguments made by John Backus [Backus78] towards imperative, so-called“von Neumann” languages [Kuhne96a]. Supplemented with the presented func-tional pattern system the object-oriented paradigm truly liberates designers fromthe von Neumann style without any concession.

Patterns and Paradigms

Patterns and paradigms have an interesting relationship. Just like patterns dependon the level of abstraction, e.g., one could consider subroutines to be a pattern inassembler languages but not for higher level languages, they also depend on thecontext’s paradigm. For instance, in a CLOS like setting based on generic functions,the Visitor pattern makes much less sense, since it mainly shows a technique toachieve double dispatch in single-dispatch languages. Also, most arguments of theIterator pattern are obsolete for SMALLTALK with its rich iteration interface for col-lections. Therefore, the occurrence of certain patterns typically denotes weaknessesof their context paradigms, since they provide functionality which is not availableat a primitive level. As has been carried out in this thesis, patterns may also beused to describe and characterize a paradigm by capturing its foundations in welldocumented mini-architectures. So, as a third relationship aspect between patternsand paradigms, patterns may be used to embed one paradigm into another. The

• signaling (what is wrong with a paradigm),

• descriptive (what are the constituents of a paradigm), and

• integrative (paradigm benefit transfer)

Page 287: A Functional Pattern System for Object-Oriented Design

Epilogue 269

escher-paradigms.ps

Figure E.2: Paradigm integration with patterns

aspects of patterns with regard to paradigms tie these two terms into a closerelationship. I expect to see more evidence of this fruitful combination in thefuture since multi paradigm programming will become more and more impor-tant [Budd95] and patterns will surely continue their success. Patterns may beused to import paradigms, but ultimately will be used as lego pieces for languagedesign.

Summarizing, this thesis created an original approach for paradigm integration.The mechanisms of object-orientation and functional programming have been com-bined and examined for their suitability to build reusable designs. Coincidentally,an imperative paradigm has been joined with a declarative paradigm (see figure 4.1on page 56). A designer now has to choose between several competing mechanismsfrom two paradigms (e.g., inheritance and function objects). While this may appearto be an extra complication to a novice, it is an essential enrichment to the expert.Since the functional concepts are not only documented but formulated as patternscontaining applicabilities and consequences, even novices will have guidance as towhich tool-set to choose for a task.

Language design

As patterns are hoped to form the engineering handbooks of software development— and, therefore, contribute to make software engineering a science — languageconcept patterns, as presented here, may as well contribute to make language de-sign a science. A dissection of programming languages into software engineeringprinciples supporting concepts would allow the design of new languages from aknown set of pieces. The functional pattern system captures the essence of soft-ware engineering properties supported by functional concepts. Along with ananalysis of an effective paradigm integration approach (see chapter 4 on page 55)it enables a language designer to take a shopping list approach for the design of

Page 288: A Functional Pattern System for Object-Oriented Design

270 Epilogue

a new language. The documentation of the added benefit of a feature (e.g., formulti-dispatching methods see Translator on page on page 201) can be used fora solid justification for integrating it into a language. If the incorporation of fea-tures is based on single examples of their usefulness (e.g., support of binary meth-ods through multi-dispatch), the decision may look arbitrary since there are oftencompeting mechanisms that solve the same problem (e.g., a type system based onsubsumption [Abadi & Cardelli95]).

Furthermore, an analysis of relationships between patterns in a system (seechapter 13 on page 221) gives rise to an overall language architecture. As indicatedin section 14.8 on page 265, patterns Function Object and Void Value autonomouslydefined their position as two poles in a language design framework. Other patternslike Lazy Object and Value Object indicate a supporting as well as a service-usingrole and, thus, demand a language designer to facilitate all the rich possible in-teractions. It is also a welcome effect if a pattern, such as Transfold or Translator,recommends itself to be kept out of the design of a language. Hence, by analyzingpattern relationships, a designer gets hints which patterns are relevant to languagedesign and what their role in a language framework might be.

Related work also recognized the relationship between patterns and lan-guages. Gamma et al. acknowledge that patterns depend on the language con-text and, e.g., remark on Visitor that it is less needed in multi-dispatch lan-guages [Gamma et al.94]. Peter Norvig investigates into the significance of well-known patterns in the context of dynamic languages [Norvig96]. Seiter et al. pro-pose a new language construct (context relations) to facilitate the expression of anumber of known patterns [Seiter et al.96]. Jan Bosch also introduces languagesupport (a layered object model) to ease the implementation of design patternsand retain them as entities in the software [Bosch98]. Baumgartner et al. argue foran orthogonal combination of several well-known language concepts (interfaces,closures, and multi-dispatch) by demonstrating the reduction of effort needed torealize a number of design patterns [Baumgartner et al.96].

The latter work and mine are similar in that they regard particular patterns assymptoms of language deficiencies. Both approaches choose a specific set of pat-terns to induce a number of language constructs, whereas the above approachesaim at supporting patterns in general by one general language mechanism. Myapproach is different, though, in that Baumgartner et al. use typical patterns rep-resenting common coding practice, whereas my pattern base consists of languageconcepts from another paradigm. Hence, the discussions take place on differentlevels and the ultimate consequences of my paradigm integration for language de-sign are much more radical (see section 14.8 on page 252).

Krishnamurthi et al. also aim at synthesizing object-oriented and func-tional design to allow extensions of both types and processors (func-tions) [Krishnamurthi et al.98]. They propose the Extensible Visitor pattern, acomposite pattern [Riehle97], that uses a combination of Visitor and FactoryMethod [Gamma et al.94], to allow visitors to cope with extensions to the datatypesthey operate on. The relation to language design is a proposed meta-linguisticabstraction to facilitate the implementation of Visitor and Extensible Visitor. How-

Page 289: A Functional Pattern System for Object-Oriented Design

Epilogue 271

ever, Extensible Visitor does not overcome the problems of the original Visitordesign, i.e., it also implies the pollution of the datatype interface, introduces a de-pendency cycle, and does not support partial visitations [Martin97]. Most severely,the requirement of a process17 method in datatypes aggravates or even prohibitsthe definition of processes on already existing datatypes. In comparison to the tilesapproach presented in section 14.8 on page 252, their resulting software structureleaves processor (function) definitions distributed over an inheritance hierarchy.Hence, in case of a required change to a processor, the programmer has to con-sult multiple classes since processor changes are likely to cross processor variantboundaries. The tile approach avoids this by the notion of multi-dispatchingfunctions that do not rely on inheritance for extensions.

Apart from being a generative set of language design pieces, the functional pat-tern system was also used as a test case for an already designed language (see chap-ter 14 on page 233). The obstacles found while trying to implement functional pat-terns in EIFFEL revealed shortcomings of the language that also will emerge in otherattempts to design reusable software. Since the functional pattern system supportsflexible designs, it evaluates an implementation language for its ability to supportthese. Any difficulties encountered with the implementation of the functional pat-tern system are valuable hints on improving the implementation language.

Previous integration attempts

The functional and object-oriented paradigms have been the target for many inte-gration attempts. However, often only a partial attempt (e.g., just providing func-tions) has been made [van Rossum91, Klagges93, Dami94, Watt et al.94], or state iscompletely rejected [Braine94, Remy & Vouillon97].

Object-oriented extensions to functional languages typical concentrate on typ-ing issues and do not provide dynamic binding [Remy & Vouillon97]. One dialectof HASKELL uses existential types to enable dynamic binding [Laufer96] but statehas to be modeled in programs, i.e., is not supported as a primitive.

CLOS [Bobrow et al.86b], OAKLISP [Lang & Pearlmutter86] and DY-LAN [Shalit et al.92] can be regarded as object-oriented extensions toLISP [Winston & Horn84] but they have no strong notion of encapsulation. Classesare modeled with records with no access restrictions. Their generic functions are avery powerful approach but when viewed from a paradigm integration perspectivethe languages appear unpolished. The frequent use of macros and the meta-levelfacilities make them suitable for explorative programming but questionable forbeing software engineering languages18.

Two languages, UFO [Sargeant93] and LEDA [Budd95], attempt a full integra-tion including state. A comparison between the nullcases of UFO and Void Valuehas been given in [Kuhne96b] (see also section 14.7 on page 248). Timothy Budd

17The analog method used for Visitor is called Accept.18Indeed, the perceived lack of determinism in CLOS led to the development of SATHER.

Page 290: A Functional Pattern System for Object-Oriented Design

272 Epilogue

shows many examples when a particular paradigm is useful but does not extractapplicabilities nor consequences. So, even with a given multi-paradigm languagelike LEDA the functional pattern system is useful for guidance as to when to usewhich technique.

A functional pattern system is a promising approach to familiarize object-oriented designers with functional concepts. Another attempt is to take a popu-lar object-oriented language and conservatively extend it with functional features.The PIZZA language adds parametric polymorphism, first-class functions, and al-gebraic types to JAVA [Odersky & Wadler97]. Any JAVA program is also a correctPIZZA program. Parametric polymorphism is a standard feature in modern lan-guages and should have been available in JAVA already. One nice point about thePIZZA approach though, is that it allows polymorphic methods, i.e., universallyquantified variable types for methods that, hence, do not demand their class to fea-ture extra type parameters. First-class functions in PIZZA implement function ob-jects (without keyword parameters and multi-dispatch) and can even be specifiedanonymously, but fail to support partial parameterization directly. The discussionin chapter 7 on page 93, however, made it very clear that partial parameterizationis one of the most beneficial aspects of function objects. While support for algebraicdata types in PIZZA introduces a nice, short syntax for constructors (subclasses im-plementing constructors of a sum type), I prefer Void Value or dynamically extensi-ble multi-dispatch functions over pattern matching. The PIZZA switch statementsover type constructors are sensitive to the addition of new constructors. Void Valueavoids this issue by promoting procedural abstraction [Kuhne96b] (see also sec-tion 4.2.1.2 on page 58). Multi-dispatching functions avoid the same problem byallowing the extension of the specialized function set without requiring changes toold code. Conclusively, PIZZA is a vast improvement over JAVA but is not entirelysatisfactory with regard to a paradigm integrating language. The design of JAVAand its virtual machine forced the designers of PIZZA several times to compro-mises [Odersky & Wadler97], and left a number of rough edges. Most prominently,built-in JAVA types do not mix well with user defined classes, for example when try-ing to define a general container class. Ironically, JAVA’s solution to polymorphicarrays, prohibits to adopt the best model of arrays in PIZZA [Odersky & Wadler97].

Although BETA [Madsen et al.93] and TRANSFRAME [Shang95b] have not beendesigned to integrate paradigms, their unification of classes and functions are theclosest approach to a holistic paradigm integrating language, as suggested by thefunctional pattern system. However, the discussion about tool support in sec-tion 14.8 on page 252 seems to indicate that a reductionistic approach appears tobe more promising. With a holistic concept like BETA’s pattern, that can be usedas an object or as a function, one, nevertheless, has to decide for either view andis left with the benefits and drawbacks of this decision. Hence, the approach tohave a language that allows defining tiles that are dynamically arranged to repre-sent a function or an object by a tool, seems to be the better approach. Also, noneof the above mentioned languages attempts to integrate state with lazy evaluation.Interesting research to make effects lazy has been carried out for functional lan-guages [Launchbury93, Launchbury & Jones94], but a language supporting state-

Page 291: A Functional Pattern System for Object-Oriented Design

Epilogue 273

ful objects with dynamic binding and lazy evaluation remains to be designed. Theinvestigation into paradigm conflicts and possible cohabitance, the impulses fromthe functional pattern system, and the proposed directions in language design, arehoped to be supporting contributions towards this goal.

“If I have not seen as far as others, then that’s because giants were standingon my shoulders.” – Hal Abelson

Page 292: A Functional Pattern System for Object-Oriented Design

274 Epilogue

Page 293: A Functional Pattern System for Object-Oriented Design

Bibliography 275

Bibliography

[Abadi & Cardelli94] Martin Abadi and Luca Cardelli. A theory of prim-itive objects: untyped and first order systems. InProc. Theor. Aspects of Computer Software, pages 296–320, Japan, 1994.

[Abadi & Cardelli95] Martin Abadi and Luca Cardelli. On subtyping andmatching. In W. Olthoff, editor, Proceedings ECOOP’95, LNCS 952, pages 145–167, Aarhus, Denmark,Springer-Verlag, August 1995.

[Abadi94] Martin Abadi. Baby modula 3 and a theory of ob-jects. Journal of Functional Programming, 4:249–283,April 1994.

[Abelson & Sussman87] Harold Abelson and Gerald Jay Sussman. Structureand Interpretation of Computer Programs. The MITPress, Cambridge, MA, London, 6th edition, 1987.

[Agrawal et al.91] Rakesh Agrawal, Linda G. DeMichiel, and Bruce G.Lindsay. Static type checking of multi-methods. InProceedings OOPSLA ’91, pages 113–128, November1991.

[Aho & Ullmann92] Alfred V. Aho and Jeffrey D. Ullmann. Foundationsof Computer Science. Computer Science Press, NewYork, 1992.

[Aho et al.86] Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman.Compilers: Principles, Techniques, and Tools. AddisonWesley, Reading, Massachusetts, March 1986.

[Alexander et al.77] C. Alexander, S. Ishikawa, and M. Silverstein. APattern Language. Oxford University Press, 1977.

[Alexander64] Christopher Alexander. Notes on the Synthesis ofForm. Harvard University Press, 1964.

[Alexander79] Christopher Alexander. A Timeless Way of Building.Oxford University Press, 1979.

Page 294: A Functional Pattern System for Object-Oriented Design

276 Bibliography

[Allison92] L. Allison. Lazy dynamic-programming can be ea-ger. Information Processing Letters, 43(4):201–212,1992.

[Almeida97] Paulo Sergio Almeida. Balloon types: Controllingsharing of state in data types. In Proceedings ofECOOP ’97, 1997.

[America & v. Linden90] Pierre America and Frank v. Linden. A parallelobject-oriented language with inheritance and sub-typing. In Proceedings OOPSLA/ECOOP ’90, ACMSIGPLAN Notices, pages 161–168, October 1990.

[Appel93] Andrew W. Appel. A critique of standard ML. Jour-nal of Functional Programming, 4(3):391–429, October1993.

[Auer94] Ken Auer. Reusability through self-encapsulation.In James O. Coplien and Douglas C. Schmidt, ed-itors, Pattern Languages of Program Design, pages505–516. Addison-Wesley, 1994.

[Backus et al.63] J. W. Backus, F. L. Bauer, J. Green, C. Katz,J. McCarthy, P. Naur, A. J. Perlis, H. Rutishauser,K. Samelson, B. Vauquois, J. H. Wegstein, A. vanWijngaarden, and M. Woodger. Report on the algo-rithmic language ALGOL 60. Communications of theACM, 1(6):1–17, January 1963.

[Backus78] John Backus. Can programming be liberated fromthe von Neumann style? A functional style and itsalgebra of programs. Communications of the ACM,21(8):613–641, August 1978.

[Baker93] Henry G. Baker. Iterators: Signs of weakness inobject-oriented languages. ACM OOPS Messenger,4(3):18–25, July 1993.

[Ballard95] Fred Ballard. Who discovered Alexander? In Pat-terns mailing list. [email protected],October 1995.

[Barendregt84] Hendrik Pieter Barendregt. The Lambda Calculus: ItsSyntax and Semantics, volume 103 of Studies in Logicand the Foundations of Mathematics. Elsevier SciencePublishing Company, 2nd edition, 1984.

Page 295: A Functional Pattern System for Object-Oriented Design

Bibliography 277

[Baumgartner et al.96] Gerald Baumgartner, Konstantin Laufer, and Vin-cent F. Russo. On the interaction of object-orienteddesign patterns and programming languages. Tech-nical Report CSD-TR-96-020, Purdue University,February 1996.

[Beaudouin-Lafon94] Micheal Beaudouin-Lafon. Object-oriented Lan-guages. Chapman and Hall, 1994.

[Beck & Johnson94] Kent Beck and Ralph E. Johnson. Patterns generatearchitectures. In Mario Tokoro and Remo Pareschi,editors, ECOOP ’94, Lecture Notes in Computer Sci-ence, pages 139–149. Springer Verlag, July 1994.

[Beck96] Kent Beck. Method object. In Patternsmailing list Digest, Patterns Digest. [email protected], April 1996.

[Berger91] Emery D. Berger. FP+OOP=Haskell. Technical Re-port TR-92-30, The University of Texas at Austin,Department of Computer Science, December 1991.

[Biddle & Tempero96] Robert Biddle and Ewan Tempero. Understandingthe impact of language features on reusability. Tech-nical Report CS-TR-95/17, Victoria University ofWellington, Department of Computer Science, Jan-uary 1996.

[Bird & de Moor92] R. Bird and O. de Moor. Solving optimisation prob-lems with catamorphisms. In Mathematics of Pro-gram Construction, LNCS 669, pages 45–66. SpringerVerlag, June 1992.

[Bird & Wadler88] Richard Bird and Philip Wadler. Introduction toFunctional Programming. C.A.R. Hoare Series. Pren-tice Hall International, 1988.

[Bird84] Richard S. Bird. Using Circular Programs to Elim-inate Multiple Traversals of Data. Acta Informatica21, pages 239–250, 1984.

[Bird86] Richard S. Bird. An Introduction to the Theory ofLists. Technical Report PRG-56, Oxford University,October 1986.

[Bobrow et al.86a] Daniel G. Bobrow, Ken Kahn, Gregor Kiczales,Larry Masinter, Mark Stefik, and Frank Zdybel.Commonloops: Merging lisp and object-oriented

Page 296: A Functional Pattern System for Object-Oriented Design

278 Bibliography

programming. In Proceedings OOPSLA ’86, pages17–29, November 1986.

[Bobrow et al.86b] Daniel G. Bobrow, Ken Kahn, Gregor Kiczales,Larry Masinter, Mark Stefik, and Frank Zdybel.Commonloops: Merging lisp and object-orientedprogramming. In Proceedings OOPSLA ’86, ACMSIGPLAN Notices, pages 17–29, November 1986.

[Boiten et al.93] Eerke A. Boiten, A. Max Geerling, and Helmut A.Partsch. Transformational derivation of (parallel)programs using skeletons. Technical Report 93-20,Katholieke Universiteit Nijmegen, September 1993.

[Booch94] Grady Booch. Object-Oriented Analysis and Designwith Applications. Benjamin / Cummings Publish-ing Company, 2nd edition, 1994.

[Borland94] Borland. Borland C/C++ 4.0 Reference Manual. Bor-land, Inc., 1994.

[Bosch98] Jan Bosch. Design patterns as language constructs.Journal of Object-Oriented Programming, 11(2), May1998.

[Botorog96] Georg Botorog. Strukturierte parallele Program-mierung zur Losung numerischer Probleme. InJ. Ebert, editor, Workshop: Alternative Konzepte furSprachen und Rechner - Bad Honnef 1995. UniversitatKoblenz-Landau, Universitat Koblenz-Landau, In-stitut fur Informatik, Rheinau 1, D-56075 Koblenz,May 1996.

[Braine94] Lee Braine. Integrating functional and object-oriented paradigms. Technical report, UniversityCollege London, Gower Street, London WC1E6BT,UK, December 1994.

[Braine95] Lee Braine. The importance of being lazy, June 1995.

[Breuel88] Thomas M. Breuel. Lexical closures for C++. In C++Conference Proceedings, pages 293–304, October 1988.

[Bruce94] Kim B. Bruce. A paradigmatic object-oriented pro-gramming language: Design, static typing and se-mantics. Journal of Functional Programming, 4:127–206, April 1994.

Page 297: A Functional Pattern System for Object-Oriented Design

Bibliography 279

[Buckley95] Bob Buckley. Common subexpression in parame-ter patterns. 5380 of comp.lang.functional, August1995.

[Budd95] Timothy Budd. Multiparadigm Programming in Leda.Addison-Wesley, 1995.

[Burstall & Darlington77] R. M. Burstall and J. Darlington. A transformationsystem for developing recursive programs. Journalof the ACM, 24(1), 1977.

[Burstall et al.80] Ron M. Burstall, D. B. MacQueen, and D. T. San-nella. HOPE: An experimental applicative lan-guage. In Proceedings, 1980 LISP Conference, pages136–143, August 1980.

[Burton & Cameron94] F. Warren Burton and Robert D. Cameron. Patternmatching with abstract data types. Journal of Func-tional Programing, 3(2):171–190, 1994.

[Buschmann et al.96] Frank Buschmann, Regine Meunier, Hans Rohn-ert, Peter Sommerlad, and Michael Stal. Pattern-Oriented Software Architecture — A System of Pat-terns. John Wiley & Sons, July 1996.

[Byte94] Component software. Byte Magazin, May 1994.

[Cann92] David Cann. Retire Fortran? A debate rekindled.Communications of the ACM, 35(8):81–89, August1992.

[Cardelli & Wegner85] Luca Cardelli and Peter Wegner. On understandingtypes, data abstraction, and polymorphism. ACMComputing Surveys, 17(4):471–522, December 1985.

[Cardelli84] Luca Cardelli. A semantics of multiple inheritance.In Semantics of Data Types, volume 173 of LNCS,pages 51–67. Springer Verlag, 1984.

[Carlsson & Hallgren93] M. Carlsson and T. Hallgren. Chalmers University,May 1993.

[Carre & Geib90] Bernard Carre and Jean-Marc Geib. The point ofview notion for multiple inheritance. In ProceedingsOOPSLA/ECOOP ’90, pages 312–321, October 1990.

[Centaur92] Centaur documentation, INRIA, 1992.

Page 298: A Functional Pattern System for Object-Oriented Design

280 Bibliography

[Chambers & Leavens95] Craig Chambers and Gary T. Leavens. Typecheck-ing and modules for multi-methods. TOPLAS,17(6):805–843, November 1995.

[Chambers92a] Craig Chambers. The design and implementation ofthe SELF compiler, an optimizing compiler for object-oriented programming languages. PhD thesis, Stan-ford University, March 1992.

[Chambers92b] Craig Chambers. Object-oriented multi-methods incecil. In O. Lehrmann Madsen, editor, ProceedingsECOOP ’92, LNCS 615, pages 33–56, Utrecht, TheNetherlands, Springer-Verlag, June 1992.

[Chiba & Masuda93] Shigeru Chiba and Takashi Masuda. Designing anextensible distributed language with a meta-levelarchitecture. In Oscar M. Nierstrasz, editor, ECOOP’93, Lecture Notes in Computer Science 707, pages482–501, July 1993.

[Clack & Myers95] C. Clack and C. Myers. The dys-functional stu-dent. In P. H. Hartel and M. J. Plasmeijer, edi-tors, Functional programming languages in education(FPLE), LNCS 1022, pages 289–309, Nijmegen, TheNetherlands, Springer-Verlag, Dec 1995.

[Cleeland et al.96] Chris Cleeland, Douglas C. Schmidt, and Timo-thy H. Harrison. External polymorphism — An ob-ject structural pattern for transparently extendingC++ concrete data types. In Preliminary Proceedingsof PLoP ’96, 1996.

[Clinger & Hansen92] W. Clinger and L. T. Hansen. Is explicit deallocationreally faster than garbage collection? 1992.

[Cole88] M. I. Cole. A ‘skeletal’ approach to the exploitationof parallel computation. In CONPAR 88, pages 100–107. British Computer Society, London, UK, 1988.

[Cole89] M. I. Cole. Algorithmic Skeletons: Structured Manage-ment of Parallel Computation. Pitman, London, UK,1989.

[Coleman et al.94] Derek Coleman et al. Object-oriented development:The Fusion Method. Object-oriented series. PrenticeHall, 1994.

Page 299: A Functional Pattern System for Object-Oriented Design

Bibliography 281

[Convex93] Convex AVS developer’s guide. Convex ComputerCorporation, first edition, Richardson, Texas, De-cember 1993.

[Cook89a] W. Cook. A Denotational Semantics of Inheritance.PhD thesis, Brown University, May 1989.

[Cook89b] W. R. Cook. A proposal for making Eiffel type-safe.The Computer Journal, 32(4):305–311, August 1989.

[Cook90] William R. Cook. Object-oriented programmingversus abstract data types. In J.W. de Bakker, W.P.de Roever, and G. Rozenberg, editors, Foundationsof Object-Oriented Languages, volume 489 of LNCS,pages 151–178. Springer, May 1990.

[Coplien92] James O. Coplien. Advanced C++: ProgrammingStyles and Idioms. Addison-Wesley, 1992.

[Coplien95] James O. Coplien. Multi-paradigm design and im-plementation. Tutorial Notes from the SummerSchool on Object Orientation in Tampere, Finland,AT&T Bell Laboratories, Naperville, Illinois, USA,August 1995.

[Coplien96a] James O. Coplien. The column without a name: Thehuman side of patterns. C++ Report, 8(1):81–85, Jan-uary 1996.

[Coplien96b] James O. Coplien. Pattern definition. In Pat-terns mailing list. [email protected],December 1996.

[Cunningham94] Ward Cunningham. The checks pattern language ofinformation integrity. In James O. Coplien and Dou-glas C. Schmidt, editors, Pattern Languages of Pro-gram Design, pages 145–156. Addison-Wesley, 1994.

[Dahl & Nygaard81] Ole-Johan Dahl and Kristen Nygaard. The devel-opment of the SIMULA language. In Richard L.Wexelblat, editor, ACM Conference on the History ofProgramming Languages, pages 439–493. AcademicPress, 1981.

[Dami94] Laurent Dami. Software Composition: Towards anIntegration of Functional and Object-Oriented Ap-proaches. PhD thesis, University of Geneva, April1994.

Page 300: A Functional Pattern System for Object-Oriented Design

282 Bibliography

[Darlington et al.91] J. Darlington, A. Field, P. G. Harrison, D. Harper,G. K. Jouret, P. Kelly, K. Sephton, and D. Sharp.Structured parallel functional programming. InH. Glaser and P. H. Hartel, editors, Proceedings of theWorkshop on the Parallel Implementation of FunctionalLanguages, pages 31–52, Southampton, UK, Depart-ment of Electronics and Computer Science, Univer-sity of Southampton, 1991.

[Darlington et al.95] John Darlington, Yi-ke Guo, Hing Wing To, and JinYang. Parallel skeletons for structured composition.In Proc. 5th ACM SIGPLAN Symposium on Princi-ples and Practice of Parallel Programming, PPoPP’95,pages 19–28, Santa Barbara, California, July 1995.

[Davies & Pfenning96] Rowan Davies and Frank Pfenning. A modal anal-ysis of staged computation. Proceedings of 23rd An-nual ACM Symposium on Principles of ProgrammingLanguages, January 1996.

[Deegener et al.94] M. Deegener, G. Große, B. Kuhnapfel, and H. Wirth.M uSE — Multimediale Systementwicklung. InI. Toch, G. Kampe, H. Ecker, and F. Breitenecker,editors, ASIM-Arbeitskreistreffen Simulation Technis-cher Systeme und Simulationsmethoden und Sprachenfur parallele Prozesse (Wien, 31.1.-1.2. 1994). ASIM,1994.

[DeMichiel & Gabriel87] Linda G. DeMichiel and Richard P. Gabriel. Thecommon lisp object system: An overview. InJ. Bezivin, J-M. Hullot, P. Cointe, and H. Lieber-mann, editors, Proceedings ECOOP ’87, LNCS 276,pages 151–170. Springer Verlag, June 1987.

[Despeyroux88] Thierry Despeyroux. Typol: a formalism to imple-ment natural semantics. Technical report, INRIA re-search report 94, 1988.

[Dijkstra76] Edsgar W. Dijkstra. A Discipline of Programming.Prentice Hall, Englewood Cliffs, N.J., 1976.

[Dosch95] Walter Dosch. Personal communication. WorkshopAlternative Konzepte fur Sprachen und Rechner inBad Honnef, May 1995.

[Duke95] Roger Duke. Do formal object-oriented methodshave a future? In Christine Mingins and Bertrand

Page 301: A Functional Pattern System for Object-Oriented Design

Bibliography 283

Meyer, editors, Technology of Object-Oriented Lan-guages and Systems: TOOLS 15, pages 273–280, Pren-tice Hall International, London, 1995.

[Dyson & Anderson96] Paul Dyson and Bruce Anderson. State patterns. InThe 1st Annual European Conference on Pattern Lan-guages of Programming, EuroPLoP ’96, Kloster Irsee,Germany, 1996.

[Ebert87] Jurgen Ebert. Funktionale Programmiersprachen:Ein Uberblick. Manuskript, 1987.

[Eckert & Kempe94] Gabriel Eckert and Magnus Kempe. Modeling withobjects and values: Issues and perspectives, August1994.

[Edelson92] Daniel R. Edelson. Smart pointers: They’re smart,but they’re not pointers. Technical Report UCSC-CRL-92-27, Baskin Center for Computer Engineer-ing and Information Sciences, University of Califor-nia, Santa Cruz, CA 95064, USA, June 1992.

[Edwards95] Stephen Edwards. Streams: A pattern for “pull-driven” processing. In J. O. Coplien and D. C.Schmidt, editors, Pattern Languages of Program De-sign, pages 417–426. Addison-Wesley, 1995.

[Ellis & Stroustrup90] M. Ellis and B. Stroustrup. The Annotated C++ Ref-erence Manual. Addison-Wesley, 1990.

[Feiler & Tichy97] Peter H. Feiler and Walter F. Tichy. Propagator —a family of patterns. In The 23rd TOOLS conferenceUSA ’97, St. Barbara, California, July 1997.

[Felleisen91] Matthias Felleisen. On the expressive power of pro-gramming languages. In Science of Computer Pro-gramming, volume 17, pages 35–75, 1991.

[Field & Harrison88] Anthony J. Field and Peter G. Harrison. FunctionalProgramming. Addison Wesley, Reading, MA, 1988.

[Fisher & Mitchel95] Kathleen Fisher and John C. Mitchel. The develop-ment of type systems for object-oriented languages.November 1995.

[Floyd64] R. W. Floyd. Treesort (algorithm 113). Communica-tions of ACM, December 1964.

Page 302: A Functional Pattern System for Object-Oriented Design

284 Bibliography

[Floyd87] R. W. Floyd. The paradigms of programming. ACMTuring Award Lectures: The First Twenty Years, 1987.

[Freddo96] Freddo. The carving of dragons. In Patterns mailinglist. [email protected], June 1996.

[Friedman & Wise76] D. Friedman and D. Wise. CONS should not eval-uate its arguments. In S. Michaelson and R. Mil-ner, editors, Automata, Languages and Programming,pages 257–284. Edingburgh University Press, 1976.

[Gabriel96] Richard Gabriel. Pattern definition. In Patterns mail-ing list. [email protected], December1996.

[Gamma et al.93] Erich Gamma, Richard Helm, John Vlissides, andRalph E. Johnson. Design patterns: Abstraction andreuse of object-oriented design. In O. Nierstrasz,editor, Proceedings ECOOP ’93, LNCS 707, pages406–431, Kaiserslautern, Germany, Springer-Verlag,July 1993.

[Gamma et al.94] Erich Gamma, Richard Helm, Ralph E. Johnson,and John Vlissides. Design Patterns: Elementsof Object-Oriented Software Architecture. Addison-Wesley, 1994.

[Gamma91] Erich Gamma. Object-Oriented Software Developmentbased on ET++: Design Patterns, Class Library, Tools(in German). PhD thesis, University of Zurich, 1991.

[Gamma95] Erich Gamma. Singular object. In Pat-terns mailing list Digest, Patterns Digest. [email protected], March 1995.

[Gamma97] Erich Gamma. The facet pattern. In Robert C.Martin, Dirk Riehle, and Frank Buschmann, edi-tors, Pattern Languages of Program Design 3, Reading,Massachusetts, Addison-Wesley, 1997.

[Gibbons & Jones93] Jeremy Gibbons and Geraint Jones. Linear-timebreadth-first tree algorithms: An exercise in thearithmetic of folds and zips. Technical Report 71,University of Auckland, Department of ComputerScience, 1993.

[Gill et al.93] Andy Gill, John Launchbury, and Simon L. PeytonJones. A short cut to deforestation. In Functional

Page 303: A Functional Pattern System for Object-Oriented Design

Bibliography 285

Programming Languages and Computer Architecture,Copenhagen ’93, April 1993.

[Goldberg & Robson83] Adele Goldberg and David Robson. Smalltalk-80: The Language and its Implementation. Addison-Wesley, Reading, MA, 1983.

[Gordon93a] Andrew D. Gordon. Functional Programming and In-put/Output. PhD thesis, University of CambridgeComputer Laboratory, February 1993.

[Gordon93b] Andrew D. Gordon. An operational semantics forI/O in a lazy functional language. In FPCA’93:Conference on Functional Programming Languages andComputer Architecture, Copenhagen. ACM, 1993.

[Gostanza et al.96] Pedro Palao Gostanza, Ricardo Pena, and ManuelNunez. A new look at pattern matching in abstractdata types. Conference on Functional Programming,31(6):110–121, June 1996.

[Grant-Duff94] Zully Grant-Duff. Skeletons, list homomorphismsand parallel program transformation. Technical Re-port Internal Report 94/14, Department of Comput-ing, Imperial College, July 94.

[Griss95] Martin Griss. Systematic software reuse – objectsand frameworks are not enough (panel). ACM SIG-PLAN Notices, 30(10):281–282, October 1995.

[Grosch & Snelting93] Franz-Josef Grosch and Gregor Snelting. Polymor-phic components for monomorphic languages. InProceedings of the Second International Workshop onSoftware Reusability, pages 47–55. IEEE Comp. Soc.Press, March 1993.

[Hankin et al.97] Chris Hankin, Hanne Riis Nielson, and Jens Pals-berg. Position statements on strategic directions forresearch on programming languages. ACM SIG-PLAN NOTICES, 32(1):59–65, January 1997.

[Hankin94] Chris Hankin. Lambda Calculi, A Guide for Com-puter Scientists. Graduate Texts in Computer Sci-ence. Clarendon Press, Oxford, 1994.

[Harkavy & et al.94] Michael Harkavy and et al., editors. Webster’s newencyclopedic dictionary. Black Dog & Leventhal pub-lishers Inc., 151 West 19th Street, New York 10011,1994.

Page 304: A Functional Pattern System for Object-Oriented Design

286 Bibliography

[Harms & Zabinski77] Edward Harms and Michael P. Zabinski. Introduc-tion to APL and computer programming. Wiley, 1977.

[Harris97] Dave Harris. Aliasing in Smalltalk. In Java Wiki Web.Hillside group, 1997.

[Harrison & Ossher93] William Harrison and Harold Ossher. Subject-oriented programming (A critique of pure objects).In Proceedings OOPSLA ’93, ACM SIGPLAN Notices,pages 411–428, October 1993.

[Harrison et al.94] R. Harrison, L. G. Samaraweera, M. R. Dobie, andP. H. Lewis. Comparing programming paradigms:An evaluation of functional and object-orientedprogams. Technical Report SO171BJ, Universityof Southhampton, Dept. of Electronincs and Com-puter Science, UK, August 1994.

[Hebel & Johnson90] Kurt J. Hebel and Ralph E. Johnson. Arithmeticand double dispatching in Smalltalk-80. Journalof Object-Oriented Programming, 2(6):40–44, March1990.

[Henderson & Constantin91] Brian Henderson and L. L. Constantin. Object-oriented development and functional decomposi-tion. Journal of Object-Oriented Programming, 3(5):11–17, January 1991.

[Henhapl et al.91] W. Henhapl, W. Bibel, J.L. Encarnacao, S. Huss,and E. Neuhold. MUSE – Multimediale Systemen-twicklung (DFG-Forschergruppenantrag). TechnischeHochschule Darmstadt, February 1991.

[Hewitt77] Carl Hewitt. Viewing control structures as patternsof passing messages. Artificial Intelligence, 8:323–364, 1977.

[Hillegass93] Aaron Hillegass. The design of the Eiffel boochcomponents. Eiffel Outlook, 3(3):20–21, December1993.

[Hirschfeld96] Robert Hirschfeld. Convenience methods. In The 1st

Annual European Conference on Pattern Languages ofProgramming, EuroPLoP ’96, Kloster Irsee, Germany,July 1996.

[Hogg et al.92] John Hogg, Doug Lea, Alan Wills, Dennis deCham-peaux, and Richard Holt. The geneva convention

Page 305: A Functional Pattern System for Object-Oriented Design

Bibliography 287

on the treatment of object aliasing. ACM OOPS Mes-senger, 2(3):11–16, April 1992.

[Hogg91] John Hogg. Islands: Aliasing protection in object-oriented languages. In Proceedings OOPSLA ’91,pages 271–285, November 1991.

[Hudak & Bloss85] Paul Hudak and Adriene Bloss. The aggregateupdate problem in functional programming lan-guages. In Conference Record of the Twelfth ACMSymposium on Principles of Programming Languages,pages 300–314. ACM, January 1985.

[Hudak & Fasel92] Paul Hudak and Joseph Fasel. A gentle introduc-tion to Haskell. ACM SIGPLAN Notices, 27(5):Sec-tion T, May 1992.

[Hudak & Sundaresh88] Paul Hudak and Raman S. Sundaresh. On the ex-pressiveness of purely functional i/o systems. Tech-nical Report YALEU/DCS/RR-665, Yale UniversityDepartment of Computer Science, December 1988.

[Hudak89] Paul Hudak. Conception, evolution, and applica-tion of functional programming languages. ACMComputing Surveys, 21(3):361–411, September 1989.

[Hudak92] Paul Hudak. Mutable abstract datatypes. ResearchReport YALEU/DCS/RR-914, Yale University De-partment of Computer Science, December 1992.

[Hudak96] Paul Hudak. Building domain-specific embeddedlanguages. Computing Surveys, 28(4), December1996.

[Hughes85] John Hughes. Lazy memo-functions. In FunctionalProgramming Languages and Computer Architecture,LNCS 201, pages 129–146. Springer, 1985.

[Hughes87] John Hughes. Why functional programming mat-ters. In David A. Turner, editor, Research Topicsin Functional Programming, pages 17–42. Addison-Wesley, August 1987.

[Hunt & Szymanski77] J.W. Hunt and T.G. Szymanski. A fast algorithm forcomputing longest common subsequences. Commu-nications of the ACM, 20:350–353, 1977.

Page 306: A Functional Pattern System for Object-Oriented Design

288 Bibliography

[Hutton92] Graham Hutton. Higher-order functions for pars-ing. Journal of Functional Programming, 2(3):323–429,July 1992.

[Ingalls86] Daniel H. H. Ingalls. A simple technique for han-dling multiple polymorphism. In Proceedings OOP-SLA ’86, pages 347–349, November 1986.

[Jacobs & Rutten97] B. Jacobs and J. Rutten. A tutorial on (co)algebrasand (co)induction. In EATCS Bulletin, 1997.

[Jacobs98] Bart Jacobs. Coalgebraic reasoning about classesin object-oriented languages. In Electronic Notesin Computer Science 11, Special issue on the work-shop Coalgebraic Methods in Computer Science (CMCS1998), 1998.

[Jacobson et al.94] Ivar Jacobson et al. Object-Oriented Software Engi-neering: A use case driven approach. Addison Wesley,4th edition, 1994.

[Jager et al.88] M. Jager, M. Gloger, and S. Kaes. Sampλe — a func-tional language. LNCS 328, 1988.

[Jager et al.91] P. Jager, W. Jonker, A. Wammes, and J. Wester. Onthe generation of interactive programming environ-ments. ESPRIT project 2177: GIPE II, 1991.

[Jazayeri95] Mehdi Jazayeri. Component programming – a freshlook at software components. In Proceedings of the5th European Software Engineering Conference, Sitges,Spain, September 1995.

[Jeschke95] Eric Jeschke. Speed of fp languages, laziness, ”de-lays”. News article 5745 of comp.lang.functional,November 1995.

[Jezequel96] J.-M. Jezequel. Object Oriented Software Engineeringwith Eiffel. Addison-Wesley, March 1996.

[Johnson & Foote88] Ralph E. Johnson and Brian Foote. Designingreusable classes. Journal of Object-Oriented Program-ming, 1(2):22–35, June 1988.

[Johnson92] Ralph E. Johnson. Documenting frameworks us-ing patterns. ACM SIGPLAN Notices, OOPSLA ’92,27(10):63–76, October 1992.

Page 307: A Functional Pattern System for Object-Oriented Design

Bibliography 289

[Johnson94] Ralph E. Johnson. How to develop frameworks. InECOOP ’94 Tutorial Documentation, July 1994.

[Jones & Wadler93] Simon Peyton Jones and Philip Wadler. Imperativefunctional programming. Proceedings of 20th AnnualACM Symposium on Principles of Programming Lan-guages, 4:71–84, January 1993.

[Jones96] Capers Jones. Programming languages ta-ble. Software Productivity Research, Release 8.2,http://www.spr.com/library/langtbl.htm, March 1996.

[Kashiwagi & Wise91] Yugo Kashiwagi and David S. Wise. Graph algo-rithms in a lazy functional programming language.In 4th Int. Symp. on Lucid and Intensional Program-ming, pages 35–46, 1991.

[Kay96] Alan Kay. The early history of Smalltalk. InThomas J. Bergin and Richard G. Gibson, editors,History of Programming Languages 2, pages 511–578.Addison-Wesley, 1996.

[Keller & Sleep86] Robert M. Keller and M. Ronan Sleep. Applica-tive caching. ACM Transactions on Programming Lan-guages and Systems, 8(1):88–108, January 1986.

[Kent & Howse96] Stuart Kent and John Howse. Value types in Eiffel.In The 19th TOOLS conference Europe 96, Paris, 1996.

[Kent78] W. Kent. Data and reality: Basic assumptions indata processing reconsidered. North-Holland Pub-lishing Company, 1978.

[Kershenbaum et al.88] Aaron Kershenbaum, David Musser, and Alexan-der Stepanov. Higher-order imperative program-ming. Technical Report 88–10, Rensselaer Polytech-nic Institute Computer Science Department, April1988.

[Khoshafian & Copeland86] Setrag N. Khoshafian and George P. Copeland. Ob-ject identity. In Proceedings OOPSLA ’86, pages 406–416, November 1986.

[Kieburtz & Lewis95] Richard B. Kieburtz and Jeffrey Lewis. Program-ming with algebras. In Advanced Functional Pro-gramming, number 925 in Lecture Notes in Com-puter Science, pages 267–307. Springer, 1995.

Page 308: A Functional Pattern System for Object-Oriented Design

290 Bibliography

[King & Launchbury93] David J. King and John Launchbury. Lazy depth-first search and linear graph algorithms in Haskell.In John T. O’ Donnell and Kevin Hammond,editors, Glasgow functional programming workshop,pages 145–155, Ayr, Scotland, Springer-Verlag,1993.

[Klagges93] Henrik Anthony Klagges. A functional languageinterpreter integrated into the C++ language sys-tem. Master’s thesis, Balliol College, University ofOxford, Oxford Univerity Computing Laboratory,11 Keble Road, Oxford OX1 3QD, United Kingdom,September 1993.

[Knuth74] Donald E. Knuth. Structured programming with goto statments. Computing Survyes, 6(4):261–301, De-cember 1974.

[Kofler93] Thomas Kofler. Robust iterators for ET++. Struc-tured Programming, 14(2):62–85, 1993.

[Kozato & Otto93] Y. Kozato and G. Otto. Benchmarking real-life im-age processing programs in lazy functional lan-guages. In FPCA’93: Conference on FunctionalProgramming Languages and Computer Architecture,Copenhagen. ACM, 1993.

[Krishnamurthi et al.98] Shriram Krishnamurthi, Matthias Felleisen, andDaniel P. Friedman. Synthesizing object-orientedand functional design to promote re-use. In ECOOP’98, to be published 1998.

[Kuhn70] Thomas Kuhn. The Structure of Scientific Revolutions.University of Chicago, Chicago, 2nd edition, 1970.

[Kuhnapfel & Große94] B. Kuhnapfel and G. Große. Specification of Techni-cal Systems by a Combination of Logical and Func-tional Languages. In Hendrik C. R. Lock, edi-tor, 3rd Workshop on Functional Logic Programming(Schwarzenberg, 10.-14.1. 1994), 1994.

[Kuhnapfel93] B. Kuhnapfel. Kombinatorsysteme zur Beschreibungparalleler Prozesse in funktionalen Sprachen. Diplo-marbeit am Fachbereich Informatik. TechnischeHochschule Darmstadt, March 1993.

Page 309: A Functional Pattern System for Object-Oriented Design

Bibliography 291

[Kuhne94] Thomas Kuhne. Higher order objects in pureobject-oriented languages. ACM SIGPLAN Notices,29(7):15–20, July 1994.

[Kuhne95a] Thomas Kuhne. Higher order objects in pure object-oriented languages. ACM OOPS Messenger, 6(1):1–6, January 1995.

[Kuhne95b] Thomas Kuhne. Parameterization versus inheri-tance. In Christine Mingins and Bertrand Meyer,editors, Technology of Object-Oriented Languages andSystems: TOOLS 15, pages 235–245, Prentice Hall In-ternational, London, 1995.

[Kuhne96a] Thomas Kuhne. Can object-orientation liberateprogramming from the von Neumann style? InJ. Ebert, editor, Workshop: Alternative Konzepte furSprachen und Rechner - Bad Honnef 1995. UniversitatKoblenz-Landau, Universitat Koblenz-Landau, In-stitut fur Informatik, Rheinau 1, D-56075 Koblenz,May 1996.

[Kuhne96b] Thomas Kuhne. Nil and none considered nulland void. In Alexander V. Smolyaninov andAlexei S. Shestialtynov, editors, Conference Proceed-ings of WOON’96 in Saint Petersburg, pages 143–154,June 1996.

[Kuhne96c] Thomas Kuhne. Recipes to reuse. In The 1st AnnualEuropean Conference on Pattern Languages of Program-ming, EuroPLoP ’96, Kloster Irsee, Germany, July1996.

[Kuhne97] Thomas Kuhne. The function object pattern. C++Report, 9(9):32–42, October 1997.

[Kuhne98] Thomas Kuhne. The translator pattern — exter-nal functionality with homomorphic mappings. InRaimund Ege, Madhu Sing, and Bertrand Meyer,editors, The 23rd TOOLS conference USA ’97, pages48–62. IEEE Computer Society, July 1998.

[LaLonde & Pugh91] Wilf LaLonde and John Pugh. Subclassing 6= Sub-typing 6= Is-a. Journal of Object-Oriented Program-ming, 3(5):57–62, January 1991.

[LaLonde94] Wilf LaLonde. Discovering Smalltalk. Benjamin /Cummings Publishing Company, 1994.

Page 310: A Functional Pattern System for Object-Oriented Design

292 Bibliography

[Landin65] P. J. Landin. A correspondence between ALGOL 60and Church’s lambda notation. Communications ofthe ACM, 8(2):89–101, February 1965.

[Lang & Pearlmutter86] Kevin J. Lang and Barak A. Pearlmutter. Oaklisp:an object-oriented scheme with first class types. InProceedings OOPSLA ’86, ACM SIGPLAN Notices,pages 30–37, November 1986.

[Laufer95] Konstantin Laufer. A framework for higher-orderfunctions in C++. In Proc. Conf. Object-OrientedTechnologies (COOTS), Monterey, CA, USENIX, June1995.

[Laufer96] K. Laufer. Type classes with existential types. Jour-nal of Functional Programming, 6(3):485–517, May1996.

[Launchbury & Jones94] John Launchbury and Simon L. Peyton Jones. Lazyfunctional state threads. SIGPLAN Notices, 29(6):24–35, June 1994.

[Launchbury & Jones95] John Launchbury and Simon L. Peyton Jones. Statein Haskell. Lisp and Symbolic Computation, 8(4):293–341, December 1995.

[Launchbury93] John Launchbury. Lazy imperative programming.In ACM SIGPLAN Workshop on State in Prog. Langs.University of Copenhagen, June 1993.

[Leavens94] Gary T. Leavens. Fields in physics are like curriedfunctions or Physics for functional programmers.Technical Report TR #94-06b, Department of Com-puter Science, Iowa State University, 229 AtanasoffHall, May 1994.

[Limberghen & Mens94] Marc Van Limberghen and Tom Mens. Encapsu-lation and composition as orthogonal operators onmixins: A solution to multiple inheritance prob-lems. Technical Report vub-prog-tr-94-10, Depart-ment of Computer Science, Vrije Universiteit Brus-sel, Belgium, September 1994.

[Liskov & Guttag86] Barbara Liskov and John Guttag. Abstraction andSpecification in Programm Development. MIT Press,1986.

Page 311: A Functional Pattern System for Object-Oriented Design

Bibliography 293

[Liskov & Wing93] Barbara Liskov and Jeannette M. Wing. A new def-inition of the subtype relation. In O. Nierstrasz, ed-itor, Proceedings ECOOP ’93, LNCS 707, pages 118–141, Kaiserslautern, Germany, Springer-Verlag, July1993.

[MacLennan82] B. J. MacLennan. Values and objects in program-ming languages. SIGPLAN Notices, 17(12):70–79,December 1982.

[Madsen et al.93] Ole L. Madsen, Kristen Nygaard, and BirgerMoller-Pedersen. Object-Oriented Programming inthe BETA Programming Language. Addison-Wesleyand ACM Press, 1993.

[Manolescu97] Dragos-Anton Manolescu. A data flow pattern lan-guage. In The 4th Annual Conference on Pattern Lan-guages of Programming, Monticello, Illinois, 1997.

[Martin94] Robert Martin. Discovering patterns in existing ap-plications. In James O. Coplien and Douglas C.Schmidt, editors, Pattern Languages of Program De-sign, pages 365–393. Addison-Wesley, 1994.

[Martin97] Robert C. Martin. Acyclic visitor. In Robert C.Martin, Dirk Riehle, and Frank Buschmann, edi-tors, Pattern Languages of Program Design 3, Reading,Massachusetts, Addison-Wesley, 1997.

[Maughan96] Glenn Maughan. Restricting access to data struc-tures? comp.lang.eiffel, February 1996.

[Meijer & Jeuring95] Erik Meijer and Johan Jeuring. Merging monadsand folds for functional programming. In Ad-vanced Functional Programming, number 925 in Lec-ture Notes in Computer Science, pages 228–266.Springer, 1995.

[Meijer et al.91] E. Meijer, M. Fokkinga, and R. Paterson. Functionalprogramming with bananas, lenses, envelopes andbarbed wire. In Proceedings of the 5th ACM Confer-ence on Functional Programming Languages and Coom-puter Architecture, Cambridge, Massachusetts, LNCS523, pages 124–144. Springer Verlag, August 1991.

[Mens et al.94] Tom Mens, Kim Mens, and Patrick Steyaert.Opus: a formal approach to object-orientation. InTim Denvir Maurice Naftalin and Miquel Bertran,

Page 312: A Functional Pattern System for Object-Oriented Design

294 Bibliography

editors, FME’ 94 Proceedings: Industrial Benefit of For-mal Methods, volume 873 of Lecture Notes in Com-puter Science. Springer-Verlag, 1994.

[Mens et al.95] Tom Mens, Kim Mens, and Patrick Steyaert. Opus:A calculus for modelling object-oriented concepts.In D. Patel, Y. Sun, and S. Patel, editors, OOIS’ 94Proceedings: International Conference on Object Ori-ented Information Systems. Springer-Verlag, 1995.

[Menzies & Haynes95] Tim Menzies and Philip Haynes. The methodologyof methodologies, or , evaluating current method-ologies: Why and how. In Christine Minginsand Bertrand Meyer, editors, Technology of Object-Oriented Languages and Systems: TOOLS 15, pages83–92, Prentice Hall International, London, 1995.

[Meunier95a] Jeffrey A. Meunier. Functional programming. Tech-nical report, University of Connecticut, December1995.

[Meunier95b] Regine Meunier. The pipes and filters architecture.In J. O. Coplien and D. C. Schmidt, editors, Pat-tern Languages of Program Design, pages 427–440.Addison-Wesley, 1995.

[Meyer88] Bertrand Meyer. Object-Oriented Software Construc-tion. Prentice Hall, Englewood Cliffs, NJ, 1988.

[Meyer92] Bertrand Meyer. EIFFEL the language. Prentice Hall,Object-Oriented Series, 1992.

[Meyer93] Bertrand Meyer. Toward an object-oriented curricu-lum. JOOP: Education & Training, May 1993.

[Meyer94a] Bertrand Meyer. Beyond design by contract. Invitedpresentation at TOOLS 15, December 1994.

[Meyer94b] Bertrand Meyer. Reusable Software. Prentice Hall,1994.

[Meyer95] Bertrand Meyer. The future of Eiffel’s type system.Personal communication, March 1995.

[Meyer96] Bertrand Meyer. Static typing and other mysteriesof life. Object Currents, 1(1), January 1996.

Page 313: A Functional Pattern System for Object-Oriented Design

Bibliography 295

[Meyer97] Bertrand Meyer. Object-Oriented Software Construc-tion. Prentice Hall, Upper Saddle River, NJ 07458,2nd edition, 1997.

[Meyrowitz87] Norman Meyrowitz, editor. Proceedings OOPSLA’87, Orlando, Florida, December 1987.

[Milner et al.90] Robin Milner, M. Tofte, and R. Harper. The definitionof standard ML. MIT Press, Cambridge, 1990.

[Minsky95] Naftaly H. Minsky. Unshareable dynamic objects– how to resolve a conflict between encapsulationand pointers. Eiffel Outlook, 4(5):4–12, June 1995.

[Minsky96] Naftaly H. Minsky. Towards alias-free pointers. InO. Lehrmann Madsen, editor, Proceedings ECOOP’96, LNCS 1098, pages 189–209. Springer-Verlag,June 1996.

[Mogensen95] Torben Ægidius Mogensen. How useful is laziness?News article of comp.lang.functional, August 1995.

[Moynihan94] Tony Moynihan. Objects versus functions in user-validation of requirements: Which paradigm worksbest? In D. Patel, Y. Sun, and S. Patel, editors,OOIS ’94 Proceedings, 1994 International Conferenceon Object Oriented Information Systems, pages 54–73,Dublin City University, Ireland, Springer-Verlag,London, 1995, ISBN 3-540-19927-6, 1994.

[Mugridge et al.91] Warwick B. Mugridge, John Hamer, and John G.Hosking. Multi-methods in a statically-typed pro-gramming language. In P. America, editor, Proceed-ings ECOOP ’91, LNCS 512, pages 307–324, Geneva,Switzerland, Springer-Verlag, July 1991.

[Murer et al.93a] Stephan Murer, Stephen Omohundro, and ClemensSzypersky. Engineering a programming language:The type and class system of Sather. In JoergGutknecht, editor, Programming Languages and Sys-tem Architectures, pages 208–227. Springer Verlag,Lecture Notes in Computer Science 782, November1993.

[Murer et al.93b] Stephan Murer, Stephen Omohundro, and ClemensSzypersky. Sather Iters: Object-oriented itera-tion abstraction. Technical Report TR-93-045, ICSI,Berkeley, August 1993.

Page 314: A Functional Pattern System for Object-Oriented Design

296 Bibliography

[Naur69] Peter Naur, editor. Meeting notes of the NATO Confer-ence in Garmisch. NATO Science Community, 1969.

[Nelson91] Michael L. Nelson. An object-oriented tower of ba-bel. ACM OOPS Messenger, 2(3):3–11, July 1991.

[Neumann86] P.G. Neumann. Risks to the public in computer sys-tems. ACM Software Engineering Notes 11, pages 3–28, 1986.

[Nielson & Nielson93] Hanne Riis Nielson and Flemming Nielson. Seman-tics with Applications: A Formal Introduction. Wiley,1993.

[Nierstrasz & Meijler95] Oscar Nierstrasz and Theo Dirk Meijler. Researchdirections in software composition. ACM Comput-ing Surveys, 27(2):262–264, June 1995.

[Nierstrasz89] Oscar Nierstrasz. A survey of object-oriented con-cepts. In W. Kim and F. Lochovsky, editors, Object-Oriented Concepts, Databases and Applications, pages3–21. ACM Press and Addison-Wesley, 1989.

[Nordberg96] Martin E. Nordberg. Variations on the visitor pat-tern. In Preliminary Proceedings of PLoP ’96, 1996.

[Norvig96] Peter Norvig. Design patterns in dynamic program-ming. Presentation at Object World ’96, May 1996.

[ObjectExpert97] Subjectivity — a new way to solve your biggest pro-gramming challenges. Object Expert issue on Sub-jectivity, SIGS Publications, March 1997.

[Odersky & Wadler97] Martin Odersky and Philip Wadler. Pizza into Java:Translating theory into practice. In Proc. 24th ACMSymposium on Principles of Programming Languages,January 1997.

[Odersky91] Martin Odersky. How to make destructive updatesless destructive. In Conference Record of the Eigh-teenth Annual ACM Symposium on Principles of Pro-gramming Languages, Orlando, Florida, pages 25–26.ACM Press, January 1991.

[Okasaki95a] Chris Okasaki. Lazy functional sorting. Article 5349of comp.lang.functional, September 1995.

Page 315: A Functional Pattern System for Object-Oriented Design

Bibliography 297

[Okasaki95b] Chris Okasaki. Re: Common subexpres-sion in parameter patterns. Article 5385 ofcomp.lang.functional, August 1995.

[Okasaki95c] Chris Okasaki. Simple and efficient purely func-tional queues and deques. Journal of Functional Pro-gramming, 5(4):583–592, October 1995.

[Omohundro & Lim92] Stephen Omohundro and Chu-Cheow Lim. Thesather language and libraries. Technical Report TR-92-017, ICSI, Berkeley, March 1992.

[Omohundro94] Stephen M. Omohundro. The Sather 1.0 specifica-tion. Technical report, International Computer Sci-ence Institute, Berkeley, December 1994.

[Pardo98a] Alberto Pardo. A Calculational Approach to Monadicand Comonadic Programs. PhD thesis, DarmstadtUniversity of Technology, 1998.

[Pardo98b] Alberto Pardo. Personal communication, January1998.

[Parnas72] D. Parnas. On the criteria to be used in decompos-ing systems into modules. Communications of theACM, 15:1053–1058, 1972.

[Pepper92] Peter Pepper. Grundlagen der Informatik. Olden-bourg, 1992.

[Petre89] Marian Petre. Finding a Basis for Matching Program-ming Languages to Programming Tasks. PhD thesis,Department of Computer Science, University Col-lege London, 1989.

[Pierce & Turner94] Benjamin C. Pierce and David N. Turner. Sim-ple type-theoretic foundations for object-orientedprogramming. Journal of Functional Programming,4:207–247, April 1994.

[Ponder88] C. G. Ponder. Are applicative languages inefficient?SIGPLAN Notices, 23(6):135–139, June 1988.

[Poole et al.98] Ian Poole, Craig Ewington, Arthur Jones, and SteveWille. Combining functional and object-orientedprogramming methodologies in a large commercialapplication. In Proceedings of ICCL ’98. IEEE Com-puter Society, May 1998.

Page 316: A Functional Pattern System for Object-Oriented Design

298 Bibliography

[Posnak et al.96] Edward J. Posnak, R. Greg Lavender, and Harrik M.Vin. Adaptive pipeline: An object structural patternfor adaptive applications. In The 3rd Annual Confer-ence on Pattern Languages of Programming, Monticello,Illinois, September 1996.

[Prechel et al.97] Lutz Prechel, Barbara Unger, and MichaelPhilippsen. Documenting design patterns incode eases program maintenance. Submittedto Proc. Mod. & Emp. Studies of SW Evolution,January 1997.

[Pree94] Wolfgang Pree. Meta patterns - a means for cap-turing the essentials of reusable object-oriented de-sign. In Mario Tokoro and Remo Pareschi, editors,ECOOP ’94, Lecture Notes in Computer Science,pages 139–149. Springer Verlag, July 1994.

[Quibeldey-Cirkel94] Klaus Quibeldey-Cirkel. Paradigmenwechselim software-engineering. Softwaretechnik-Trends,14(1):59–62, February 1994.

[Racko94] Roland Racko. Object lessons: In praise of Eiffel.Software Development, 2(12), December 1994.

[Ran95] Alexander Ran. Moods: Models for object-orienteddesign of state. In J. O. Coplien and D. C.Schmidt, editors, Pattern Languages of Program De-sign. Addison-Wesley, Reading, MA, 1995.

[Reddy88] Uday S. Reddy. Objects as closures: Abstract se-mantics of object oriented languages. ACM Sym-posium on LISP and Functional Programming, pages289–297, July 1988.

[Remy & Vouillon97] D. Remy and Jirtme Vouillon. Objective ML: a sim-ple object-oriented extension of ML. In Proc. 24th

ACM Symposium on Principles of Programming Lan-guages, January 1997.

[Reynolds75] John C. Reynolds. User-defined types and procedu-ral data structures as complementary approachesto data abstraction. In S. A. Schuman, editor, NewDirections in Algorithmic Languages, pages 157–168.IFIP Working Group 2.1 on Algol, 1975.

Page 317: A Functional Pattern System for Object-Oriented Design

Bibliography 299

[Reynolds96] John C. Reynolds. Private communication. ACMState of the Art Summer School: Functional and Ob-ject Oriented Programming, September 1996.

[Riehle et al.97] Dirk Riehle, Wolf Siberski, Dirk Baumer, DanielMegert, and Heinz Zullighoven. Serializer.In Robert C. Martin, Dirk Riehle, and FrankBuschmann, editors, Pattern Languages of ProgramDesign 3, Reading, Massachusetts, Addison-Wesley,1997.

[Riehle97] Dirk Riehle. Composite design patterns. In ACMSIGPLAN Conference on Object-Oriented Program-ming Systems, Languages & Applications, pages 218–228, 1997.

[Rist & Terwilliger95] Robert Rist and Robert Terwilliger. Object-OrientedProgramming in Eiffel. Object-Oriented Series. Pren-tice Hall, 1995.

[Ritchie84] Dennis M. Ritchie. A stream input-output sys-tem. AT&T BGell Laboratories Technical Journal,63(8):1897–1910, October 1984.

[Rohlfing78] Helmut Rohlfing. Pascal: Eine Einfuhrung.Hochschultaschenbucher 756. B.I. Wis-senschaftsverlag, Mannheim, Wien, Zurich,1978.

[Rozas93] Guillermo J. Rozas. Translucent Procedures, Abstrac-tion without Opacity. PhD thesis, Department ofElectrical Engineering and Computer Science, Mas-sachusetts Institute of Technology, 1993.

[Rumbaugh et al.97] James Rumbaugh, Grady Booch, and Ivar Jacobson.Unified Modeling Language, version 1.0. Rational Soft-ware Corporation, January 1997.

[Rumbaugh91] J. Rumbaugh. Object-Oriented Modeling and Design.Prentice-Hall International Editions, 1991.

[Sargeant et al.95] John Sargeant, Steve Hooton, and Chris Kirkham.Ufo: Language evolution and consequences ofstate. In A. P. Wim Bohm and John T. Feo, edi-tors, High Performance Functional Computing, pages48–62, April 1995.

Page 318: A Functional Pattern System for Object-Oriented Design

300 Bibliography

[Sargeant93] John Sargeant. Uniting Functional and Object-Oriented Programming. In Object Technologies forAdvanced Software, volume 742 of Lecture Notes inComputer Science, pages 1–26. First JSSST Interna-tional Symposium, November 1993.

[Sargeant95] John Sargeant. How useful is laziness? News articleof comp.lang.functional, August 1995.

[Sargeant96a] John Sargeant. ”imperative” and ”declarative”.comp.lang.functional, April 1996.

[Sargeant96b] John Sargeant. Personal communication, April1996.

[Schmidt85] David A. Schmidt. Detecting global variables in de-notational specifications. ACM Transactions on Pro-gramming Languages and Systems, 7(2):299–310, 1985.

[Schmidt86] D. A. Schmidt. Denotational Semantics. A Methodol-ogy for Language Development. Allyn and Bacon, Inc,Boston Mass., 1986.

[Schmitz92] Lothar Schmitz. Using inheritance to explore a fam-ily of algorithms. Structured Programming, 13(2):55–64, February 1992.

[Schneider91] Hans-Jochen Schneider, editor. Lexikon der Infor-matik und Datenverarbeitung. 3. Auflage. Olden-bourg, 1991.

[Schroeder95] Ulrik Schroeder. Inkrementelle, syntaxbasierteRevisions- und Variantenkontrolle mit interaktiver Kon-figurationsunterstutzung. PhD thesis, TH Darmstadt,1995.

[Seiter et al.96] Linda Seiter, Jens Palsberg, and Karl Lieberherr.Evolution of object behavior using context rela-tions. In ACM SIGSOFT’96, Fourth Symposium onthe Foundations of Software Engineering. ACM Press,San Francisco, California, October 1996.

[Sestoft88] Peter Sestoft. Replacing function parameters byglobal variables. Diku, University of Copenhagen,October 1988.

[Shalit et al.92] Andrew Shalit, Jeffrey Piazza, and David Moon.Dylan — an object-oriented dynamic language. AppleComputer, Inc., 1992.

Page 319: A Functional Pattern System for Object-Oriented Design

Bibliography 301

[Shan95] Yen-Ping Shan. Smalltalk on the rise. Communica-tions of the ACM, 38(10):102–104, October 1995.

[Shang94] David L. Shang. Covariant specification. ACM SIG-PLAN Notices, 29(12):58–65, December 1994.

[Shang95a] David L. Shang. Covariant deep subtyping recon-sidered. ACM SIGPLAN Notices, 30(5):21–28, May1995.

[Shang95b] David L. Shang. Transframe: The language refer-ence, September 1995.

[Shank90] Roger C. Shank. Tell Me A Story. Charles Scribner’sSons, NY, 1990.

[Shaw96] Mary Shaw. Some patterns for software architec-ture. In John M. Vlissides, James O. Coplien, andNorman L. Kerth, editors, Pattern Languages of Pro-gram Design 2. Addison-Wesley, 1996.

[Siegel95] David M. Siegel. Pass by reference. In Pat-terns mailing list Digest, Patterns Digest. [email protected], March 1995.

[Smith95] David N. Smith. Dave’s Smalltalk FAQ. D. N. Smith,[email protected], July 1995.

[Snelting et al.91] Gregor Snelting, Franz-Josef Grosch, and UlrikSchroeder. Inference-based support for program-ming in the large. In Proceedings of the 3rd EuropeanSoftware Engineering Conference, LNCS 5550, pages396–408, Milano, Italy, October 1991.

[Snelting86] Gregor Snelting. Inkrementelle semantische Analyse inunvollstandigen Programmfragmenten mit Kontextrela-tionen. PhD thesis, TH Darmstadt, 1986.

[Snyder86] Alan Snyder. Encapsulation and inheritance inobject-oriented programming languages. Proceed-ings OOPSLA ’86, 21(11):38–45, November 1986.

[Stepanov & Kershenbaum86] A. Stepanov and A. Kershenbaum. Using tourna-ment trees to sort. Technical Report 86–13, Centerfor Advanced Technology in Telecommunications,Polytechnic University, 1986.

Page 320: A Functional Pattern System for Object-Oriented Design

302 Bibliography

[Stepanov & Lee94] A. Stepanov and M. Lee. The standard templatelibrary. ISO Programming Language C++ Project.Doc. No. X3J16/94-0095, WG21/NO482, May 1994.

[Sun97] Java JDK 1.1 guide, 1997.

[Szyperski92] Clemens A. Szyperski. Import is not inheritance– why we need both: Modules and classes. InO. Lehrmann Madsen, editor, Proceedings ECOOP’92, LNCS 615, pages 19–32, Utrecht, The Nether-lands, Springer-Verlag, July 1992.

[Thierbach96] Dirk Thierbach. Ein kalkulbasierter Vergle-ich zwischen objektorientiertem und funktionalemParadigma. Master’s thesis, TH Darmstadt, March1996.

[Thierbach97] Dirk Thierbach. Personal communication, July1997.

[Thomas & Weedon95] Pete Thomas and Ray Weedon. Object-Oriented Pro-gramming in Eiffel. International Computer ScienceSeries. Addison-Wesley, 1995.

[Thomas95] David Thomas. Ubiquitous applications: Embed-ded systems to mainframe. Communications of theACM, 38(10):112–114, October 1995.

[Thompson89] Simon Thompson. Lawful functions and programverification in Miranda. Science of Computer Pro-gramming, 13:181–218, 1989.

[van Rossum91] Guido van Rossum. Linking a stub generator to aprototyping language (python). Proceedings of Eu-rOpen Spring Conference, 1991.

[van Yzendoor95] Arjan van Yzendoor. Cyclic datastructures infunctional programming. Article: 5784 ofcomp.lang.functional, November 1995.

[Wadler & Blott89] Philip Wadler and Stephen Blott. How to make ad-hoc polymorphism less ad-hoc. In Conference Recordof the Sixteenth Annual ACM Symposium on Princi-ples of Programming Languages, pages 60–76, Austin,Texas, January 1989.

[Wadler85] Philip Wadler. How to replace failure by a list ofsuccesses. Functional Programming Languages and

Page 321: A Functional Pattern System for Object-Oriented Design

Bibliography 303

Computer Architecture, pages 113–128, September1985.

[Wadler87] Philip Wadler. Views: A way for pattern match-ing to cohabit with data abstraction. In ConferenceRecord of the Fourteenth Annual ACM Symposium onPrinciples of Programming Languages, pages 307–313,Munich, Germany, January 1987.

[Wadler89] Philip Wadler. Theorems for free! In 4th Inter-national Symposium on Functional Programming Lan-guages and Computer Architecture, London, Septem-ber 1989.

[Wadler90] P. Wadler. Linear types can change the world! InM. Broy and C. Jones, editors, IFIP TC 2 Work-ing Conference on Programming Concepts and Methods,Sea of Galilee, Israel, pages 347–359. North Holland,April 1990.

[Wadler92] Philip Wadler. The essence of functional program-ming. Proceedings of 19th Annual ACM Symposium onPrinciples of Programming Languages, January 1992.

[Walden & Nerson95] Kim Walden and Jean-Marc Nerson. SeamlessObject-Oriented Software Architecture: Analysis andDesign of reliable Systems. Prentice Hall, 1995.

[Wampler94] Dean Wampler. Where’s the beef? patterns to chewon (first in a series). In Patterns mailing list. [email protected], December 1994.

[Ward94] Martin P. Ward. Language-oriented programming.Software Concepts & Tools, 15(4):147–161, 1994.

[Waters79] Richard C. Waters. A method for analyzing loopprograms. IEEE Transactions on Software Engineering,5(3):237–247, January 1979.

[Watt et al.94] Stephen M. Watt, Peter A. Broadbery, Samuel S.Dooley, Pietro Iglio, Scott C. Morrison, Jonathan M.Steinbach, and Robert S. Sutor. A first report on theA# compiler. In Proceedings of the International Sym-posium on Symbolic and Algebraic Computation, July1994.

[Wechler92] Wolfgang Wechler. Universal Algebra for ComputerScientists. EATCS 25. Springer-Verlag, Berlin, Hei-delberg, New York, 1992.

Page 322: A Functional Pattern System for Object-Oriented Design

304 Bibliography

[Wegner87] Peter Wegner. Dimensions of object-based languagedesign. OOPSLA ’87, 22(12):168–182, December1987.

[Wegner90] Peter Wegner. Concepts and paradigms of object-oriented programming. ACM OOPS Messenger,1(1):3–11, August 1990.

[Wegner95] Peter Wegner. Models and paradigms of inter-action. Technical Report CS-95-21, Deparment ofComputer Science, Brown University, Providence,Rhode Island 02912, 1995.

[Weinand & Gamma94] Andre Weinand and Erich Gamma. Et++ — Aportable, homogenous class library and applicationframework. Proceedings of the UBILAB ’94 Confer-ence, pages 66–92, September 1994.

[Wikstrom87] Ake Wikstrom. Functional Programming Using Stan-dard ML. International Series in Computer Science.Prentice Hall, 1987.

[Wilson95] Neil Wilson. Const correctness with eiffel classes.Article: 6749 of comp.lang.eiffel, May 1995.

[Winston & Horn84] P. H. Winston and B. K. P. Horn. LISP. Addison-Wesley, 2nd edition, 1984.

[Wirfs-Brock et al.90] Rebecca Wirfs-Brock, Brian Wilkerson, and LaurenWiener. Designing Object-Oriented Software. PrenticeHall, 1990.

[Yu & Zhuang95] S. Yu and Q. Zhuang. Software reuse via algo-rithm abstraction. In Raimund Ege and BertrandMeyer, editors, Technology of Object-Oriented Lan-guages and Systems: TOOLS 17, Prentice Hall Inter-national, London, 1995.

[Zimmer94] Walter Zimmer. Relationships between design pat-terns. In James O. Coplien and Douglas C. Schmidt,editors, Pattern Languages of Program Design, pages345–364. Addison-Wesley, 1994.

Page 323: A Functional Pattern System for Object-Oriented Design

Index 305

Index

Symbolsα-conversion . . . . . . . . . . . . . . . . . . . . . . 49β-reduction . . . . . . . . .47, 49, 51, 53, 106δ-reduction . . . . . . . . . . . . . . . . . . . . 48, 52η-reduction . . . . . . . . . . . . . . . . 47, 48, 53λ-calculus . . . . . . . . . . . . . . . . 9, 11, 45–54⊕-operator . . . . . . . . . . . . . . . . . . . . . . . . 60⊥ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27σ-calculus . . . . . . . . . . . . . . . . . . . . . . . . .46LATEX 2ε . . . . . . . . . . . . . . . . . . . . . . . . . . . .vi6502 processor . . . . . . . . . . . . . . . . . . . . 2668000 processor . . . . . . . . . . . . . . . . . . . 26

AABACAB . . . . . . . . . . . . . . . . . . . . . . . . . 70Abelson, Hal . . . . . . . . . . . . . . . . . . . . . 273abstract datatype . . . . . . . . . . . . . . 26, 30Abstract Factory pattern . . . . . . . . . .200abstract interpretation . . . . . . . . . . . .201abstract state . . . . . . . . . . . . . 58, 156, 159abstract syntax tree . . . . . . . . . . 202, 227abstraction . . . . . . . . . . . . . . . . . . 102, 196

iterator . . . . . . . . . . . . . . . . . . . . . . .164procedural . . . . . . . . . . . . . . . . . . . 255timeless . . . . . . . . . . . . . . . . . . . . . . 152type . . . . . . . . . . . . . . . . . . . . . . . . . .255

accessread-only . . . . . . . . . . . . . . . . . . . . 152

access thread . . . . . . . . . . . . . . . . . . . . .158accessor function . . . . . . . . . . . . . . . . .161accumulator technique . . . . . . . . . . . . 23Acyclic Visitor pattern . . . . . . . . . . . .218ad-hoc polymorphism . . . . . . . . . . . . .35ADA 95 . . . . . . . . . . . . . . . . . . . . . . . . . . . 91Adapter pattern . . . . . . . . . 112, 129, 203adding functionality . . . . . . . . . . . . . 203adding semantics . . . . . . . . . . . . . . . . 204addition

non-intrusive . . . . . . . . . . . . . . . . .212adjustment

iterator . . . . . . . . . . . . . . . . . . . . . . .179ADT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191aggregate . . . . . . . . . . . . . . . . . . . . . . . . 163Alexander, Christopher . . . . .69, 74, 86algebraic datatype . . . . . . . . . . . . . . . 239ALGOL . . . . . . . . . . . . . . . . . . . . . 40, 41, 75ALGOL 60 . . . . . . . . . . . . . . . . . . . . . . . 144ALGOL 68 . . . . . . . . . . . . . . . . . . . . . . . .110algorithm

graph . . . . . . . . . . . . . . . . . . . . . . . . 144algorithm classification . . . . . . . . . . . .98algorithmic skeleton . . . . . . . . . . . . . 242algorithms

monolithic . . . . . . . . . . . . . . . . . . . . 99aliasing . . . 12, 26, 38, 92, 149, 151, 152,

154, 155, 158, 161, 244control . . . . . . . . . . . . . . . . . . . . . . . 244direct . . . . . . . . . . . . . . . . . . . . . . . . 156indirect . . . . . . . . . . . . . . . . . . . . . . 156

aliasing protection . . . . . . . . . . . . . . . 154all-quantifier . . . . . . . . . . . . . . . . . . . . . . 35Allen, Fred . . . . . . . . . . . . . . . . . . . . . . .163anonymous closures . . . . . . . . . . . . . . .59anonymous function . . . . . . . . . . . . . 235APL . . . . . . . . . . . . . . . . . . . . .179, 186, 187Appel, Andrew W. . . . . . . . . . . . . . . . . 22append operator . . . . . . . . . . . . . . . . . . 23application skeleton . . . . . . . . . . . . . . . 36applicative-order reduction . . . . 11, 14architecture

data-flow . . . . . . . . . . . . . . . . . . . . 123language . . . . . . . . . . . . . . . . . . . . . 270von Neumann . . . . . . . . . . . . . . . . .40

argumentimmutable . . . . . . . . . . . . . . . . . . . 158

Page 324: A Functional Pattern System for Object-Oriented Design

306 Index

initial . . . . . . . . . . . . . . . . . . . . . . . . 106arguments

initial . . . . . . . . . . . . . . . . . . . . . . . . 106ordering of . . . . . . . . . . . . . . . . . . . . 27

arithmetic rounding effect . . . . . . . . . 26ASCII . . . . . . . . . . . . . . . . . . . . . . . .126, 143assignment frequency . . . . . . . . . . . . 154AST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .58attribute grammar . . . . . . . . . . . . . . . 144Auer, Ken . . . . . . . . . . . . . . . . . . . . . . . .251Austern, Matthew . . . . . . . . . . . . . . . .149avoiding tail suspensions. . . . . . . . .128awkward reuse . . . . . . . . . . . . . . . . . . . .95

Bback-pointer . . . . . . . . . . . . . . . . . . . . . 211Backus, John20, 40, 41, 58, 89, 184, 268backward iteration . . . . . . . . . . . . . . . 181bank account . . . . . . . . . . . . . . . . . . . . . .22Barton, Bob . . . . . . . . . . . . . . . . . . . . . . . 30basic type . . . . . . . . . . . . . . . . . . . 158, 160Bauer, Friedrich L. . . . . . . . . . . . . . . . . .74behavior

default . . . . . . . . . . . . . . . . . . . . . . . 194idle . . . . . . . . . . . . . . . . . . . . . . . . . . 199

behavior parameterization . . . . . 13, 93BETA . . . . . . . . . 41, 44, 91, 235, 245, 272binding

implicit . . . . . . . . . . . . . . . . . . . . . . 235bitmap . . . . . . . . . . . . . . . . . . . . . . . . . . .143bitstream . . . . . . . . . . . . . . . . . . . . . . . . .117black-box composition . . . . . . . . . . . 103black-box framework. . . . . . . . . . . . .252black-box reuse . . . . . . . . . . . . .37, 64, 86bloated interface . . . . . . . . . . . . . . . . . 203Bosch, Jan . . . . . . . . . . . . . . . . . . . . . . . .270bottleneck

von Neumann . . . . . . . . . . . . . . . . .40Bottom . . . . . . . . . . . . . . . . . . . . . . . . . . . .27bound

upper . . . . . . . . . . . . . . . . . . . .123, 246Bridge pattern . . . . . . . . . . 102, 188, 200broken encapsulation . . . . . . . . . . . . 210Budd, Timothy. . . . . . . .3, 179, 267, 271building . . . . . . . . . . . . . . . . . . . . . . . . . 203

bureauobject-oriented . . . . . . . . . . . . . . . . 32

Burke, Edmund . . . . . . . . . . . . . . . . . . . . vBurroughs 220 . . . . . . . . . . . . . . . . . . . . 75Burroughs B5000 . . . . . . . . . . . . . . . . . . 75Busch, Wilhelm . . . . . . . . . . . . . . . . . . 115business transactions . . . . . . . . . . . . . . 98business-transaction . . . . . . . . . . . . . . . . 67business-transactions . . . . . . . . . . . . .111BYTE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

CC . . . . . . . . . . . . . . . . . . . .14, 21, 33, 36, 37C++ . . . . . . . 3, 29, 33, 35, 40–42, 59, 75,

91, 95, 102, 105, 110, 154, 157,164, 166, 181, 187, 203, 214,218, 227, 237, 245

cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23CAD-object . . . . . . . . . . . . . . . . . . . . . . . 99calculation

delayed . . . . . . . . . . . . . . . . . . . . . . 100calculation pipeline . . . . . . . . . . . . . . 113calculus

λ-calculus . . . . . . . . . . . . 9, 11, 45–54σ-calculus . . . . . . . . . . . . . . . . . . . . . 46Opulus . . . . . . . . . . . . . . . . . . . . 46–54Opus . . . . . . . . . . . . . . . . . . . . . . 46, 48

Call-back function . . . . . . . . . . . 107, 111call-by

name . . . . . . . . . . . . . . . . . . 11, 14, 144need . . . . . . . . . 15, 87, 144, 145, 177value . . . . . . . . . . . . . . . . . . 11, 14, 106

callback. . . . . . . . . . . . . . . . . . . . . . .87, 110cartesian coordinates . . . . . . . . . . . . . 156catcalls . . . . . . . . . . . . . . . . . . . . . . . . . . . 107categorical interpretation . . . . . . . . .127Cecil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203CECIL . . . . . . . . . . . . . . . . . . . . . . . . 37, 238CENTAUR . . . . . . . . . . . . . . . . . . . . . .48, 50Chain of Responsibility pattern . . 113,

163, 188, 200chessboard . . . . . . . . . . . . . .20, 25, 26, 32Church-Rosser property . . . . . . . . . . . 46Church-Rosser Theorem . . . . . . . . . . .11Circle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .65

Page 325: A Functional Pattern System for Object-Oriented Design

Index 307

circular definition . . . . . . . . . . . . . . . . 246circular dependencies . . . . . . . . . . . . . 28citizens

first-class . . . . . . . . . . . . . . . . . . . . . . 12class

inner . . . . . . . . . . . . . . . . . . . . . . . . . 110class diagram . . . . . . . . . . . . . . . . . . . . . 80class encapsulation . . . . . . . . . . . . . . . .33class versus object adapter . . . . . . . 129classification

algorithm . . . . . . . . . . . . . . . . . . . . . 98clean state . . . . . . . . . . . . . . . . . . . . . . . 124CLOS . 33, 37, 41, 91, 109, 166, 203, 238,

268, 271closure . . . . . . . . . . . . . . . . . . . . . . . . 13, 49closure overhead . . . . . . . . . . . . . . . . . 127co-routine . . . . . . . . . . . . . . . . . . . . . . . .120Co-Routine pattern . . . . . . . . . . . . . . .145coalgebra . . . . . . . . . . . . . . . . . . . . . . . . 247COBOL . . . . . . . . . . . . . . . . . . . . . . . . . . . .85Code

= . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2450.0 . . . . . . . . . . . . . . . . . . . . . . . . . . 1871.3 . . . . . . . . . . . . . . . . . . . . . . . . . . 1663DPoint . . . . . . . . . . . . . . . . . . . . . . . 4341.inc . . . . . . . . . . . . . . . . . . . . . . 1530 . . . . . . . . . . . . . . . . . . . . . . . . 193, 1940 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .187A . . . . . . . . . . . . . . . . . . . . . . . . . . . . .244A. . . . . . . . . . . . . . . . . . . . . . . . . . . . .237a . . . . . . . . . . . . . . . . . . . . . . . . . 41, 164a:=a + b; . . . . . . . . . . . . . . . . . . 153Accept . . . . . . . . . . . . . . . . . . .255, 271Accept . . . . . . . . . . . . . . . . . . . . . . 209accept . . . . . . . . . . . . . . . . . . . . . . . .112accumulated results . . . . . . . . . . 109add . . . . . . . . . . . . . . . . . . . . . . 150, 152addComplex . . . . . . . . . . . . . . . . . .110addInteger . . . . . . . . . . . . . . . . . . . 109addReal . . . . . . . . . . . . . . . . . . . . . .109Aggregate . . . . . . . . . . . . . . . . . . . .175all . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174allInstances . . . . . . . . . . . . . . 160AllPurposeCook . . . . . . . . . . . . . . . 62aMatrix . . . . . . . . . . . . . . . . . . . . . .41

aMatrix2 . . . . . . . . . . . . . . . . . . . . 41and . . . . . . . . . . . . . . . . . . . . . . . . . . 113and . . . . . . . . . . . . . . . . . . . . . . .98, 125Any . . . . . . . . . . . . . . . . . . . . . .107, 237AnyObject . . . . . . . . . . . . . . . 193, 194append. . . . . . . . . . . . . . . . . . . . . . .174apply . . . . . . . . . . . . . . . . . . . . . . . . . . 62apply . . . . . . . . . . . . . . . . . . . . . . . 238applyInner . . . . . . . . . . . . . . . . .187assign . . . . . . . . . . . . . . . . . . . . . . 211asStream . . . . . . . 175, 177, 179, 181asStreamWithFunction . . . . . . . 181@ . . . . . . . . . . . . . . . . . . . . 96, 208, 215aToolTip . . . . . . . . . . . . . . . . . . . 243average . . . . . . . . . . . . . . . . . . . . . . 174B . . . . . . . . . . . . . . . . . . . . . . . . . . . . .244b . . . . . . . . . . . . . . . . . . . . . 41, 152, 238Bag . . . . . . . . . . . . . . . . . . . . . . . . . . 179become . . . . . . . . . . . . . . . . . . . . . . 199become: . . . . . . . . . . . . . .98, 156, 199BinaryTree . . . . . . . . . . . . . . . . . . . 191Bird . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Book . . . . . . . . . . . . . . . . . . . . . . . . . . 96Boolean . . . . . . . . . . . . . . . . . . . . . . . 96button . . . . . . . . . . . . . . . . . . . . . . 243Car . . . . . . . . . . . . . . . . . . . . . . . . . . . .43checkPlane . . . . . . . . . . . . . . . . . . . .67CheckTitle . . . . . . . . . . . . . . . . 97, 106Circle . . . . . . . . . . . . . . . . . .34–37, 156class name . . . . . . . . . . . . . . . . . 217Client . . . . . . . . . . . . . . . . . . . . 101, 140clone . . . . . . . . . . . . . . . . . . . 237, 238coll1 + coll2 + coll3 +

coll4 + ... . . . . . . . . . . . 187Collision . . . . . . . . . . . . . . . . . . . . . . .67collision . . . . . . . . . . . . . . . . . . . . . . . 67Comparable . . . . . . . . . . . . . . 94, 243compAuthor . . . . . . . . . . . . . . . . . . 103compile . . . . . . . . . . . . . . . . . . . . . . . 99Complex . . . . . . . . . . . . 149–151, 153Component . . . . . . . . . . . . . . . 95, 146Composite . . . . . . . . . . . . . . . 163, 188compTitle . . . . . . . . . . . . . . . . . . . . 103Concrete . . . . . . . . . . . . . . . . . . . . . 143ConcreteFlyweight . . . . . . . . . . . 199

Page 326: A Functional Pattern System for Object-Oriented Design

308 Index

ConcreteFunction . . . . . . . .101, 109ConcretePipe . . . . . . . . . . . . . . . . 143ConcretePipeFunc . . . . . . . . . . . 143ConcreteState . . . . . . . . . . . . . . . .257ConcreteStates . . . . . . . . . . . . . . .199ConcreteStream . . . . . . . . . . . . . .175cons . . . . . . . . . . . . . . . . . . . . . . . . . 181cons . . . . . . . . . . . . . . . . . . . . . . . . .182ConsList . . . . . . . . . . . . . . . . . . . . . . 89conss . . . . . . . . . . . . . . . . . . . . . . . 182Const Complex . . . . . . . . . . . . . . .153ContFunc . . . . . . . 127–130, 138, 140contFunc . . . . . . . . . . . . . . . . . . . 134continue? . . . . . . . . . . . . . 167, 177Cook . . . . . . . . . . . . . . . . . . 60–63, 102cook.prepare . . . . . . . . . . . . . . 102cook.prepare(desert) . . . 102cook.prepare(fish) . . . . . .102Cook1 . . . . . . . . . . . . . . . . . . . . . . . . .61Cook2 . . . . . . . . . . . . . . . . . . . . . . . . .61CookAdapter . . . . . . . . . . . . . . . . . . 63copy . . . . . . . . . . . . . . . . . . . . . . . . . 174count . . . . . . . . . . . . . . . . . . . . . 95, 249count . . . . . . . . . . . . . . . . . . . . . . . 196counter . . . . . . . . . . . . . . . . . . . . . . .108creation . . . . . . . . . . . . . . . . . . . . . . 251Current . . . . . . . . . . . . . . . . . . . . .216d . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41DefaultReference . . . .198, 200, 250delay . . . . . . . . . . . . . . . . . . . 124, 247detect . . . . . . . . . . . . . . . . . . . . . . . . 163Dictionary . . . . . . . . . . . . . . . . . . . . 104Dispenser . . . . . . . . . . . . . . . . . . . . 163display . . . . . . . . . . . . . . . . . . . . . 35, 36display . . . . . . . . . . . . . . . . . . . . .215do all. . . . . . . . . . . . . . . . . . . . . . . . .240do if . . . . . . . . . . . . . . . . . . . . . 109, 240do until . . . . . . . . . . . . . . . . . . . . . . .240do while . . . . . . . . . . . . . . . . . . . . . .240Double . . . . . . . . . . . . . . . . . . . . . . . 243Drawer . . . . . . . . . . . . . . . . . . . . . . . . 32duration . . . . . . . . . . . . . . . . . .110, 213Ellipse . . . . . . . . . . . . . . . . . . . . . . . 156eos . . . . . . . . . . . . . 127–129, 138, 143exists . . . . . . . . . . . . . . . . . . . . . . . . 174

exp . . . . . . . . . . . . . . . . . . . . . . . . . . 215expanded . . . . . . . . . . . . . . . . . . . . 106expanded . . . . . . . . . . . . . . . . . . . 183f . . . . . . . . . . . . . . . . . . . . . . . . 103, 238false . . . . . . . . . . . . . . . . . . . . . . . . . 192Figure . . . . . . . . . . . . . . . . . . . . . 34–36figure . . . . . . . . . . . . . . . . . . . . . . . 35Filter . . . . . . . . . . . . . . . . . . . . .138, 140filter . . . . . . . . . . . . . . . . . . . . . . . . . . 174Filter1 . . . . . . . . . . . . . . . . . . . . . . . . 140FilterFunc . . . . . . . . . . . 137, 138, 140first . . . . . . . . . . . . . . . . . . . . . . 104, 145fishCook.prepare . . . . . . . . 102FlyingObject . . . . . . . . . . . . . . . . . . .67Fold . . . . . . . . . . . . . . . . . . . . . . . . . .237Fold . . . . . . . . . . . . . . . . . . . . . . . . .183fold . . . . . . . . . . . . . . . . . . . . . . . . .183force . . . . . . . . . . . . . . . . . . . . . . . 144forceItem . . . . . . . . . . . . . . . . 129, 134forceItem . . . . . . . . . . . . . 133, 134forceTail . . . . . . . . . . . . .128, 129, 135forceTail . . . . . . . . . . . . . . . . . . 129foreach . . . . . . . . . . . . . . . . . . . . .187Function . . . 138, 140, 174, 175, 207,

236, 237Functions . . . . . . . . . . . . . . . . . . . . 207General . . . . . . . . . . . . . . . . . .237, 238GenFunc . . . . . . . . . . . . . . . . 207, 215goto . . . . . . . . . . . . . . . . . . . . . . . . . . 22has . . . . . . . . . . . . . . . . . . . . . . . 93, 192has . . . . . . . . . . . . . . . . . . . . . . . . . . . 97Hash Table . . . . . . . . . . . . . . . . . . .216HashArray . . . . . . . . . . . . . . . . . . . . .31horner. . . . . . . . . . . . . . . . . . . . . . . .174I . . . . . . . . . . . . . . . . . . . . . . . . . . . . .166Identity . . . . . . . . . . . . . . . . . . . . . . . 236if . . . . . . . . . . . . . . . . .14, 192, 193, 196IfThen . . . . . . . . . . . . . . . . . . . . . . . .203ImmutableInterface . . . . . . . . . . . 152In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96Inductive . . . . . . . . . . . . . . . . . . . . . 240infix + . . . . . . . . . . . . . . . . . . . 153init . . . . . . . . . . . . . . . . . . . . . . . . .215initialize . . . . . . . . . . . . . . . . .211inject: into: . . . . . . . . . . . . . . . . . . . 187

Page 327: A Functional Pattern System for Object-Oriented Design

Index 309

innerProduct . . . . . . . . . . . . . . . . . 187int . . . . . . . . . . . . . . . . . . . . . . . . . . . .109Integer . . . . . . . . . . . . . . . . . . . . . . . 243IntegerSet . . . . . . . . . . . . . . . . . . . . .31Internal. . . . . . . . . . . . . . . . . . . . . . .217interpretation . . . . . . . . . . . 203Invoker . . . . . . . . . . . . . . . . . . . . . . . 101ip . . . . . . . . . . . . . . . . . . . . . . . . . . . 182is equal . . . . . . . . . . . . . . . . . . . . 156is valid . . . . . . . . . . . . . . . . . . . . . . . . 95is void . . . . . . . . . . . . . . . . . . . . . . . .250isBible . . . . . . . . . . . . . . . . . . . . . . . 100item . . . . . . . . 131, 134, 137, 140, 217item . . . . . . . . . . . . . . . . . . . . 121, 133Iterator . . . . . . . 94, 95, 103, 124, 145iterator.do(f) . . . . . . . . . . . 103l1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19l2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19LA address . . . . . . . . . . . . . . . . . . .227Language . . . . . . . . . . . . . . . . . . . . 204last . . . . . . . . . . . . . . . . . . . . . . 128, 129LazyObject . . . . . . . . . . . . . . 121, 130LD data . . . . . . . . . . . . . . . . . . . . . . 227leaf . . . . . . . . . . . . . . . . . . . . . . . . . . 249length . . . . . . . . . . . . . . . . . . . . . . . . 174Letter . . . . . . . . . . . . . . . . . . . . . . . . . 32List . . . . . . . . . . . . . . . . . . . . 32, 89, 164M . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48make . . . . . . . . . . . . . . . . . . . . . . . . .198make(3) . . . . . . . . . . . . . . . . . . . . .216Mammal . . . . . . . . . . . . . . . . . . . . . . .31Map . . . . . . . . . . . . . . . . . . . . . 143, 145map . . . . . . . . . . . . . . . . . . . . . . . . . .174map. . . . . . . . . . . . . . . . . . . . . . . . . . 183max . . . . . . . . . . . . . . . . . . . . . . . . . . 174min . . . . . . . . . . . . . . . . . . . . . . . . . . 174min . . . . . . . . . . . . . . . . . . . . . . . . . . 126mobyBook . . . . . . . . . . . . . . . . . . . . .97mult . . . . . . . . . . . . . . . . . . . . . . . . . . 150MutableInterface . . . . . . . . . . . . . 152MutableObject . . . . . . . . . . . 195–200N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .48name . . . . . . . . . . . . . . . . . . . . . . . . 108NatFunc . . . 128, 130, 131, 135, 137,

140

next . . . . . . . . . . . . . . . . . . . . . . . . . . 104nil . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263nil . . . . . . . . . . . . . . . . . . . . . . . . . . 161NilList . . . . . . . . . . . . . . . . . . . . . . . . . 89no-valid-employee . . . . . . . . . . . . 194NoController . . . . . . . . . . . . . 199, 263node . . . . . . . . . . . . . . . . . . . . . . . . . 108node-type . . . . . . . . . . . . . . . . . . 203NullDragMode . . . . . . . . . . . . . . . .199NullInputManager . . . . . . . . . . . . 199NullScope . . . . . . . . . . . . . . . . . . . . 199number . . . . . . . . . . . . . . . . . 187, 243number.zero . . . . . . . . . . .187, 243Numeric . . . . . . . . . . . . . . . . . . . . . .243obj = Void . . . . . . . . . . . . . . . . .193obj.is void . . . . . . . . . . . . . . . .193ObjectInterface . . . . . . . . . . 195–197ObjectProxy . . . . . . . . . . . . . . . . . . 198other . . . . . . . . . . . . . . . . . . . 151, 237Out . . . . . . . . . . . . . . . . . . . . . . . . . . . .96out . . . . . . . . . . . . . . . . . . . . . . . . . . . 192out . . . . . . . . . . . . . . . . . . . . . . . . . . 130outerProduct . . . . . . . . . . . . . . . . . 187Part . . . . . . . . . . . . . . . . . . . . . . . . . . . 67Person . . . . . . . . . . . . . . . . . . . .43, 246PfIf . . . . . . . . . . . . . . . . . . . . . . . . . . .207pfVar . . . . . . . . . . . . . . . . . . . . . . . 216Piece . . . . . . . . . . . . . . . . . . . . . . 67, 99Pig . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Pipe . . . . . . . .129, 137, 138, 140, 143PipeFunc . . . . . . . . . . . . . . . . 138, 140pipeFunc . . . . . . . . . . . . . . . . . . . . . 143pipeFunc . . . . . . . . . . . . . . . . . . . 137plus . . . . . . . . . . . . . . . . . . . . . 151–153plus . . . . . . . . . . . . . . . . . . . . 153, 182Point . . . . . . . . . . . . . . . . . . . . . . . . . . 43PpFunctions . . . . . . . . . . . . . . . . . .207PpIf . . . . . . . . . . . . . . . . . . . . . . . . . . 207PpLang . . . . . . . . . . . . . . . . . . . . . . 215prepare . . . . . . . . . . . . . . . . . . . . . . . 61prepare1 . . . . . . . . . . . . . . . . . . . . . . 61prepare2 . . . . . . . . . . . . . . . . . . . . . . 61prettyFunctions . . . . . 213, 215prettyPrint . . . . . . . . . . . . . . . 215printType . . . . . . . . . . . . . . . . . . . . . . 36

Page 328: A Functional Pattern System for Object-Oriented Design

310 Index

process . . . . . . . . . . . . . . . . . . . . . . 271product . . . . . . . . . . . . . . . . . . . . . . .174pushAllFromStack . . . . . . . . . . . . . 33Queue . . . . . . . . . . . . . . . . . . . . . . . 163R. . . . . . . . . . . . . . . . . . . . . . . . . . . . .238real . . . . . . . . . . . . . . . . . . . . . . . . . . 109Recipe . . . . . . . . . . . . . . . . . . . . . . . . 62recipe . . . . . . . . . . . . . . . . . . . . . . . . . 61Recipe1 . . . . . . . . . . . . . . . . . . . . . . . 62Recipe2 . . . . . . . . . . . . . . . . . . . . . . . 62Rectangle . . . . . . . . . . . . . . . 156, 157reduce . . . . . . . . . . . . . . . . . . . . . . . 174reduction . . . . . . . . . . . . . . . . . . . . . 187reverse . . . . . . . . . . . . . . . . . . . . . . .174salary . . . . . . . . . . . . . . . . . . . . . . . . 194scan . . . . . . . . . . . . . . . . . . . . . . . . . 187server . . . . . . . . . . . . . . . . . . . . . . 117Set . . . . . . . . . . . . . . . . . . . . . . . 31, 179Shaft . . . . . . . . . . . . . . . . . . . . . . .67, 99Sieve . . . . . . . . . . . . . . . .136, 137, 140SieveFunc . . . . . . . . . . .136–138, 140SortedList . . . . . . . . . . . . . . . . . . . . . 93SportsCar . . . . . . . . . . . . . . . . . . . . . 43Square . . . . . . . . . . . . . . . . . . 156, 157Stack . . . . . . . . . . . . . . . . . .32, 33, 163stat . . . . . . . . . . . . . . . . . . . . . . . . .215State . . . . . . . . . . . . . . . . . . . . . . . . . 257step . . . . . . . . . . . . 127–129, 135, 143store . . . . . . . . . . . . . . . . . . . . . . . 211StrategyContext . . . . . . . . . . . . . . 200Stream124, 128, 130, 131, 134, 140,

145, 175StreamFunc . . . .128–131, 133, 136,

137, 140, 146, 186StreamFuncOpt .128, 135, 136, 146StreamFunction . . . . . . . . . . . . . . 136StreamSuspension . 128, 129, 131,

134StreamTail . 128, 134–136, 140, 146StreamVal . . . . . . . . . . .124, 136, 140StreamValue . . . . . . . . . . . . . . . . . 128String . . . . . . . . . . . . . . . . . . . . . . . . 154StringBuffer . . . . . . . . . . . . . . . . . . 154Student . . . . . . . . . . . . . . . . . . . . . . . 43subordinates . . . . . . . . . . . . . . . . . 194

sum . . . . . . . . . . . . . . . . . . . . . . . . . . 174Suspension . . . . . . . . . . . . . . 121, 130suspension . . . . . . . . . . . . . . 134, 146Symbol . . . . . . . . . . . . . . . . . . . . . . .161tail . . . . . . . . . . . . . .128, 134, 136, 137tail . . . . . . . . . . . . . . . .121, 129, 145takeOff . . . . . . . . . . . . . . . . . . . . . . . . 67Text . . . . . . . . . . . . . . . . . . . . . . . . . . 245Times . . . . . . . . . . . . . . . . . . . . . . . . 180times . . . . . . . . . . . . . . . . . . . 106, 182titleCompare . . . . . . . . . . . . . . . 97toValue . . . . . . . . . . . . . . . . . . . . .130ToyAss . . . . . . . . . . . . . . . . . . . . . . . 204ToyIf . . . . . . . . . . . . . . . . . . . . . 204, 207ToyLang . . . . . . . . . . . . .204, 207, 215Transfold. . . . . . . . . . . . . . . . .175, 242transfold. . . . . . . . . . . . . . . . . . . . . .176TransFold3 . . . . . . . . . . . . . . . . .184transMapInner . . . . . . . . . . . . 186transpose . . . . . . . . . . . . . 184, 186true . . . . . . . . . . . . . . . . . . . . . . . . . . . 98typeCheck . . . . . . . . . . . . . . . . . . . . .99typeMessage . . . . . . . . . . . . . . 36, 37undo . . . . . . . . . . . . . . . . . . . . . . . . . 109unit . . . . . . . . . . . . . . . . . . . . . . . . . . 107unit . . . . . . . . . . . . . . . . . . . . 124, 247update . . . . . . . . . . . . . . . . . . . . . . 117Value . . . . . . . . . . . . . . . . . . . . 121, 130value . . . . . . . . . . . . . . . .127–129, 143value . . . . . . . . . . . . . . . . . . . . . . . 134ValueObject . . . . . . . . . . . . . 155, 158variance . . . . . . . . . . . . . . . . . . . . . .174void . . . . . . . . . . . . . . . . . . . . . . . . .187VoidAny . . . . . . . . . . . . . . . . . 193, 194VoidClass . . . . . . . . . . . . . . . . . . . . 248VoidEmployee . . . . . . . . . . . . . . . . 194VoidSalary . . . . . . . . . . . . . . . . . . . 194VoidTree . . . . . . . . . . . . . . . . . . . . . 192VoidValue . . . . . . .192, 195, 197–200Vulture . . . . . . . . . . . . . . . . . . . . . . . . 31Whale . . . . . . . . . . . . . . . . . . . . . . . . . 31while . . . . . . . . . . . . . . . . . . . . . . . . . . 20with: do: . . . . . . . . . . . . . . . . . . . . . .187xs . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

code generation . . . . . . . . . . . . . . . . . . 202

Page 329: A Functional Pattern System for Object-Oriented Design

Index 311

code readability . . . . . . . . . . . . . . . . . . 234coinductive type . . . . . . . . . . . . . . . . . 127collection . . . . . . . . . . . . . . . . . . . . . . . . 163

garbage . . . . . . . . . . . . . . . . . . . . . . 247heterogeneous. . . . . . . . . . . . . . . .204ordered . . . . . . . . . . . . . . . . . . . . . . 164

collection of books . . . . . . . . . . . . . . . . 93combination

static . . . . . . . . . . . . . . . . . . . . . . . . . . 94combinator function . . . . . . . . . . . . . . .13combinatorial explosion . . . . . . . . . . . 94comfort . . . . . . . . . . . . . . . . . . . . . . . . . . 264command . . . . . . . . . . . . . . . . . . . . . . . . . 68Command pattern . . . .87, 99, 101, 109,

111, 112, 146, 199, 261command-query separation .57, 58, 68comparison

pointer . . . . . . . . . . . . . . 156, 160, 245competence . . . . . . . . . . . . . . . . . . . . . . . 66complex number . . . . . . . . . . . . 149, 153complexity

description . . . . . . . . . . . . . . . . . . . 266component . . . . . . . . . . . . . . . . . . . . . . .264Composite pattern . 113, 146, 163, 188,

197, 199, 218, 249composition

black-box . . . . . . . . . . . . . . . . . . . . 103function . . . . . . . . . . . . . . . . . . . . . .236

compositional definition . . . . . . . . . 201compositionality . . . . . . . . . . . . . . . . . 102concrete function package . . . . . . . . 207concrete state . . . . . . . . . . . . . . . . . 58, 156configuration

stream . . . . . . . . . . . . . . . . . . . . . . . 125conformance

expression . . . . . . . . . . . . . . . . . . . 237cons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15const . . . . . . . . . . . . . . . . . . . . . . . . . . . . .152Constant Object pattern . 152, 161, 199constraints

space . . . . . . . . . . . . . . . . . . . . . . . . .206time . . . . . . . . . . . . . . . . . . . . . . . . . .120

constructorempty . . . . . . . . . . . . . . . . . . . . . . . .191

consumption

data . . . . . . . . . . . . . . . . . . . . . . . . . .123consumption . . . . . . . . . . . . . . . . . . . . . . 177containment

object . . . . . . . . . . . . . . . . . . . . . . . . 159context relations . . . . . . . . . . . . . . . . . 258continuation

iteration . . . . . . . . . . . . . . . . . . . . . .180semantics . . . . . . . . . . . . . . . . . . . . . 22

contravariant . . . . . . . . . . . . . . . . . . . . . .34control

termination . . . . . . . . . . . . . . . . . . 246control aliasing. . . . . . . . . . . . . . . . . . .244control object . . . . . . . . . . . . . . . . . . . . . . . 66Convenience Methods pattern . . . .109Convex . . . . . . . . . . . . . . . . . . . . . . . . . . 144Conway, John Horton . . . . . . . . . . . . . 24cook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60Cook, Rich . . . . . . . . . . . . . . . . . . . . . . . . 41Cook, William. . . . . . . . . . . . . . . . . . . .267coordinates

cartesian . . . . . . . . . . . . . . . . . . . . . 156polar . . . . . . . . . . . . . . . . . . . . . . . . . 156

Coplien, James O. . . . . . . . . . . . . . .73, 75copy

deep . . . . . . . . . . . . . . . . . . . . . . . . . 160shallow . . . . . . . . . . . . . . . . . . . . . . 156

copy operation . . . . . . . . . . . . . . . . . . . 158copy semantics . . . . . . . . . . . . . . . . . . . .88copy-on

change . . . . . . . . . . . . . . . . . . 158, 159demand . . . . . . . . . . . . . . . . . . . . . . 160passing . . . . . . . . . . . . . 154, 158, 159

coroutines . . . . . . . . . . . . . . . . . . . . . . . 242correctness . . . . . . . . . . . . . . . . . . . . . . .155

proof . . . . . . . . . . . . . . . . . . . . . . . . . . 18cosmic particle . . . . . . . . . . . . . . . . . . . . . vcovariance . . . . . . . . . . . . . . . . . . . . . . . 107covariant . . . . . . . . . . . . . . . . . . . . . . . . . .34covariant redefinition . . . . . . . . . . . . 107creation

implicit . . . . . . . . . . . . . 198, 249, 251Cunningham, Ward . . . . . . . . . . . . . . . 75curried . . . . . . . . . . . . . . . . . . . . . . . . . . . .13curry functional . . . . . . . . . . . . . . . . . .235Curry, Haskell B. . . . . . . . . . . . . . . . . . . 13

Page 330: A Functional Pattern System for Object-Oriented Design

312 Index

cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . .165cycle

dependency . . . . . . . . . . . . . . . . . . 263cyclic data structures . . . . . . . . . . . . . . 22

Dda Vinci, Leonardo . . . . . . . . . . . . . . . . . 4DAC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116data abstraction . . . . . . . . . . . . . . . .10, 30data consumption . . . . . . . . . . . . . . . .123data dependencies . . . . . . . . . . . . . . . . 18data duplication . . . . . . . . . . . . . . . . . 154data generation . . . . . . . . . . . . . .123, 246data generator . . . . . . . . . . . . . . . . . . . . 16data structure

infinite . . . . . . . . . . . . . . . . . . . . . . . . 15data-flow architecture . . . . . . . . . . . . 123datatype

algebraic . . . . . . . . . . . . . . . . . . . . . 239datatype extension . . . . . . . . . . . . . . . 254dead data . . . . . . . . . . . . . . . . . . . . . . . . . 40declaration

up-front . . . . . . . . . . . . . . . . . 119, 246declarative . . . . . . . . . . . . . . . . . . . . . . . . 18declarative programming . . . . . . . . . 24decomposition

functional . . . . . . . . . . . . . . . . .10, 235object-oriented . . . . . . . . . . . .32, 235

Decorator pattern . . . . . . . . . . . .162, 203decoupling

module . . . . . . . . . . . . . . . . . . . . . . 120deemphasize unit . . . . . . . . . . . . . . . . 117deep copy . . . . . . . . . . . . . . . . . . . . . . . .160default behavior . . . . . . . . . . . . . . . . . 194default parameter . . . . . . . . . . . . . . . . 108Default Value pattern . . . . . . . . . . . . 222definition

circular . . . . . . . . . . . . . . . . . . . . . . .246compositional . . . . . . . . . . . . . . . . 201design pattern . . . . . . . . . . . . . . . . .69

delayed calculation . . . . . . . . . . . . . . 100delayed number . . . . . . . . . . . . . . . . . 180delegation based . . . . . . . . . . . . . . . . . . 29demands

unknown . . . . . . . . . . . . . . . . . . . . .119

denotational function definition . . 207denotational semantics . . . . . . . . . . . 204dependency cycle . . . . . . . . . . . . . . . . 263

mutual . . . . . . . . . . . . . . . . . . . . . . . 218dependency specification . . . . . . . . .119dependent type . . . . . . . . . . . . . . . . . . 238depth first search . . . . . . . . . . . . . . . . .119description complexity . . . . . . . . . . . 266design

language . . . . . . . . . . . . . . . . . . . . . 233design pattern . . . . . . . . . . . . . . . . . . . . .69destructive update . . . . . . . . . . .155, 157diagram

class. . . . . . . . . . . . . . . . . . . . . . . . . . .80interaction . . . . . . . . . . . . . . . . . . . . 81notation . . . . . . . . . . . . . . . . . . . . . . . 79object . . . . . . . . . . . . . . . . . . . . . . . . . 80

dichotomy . . . . . . . . . . . . . . 235, 255, 267digital-analog-converter . . . . . . . . . . 116Dijkstra, Edsger W. . . . . . . . . . . . . . . .201direct aliasing . . . . . . . . . . . . . . . . . . . . 156dish . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .60dispatch

multiple . . . . . . . . . . . . . . . . . . . . . .109dispatch strategy . . . . . . . . . . . . . . . . . 113distributed setting . . . . . . . . . . . . . . . 212distribution

workload . . . . . . . . . . . . . . . . . . . . 125division-by-zero . . . . . . . . . . . . . . . . . . .14DNA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179domain

source . . . . . . . . . . . . . . . . . . . . . . . .201target . . . . . . . . . . . . . . . . . . . . . . . . 201

double-dispatch . . . . . . . . . . . . . . . . . .217downward-funarg . . . . . . . . .12, 14, 234drawback . . . . . . . . . . . . . . . . . . . . . . . . 266dual view . . . . . . . . . . . . . . . . . . . . . . . . 267duplication

data . . . . . . . . . . . . . . . . . . . . . . . . . .154Dylan . . . . . . . . . . . . . . . . . . . . . . . . . . . .203DYLAN . . . . . . . . . . . . . . . . . . . . . . . 41, 271dynamic binding . . . . . . . . . . . . . . . . . . 35dynamic paradigm . . . . . . . . . . . . . . .267dynamic programming . . . . . . . . . . .126Dynamic Visitor pattern . . . . . . . . . .218

Page 331: A Functional Pattern System for Object-Oriented Design

Index 313

dynamics . . . . . . . . . . . . . . . . . . . . . . . . . 98Dyson, Nick . . . . . . . . . . . . . . . . . . . . . . . .v

Eeager evaluation . . . . . . . . . . . . . . . 14, 56eager semantics . . . . . . . . . . . . . . . . . . 246eagerness . . . . . . . . . . . . . . . . . . . . . . . . . 14Ebert, Jurgen . . . . . . . . . . . . . . . . . . . . . . . vEC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116efficiency . . . . . . . . . . . . . . . 105, 157, 196

space . . . . . . . . . . . . . . . . . . . . . . . . .126time . . . . . . . . . . . . . . . . . . . . . . . . . .125

efficient value passing . . . . . . . . . . . .154EIFFEL . . . . . . . . . . . .3, 29, 31, 33–35, 41,

43, 79, 85, 91, 96, 105–107, 144,149, 154, 156, 157, 159, 181,182, 191, 198, 214, 219, 236–238, 240, 242, 243, 245, 246,248, 250, 251, 253, 254, 259, 271

Eiffel Booch Components . . . . . . . . .110eight queens problem . . . . . . . . . . 20, 25Einstein, Albert . . . . . . . . . . . . . . . . . . . 21elementary particle . . . . . . . . . . . . . . .267elementary particles . . . . . . . . . . . . . .235elements

heterogeneous. . . . . . . . . . . . . . . .209unstable . . . . . . . . . . . . . . . . . . . . . .205

Eliza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25embedded system . . . . . . . . . . . . . . . . . 31empty constructor . . . . . . . . . . . . . . . .191encapsulation . . 32, 103, 149, 155, 156,

238broken . . . . . . . . . . . . . . . . . . . . . . . 210class. . . . . . . . . . . . . . . . . . . . . . . . . . .33object . . . . . . . . . . . . . . . . . . . . . . . . . 33poor . . . . . . . . . . . . . . . . . . . . . . . . . . 95

encodingHuffman . . . . . . . . . . . . . . . . . . . . . 123

entity object . . . . . . . . . . . . . . . . . . . . . . . .66entity-relationship. . . . . . . . . . . . . . . . .21environment . . . . . . . . . . . . . . . . . . . . . 233equality . . . . . . . . . . . . . . . . . . . . . . . . . .156equality method . . . . . . . . . . . . . . . . . 156equality operation . . . . . . . . . . . 159, 161equals by equals . . . . . . . . . . . . . . . . . . 18

errordivision-by-zero . . . . . . . . . . . . . . . 14initialization . . . . . . . . . . . . . . . . . 197runtime . . . . . . . . . . . . . . . . . . . . . . 217type . . . . . . . . . . . . . . . . . . . . . . . . . .151

error correction unit . . . . . . . . . . . . . . 116error handling . . . . . . . . . . . . . . . . . . . 194error state . . . . . . . . . . . . . . . . . . . . . . . .200Escher, M.C. . . . . . . . . . . . . . . . . . . . . . . .74ET++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75evaluation

eager . . . . . . . . . . . . . . . . . . . . . . . . . .14lazy . . . . . . . . . . . . . . . . . . . . . . .14, 107partial. . . . . . . . . . . . . . .105, 120, 123

event handling . . . . . . . . . . . . . . . . . . .127execution

parallel . . . . . . . . . . . . . . . . . . 125, 242execution profile . . . . . . . . . . . . . . . . . 125existentially quantified . . . . . . . . . . . . 35expanded . . . . . . . . . . . . . . . . . . . . . . . . 246expanded type . . . . . 156, 159, 198, 245explicitness . . . . . . . . . . . . . . . . . . . . . . 102exploration

structure . . . . . . . . . . . . . . . . . . . . . 243exploration . . . . . . . . . . . . . . . . . . . . . . . .177explosion

combinatorial . . . . . . . . . . . . . . . . . 94expression

first-class . . . . . . . . . . . . . . . . . . . . . . 19expression conformance . . . . . . . . . .237extensibility . . . . . . . . . . . . . . . . . . . . . .210extensible language . . . . . . . . . . . . . . 247Extensible Visitor pattern254, 270, 271extension

constructor . . . . . . . . . . . . . . . . . . .239datatype . . . . . . . . . . . . . . . . . . . . . 254functional . . . . . . . . . . . . . . . . . . . . 254interface . . . . . . . . . . . . . . . . . . . . . 197operation . . . . . . . . . . . . . . . . . . . . 239

external . . . . . . . . . . . . . . . . . . . . . . . . . . 104external features . . . . . . . . . . . . . . . . . 203external function . . . . . . . . . . . . . . . . . . 10external functionality . . . . . . . . 204, 209external interpretation . . . . . . . . . . . 203external iteration . . . . . . . . . . . . . . . . . 192

Page 332: A Functional Pattern System for Object-Oriented Design

314 Index

external iterator . . . . . . . . . . . . . . . . . . . 94external polymorphism . . . . . . . . . . 203External polymorphism pattern . . 218extrinsic. . . . . . . . . . . . . . . . . . . . . . . . . . .66extrinsic operation . . . . . . . . . . . . . . . . 99extrinsic properties . . . . . . . . . . . . . . .203

FFacade pattern . . . . . . . . . . . . . . . . . . . 244Facet pattern . . . . . . . . . . . . . . . . . . . . . 218facility inheritance . . . . . . . . . . . . . . . . . . 43Factory Method pattern . 146, 200, 270fat software . . . . . . . . . . . . . . . . . . . . . . 264feature . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33features

external . . . . . . . . . . . . . . . . . . . . . . 203feedback loop . . . . . . . . . . . . . . . . . . . . . 28feedback model . . . . . . . . . . . . . . . . . . . 22fibonacci function . . . . . . . . . . . . . . . . . 23fibonacci number . . . . . . . . .23, 119, 125fibonacci stream . . . . . . . . . . . . . . . . . .125finite stream . . . . . . . . . . . . . . . . . . . . . 128first-class citizens . . . . . . . . . . . . . . . . . 12first-class expression . . . . . . . . . . . . . . 19first-class function . . . . . . . . . . . . . 12, 14fixpoint combinator . . . . . . . . . . . . . . . 47flexibility . . . . . . . . . . . . . . . . . . . . 103, 210

unrestricted . . . . . . . . . . . . . . . . . . . 95Floyd, Robert . . . . . . . . . . . . . . . . . . . . . 75Flyweight pattern . . . . . . . 199, 218, 249fold . . . . . . . . . . . . . . . . 177, 178, 240, 254fold-fusion . . . . . . . . . . . . . . . . . . . . . . . 241folding . . . . . . . . . . . . . . . . . . . . . . . . . . .240foldl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26foldr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26form

functional . . . . . . . . . . . . . . . . . . . . 241FORTRAN . . . . . . . 9, 21, 40, 85, 166, 179FP . . . . . . . . . . . . . . . . . . . . . 11, 20, 89, 241fraction number . . . . . . . . . . . . . . . . . .159fragile base classes . . . . . . . . . . . . . . . . . . 43fragment system . . . . . . . . . . . . . . . . . 259framework . . . . . . . . . . . . . . . . . . . . . . . . 36

black-box . . . . . . . . . . . . . . . . . . . . 252free functions . . . . . . . . . . . . . . . . . . . . . 30

free theorem . . . . . . . . . . . . . . . . . . . . . 241free variable . . . . . . . . . . . . . . . . . . . . . . .13frequency

assignment . . . . . . . . . . . . . . . . . . .154passing . . . . . . . . . . . . . . . . . . . . . . 154update . . . . . . . . . . . . . . . . . . . . . . . 154

friendly software . . . . . . . . . . . . . . . . .265function

accessor . . . . . . . . . . . . . . . . . . . . . . 161anonymous . . . . . . . . . . . . . . . . . . 235combinator . . . . . . . . . . . . . . . . . . . . 13fibonacci . . . . . . . . . . . . . . . . . . . . . . 23first-class . . . . . . . . . . . . . . . . . . . . . . 12generic . . . . . . . . . . . . . . 203, 223, 238gravitational force . . . . . . . 104, 108higher-order . . . . . . . . . . . . . . . . . . .12intrinsic . . . . . . . . . . . . . . . . . . . . . . . 99multi-dispatching . . . . . . . . . . . . 255specialized . . . . . . . . . . . . . . . . . . . 207symmetric . . . . . . . . . . . . . . . . . . . . .99

function abstraction . . . . . . . . . . . . . . . 13function application . . . . . . . . . . . 9, 207function composition . . . . . . . . . 13, 236function definition

denotational . . . . . . . . . . . . . . . . . 207function object

specialized . . . . . . . . . . . . . . . . . . . 203Function Object pattern .87, 90, 92, 96,

98–104, 107, 110–113, 145, 146,161, 168, 188, 189, 208, 217,219, 222–228, 233, 235, 247,252, 261–264, 266, 270

function package . . . . . . . . . . . . . . . . .217concrete . . . . . . . . . . . . . . . . . . . . . .207hiding. . . . . . . . . . . . . . . . . . . . . . . .213

functional . . . . . . . . . . . . . . . . . . . . . . . . . 13curry . . . . . . . . . . . . . . . . . . . . . . . . .235

functional concepts . . . . . . . . . . . . . . . .10functional decomposition . . . . . 10, 235functional extension . . . . . . . . . . . . . .254functional form . . . . . . . . . . . . . . . . . . 241functional paradigm . . . . . . . . . . . . . . . .9functional programming . . . . . . . . . . . 9functional programming language160functional style . . . . . . . . . . . . . . . . . . . .19

Page 333: A Functional Pattern System for Object-Oriented Design

Index 315

functionalityadding . . . . . . . . . . . . . . . . . . . . . . . 203external . . . . . . . . . . . . . . . . . .204, 209

functionsmapping elements to . . . . . . . . . 213stream . . . . . . . . . . . . . . . . . . . . . . . 127

future variable . . . . . . . . . . . . . . . . . . . 106

GGame of Life . . . . . . . . . . . . . . . . . . . . . . . .24Gamma, Erich . . . . . . . . . . . . . . . . . . . . .75garbage collection . . . . . . . . . . . . 10, 247garbage collector . . . . . . . . . . . . . . . . . 157generation

data . . . . . . . . . . . . . . . . . . . . . 123, 246generative system . . . . . . . . . . . . . . . . 221generator operation 154, 155, 157, 158,

160, 161generic function . . . . . . . . .203, 223, 238Generic Function Object pattern. . .89,

219, 227, 228, 238generic parameter . . . . . . . . . . . . . . . .236generic property . . . . . . . . . . . . . . . . . 177getter-routine . . . . . . . . . . . . . . . . . . . . 122glue

between function . . . . . . . . . . . . . . 20functions together . . . . . . . . . . . . . 19results and arguments . . . . . . . . . 19

GOF . . . . . . . . . . . . . . . . . . .75, 76, 79, 221pattern template . . . . . . . . . . . . . . .76

Goldberg, Adele . . . . . . . . . . . . . . . . . . 29goto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .22graph algorithm . . . . . . . . . . . . . . . . . 144graph reduction . . . . . . . . . . . . . . . . . . . 14gravitational force function . . 104, 108Group, GOF . . . . . . . . . . . . . . . . . . . . . 166Group, PoSA . . . . . . . . . . . . . . . . . . . . .221Group, Sather . . . . . . . . . . . . . . . . . . . . 165

Hhamming numbers . . . . . . 117, 118, 135handling

error . . . . . . . . . . . . . . . . . . . . . . . . . 194event . . . . . . . . . . . . . . . . . . . . . . . . .127

Hankin et al., Chris . . . . . . . . . . . . . 3, 39

hardwarevon Neumann . . . . . . . . . . . . . . . . .39

HASKELL . 10, 11, 22, 27, 28, 35, 91, 271Helm, Richard . . . . . . . . . . . . . . . . . . . . 75Henhapl, Wolfgang . . . . . . . . . . . . . . . . vHeraklit . . . . . . . . . . . . . . . . . . . . . . . . . .149heterogeneity . . . . . . . . . . . . . . . . . . . . 157heterogeneous collection . . . . . . . . . 204heterogeneous elements . . . . . . . . . . 209heterogenous list . . . . . . . . . . . . . . . . . . 37hiding

information . . . . . . . . . . . . . . . . . . 123hiding function package . . . . . . . . . .213higher-order function . . . . . . . . . . . . . 12Hoare, C.A.R. . . . . . . . . . . . . . . . . . . . . . 17Hoffmann, H. J. . . . . . . . . . . . . . . . . . . . . 3Hollywood-principle . . . . . . . . . . . . . . 36homogenous list . . . . . . . . . . . . . . . . . . 37homomorphic mapping . . . . . . . . . . 212homomorphic properties . . . . . . . . . 205homomorphic relationship . . . . . . . 201homomorphism . . . . . . . . . . . . . . . . . .201hook method . . . . . . . . . . . . . . . . . . . . . .61HOPE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16hot spots . . . . . . . . . . . . . . . . . . . . . . . . . . . 66hot-spots . . . . . . . . . . . . . . . . . . . . . . . . . .87Hsieh, Liu . . . . . . . . . . . . . . . . . . . . . . . . .74Hudak, Paul . . . . . . . . . . . . . . . . .3, 21, 23Huffman encoding . . . . . . . . . . . . . . . 123Huffman, David . . . . . . . . . . . . . . . . . 123Hughes, John . . . . . . . . . . . 117, 263, 268hylomorphism . . . . . . . . . . . . . . . . . . . 240

II/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22identity . . . . . . . . . . . . . .11, 22, 37, 95, 98Identity Object pattern . .160, 161, 199,

222idiom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75idle behavior . . . . . . . . . . . . . . . . . . . . .199IEEE COMPUTER . . . . . . . . . . . . . . . . . . .2Ihrig, Norbert . . . . . . . . . . . . . . . . . . . . . . vimmutability . . . . . . . . . . . . . . . . . . . . .196immutable argument . . . . . . . . . . . . .158immutable interface . . . . . 151–153, 161

Page 334: A Functional Pattern System for Object-Oriented Design

316 Index

Immutable Object pattern . . . . . . . . 149immutable value . . . . . . . . . . . . . .11, 245impedance mismatch . . . . . . . . . . 22, 68impedance mismatch . . . . . . . . . . . . . . . . 38imperative style . . . . . . . . . . . . . . . . . . . 19implicit binding . . . . . . . . . . . . . . . . . .235implicit creation . . . . . . . . .198, 249, 251implicit initialization . . . . . . . . . . . . . 197implicit state . . . . . . . . . . . . . . . . . . .11, 12In-band . . . . . . . . . . . . . . . . . . . . . . . . . . 127in-order traversal . . . . . . . . . . . . . . . . 227inclusion polymorphism . . . . . . 34, 215incremental update . . . . . . . . . . . . . . 212incrementality . . . . . . . . . . . . . . . 205, 212indirect aliasing . . . . . . . . . . . . . . . . . . 156indirection table . . . . . . . . . . . . . . . . . . .75inefficient . . . . . . . . . . . . . . . . . . . 154, 158infinite data structures . . . . . . . . . . . . 15infinite stream . . . . . . . . . . . . . . . . . . . 128infinite transformations . . . . . . . . . . . 15information hiding . . . . . . . . . . . 32, 123inheritance . . . . . . . . . . . . . . . . . . . . . . . .33

is-a . . . . . . . . . . . . . . . . . . . . . . . 31, 156multiple . . . . . . . . . . . . . . . . . . . . . .197specialization . . . . . . . . . . . . . 31, 156subtyping . . . . . . . . . . . . . . . . . . . . . 31

initial argument . . . . . . . . . . . . . . . . . .106initial arguments . . . . . . . . . . . . . . . . . 106initial value . . . . . . . . . . . . . . . . . . . . . . 122initialization . . . . . . . . . . . . . . . . .122, 246

implicit . . . . . . . . . . . . . . . . . . . . . . 197lazy . . . . . . . . . . . . . . . . . . . . . . . . . . 251reference . . . . . . . . . . . . . . . . . . . . . 198system . . . . . . . . . . . . . . . . . . . . . . . 125value . . . . . . . . . . . . . . . . . . . . . . . . .197

initialization error . . . . . . . . . . . . . . . .197inner class . . . . . . . . . . . . . . . . . . . . . . . 110inner product . . . . . . . . . . . . . . . . . . . . 170Input/Processing/Output model9, 30insertion-sort . . . . . . . . . . . . . . . . . . . . 125insertion-sort . . . . . . . . . . . . . . . . . . . . . . .15InsertSort . . . . . . . . . . . . . . . . . . . . . . . . .242instability . . . . . . . . . . . . . . . . . . . . . . . . 209inter-collection language . . . . . . . . . 124interaction diagram . . . . . . . . . . . . . . . 81

interaction machines . . . . . . . . . . . 38, 72interface

bloated . . . . . . . . . . . . . . . . . . . . . . .203immutable . . . . . . . . . . 151–153, 161mutable . . . . . . . . . . . . . . . . . 151, 152mutating . . . . . . . . . . . . . . . . . . . . . 161node . . . . . . . . . . . . . . . . . . . . . . . . . 202small . . . . . . . . . . . . . . . . . . . . . . . . . . 99

interface extension . . . . . . . . . . . . . . . 197interface pollution . . . . . . . . . . . . . . . 263intermediate structure . . . . . . . 205, 212internal iteration . . . . . . . . . . . . .192, 263internal iterator . . . . . . . . . . . . . . . 94, 104interpretation . . . . . . . . . . . . . . . . . . . . 162

abstract . . . . . . . . . . . . . . . . . . . . . . 201external . . . . . . . . . . . . . . . . . . . . . . 203

interpreter . . . . . . . . . . . . . . . . . . . . . . . 254Interpreter pattern . . . . . . 217, 255, 262intrinsic . . . . . . . . . . . . . . . . . . . . . . . . . . . 66intrinsic function . . . . . . . . . . . . . . . . . . 99intrinsic properties . . . . . . . . . . 203, 218inversion of control . . . . . . . . . . . . . . . 36invocation

uniform . . . . . . . . . . . . . . . . . . . . . . 103is-a . . . . . . . . . . . . . . . . . . . . . . . . . . . 31, 156Islands . . . . . . . . . . . . . . . . . . . . . . . . 42, 266iteration . . . . . . . . . . . . . . . . 104, 124, 163

backward . . . . . . . . . . . . . . . . . . . . 181external . . . . . . . . . . . . . . . . . . . . . . 192internal . . . . . . . . . . . . . . . . . . 192, 263

iteration continuation . . . . . . . . . . . . 180iteration strategy . . . . . . . . . . . . . . . . . 103iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . .88

external . . . . . . . . . . . . . . . . . . . . . . . 94internal . . . . . . . . . . . . . . . . . . . 94, 104Null . . . . . . . . . . . . . . . . . . . . . . . . . .192robust . . . . . . . . . . . . . . . . . . . . . . . .165

iterator abstraction . . . . . . . . . . . . . . . 164iterator adjustment . . . . . . . . . . . . . . .179Iterator pattern . . 88, 94, 112, 145, 166,

177, 188, 200, 268iterator subclass . . . . . . . . . . . . . . . . . . . 95

JJacobson, Ivar . . . . . . . . . . . . . . . . . . . . . 66

Page 335: A Functional Pattern System for Object-Oriented Design

Index 317

JAVA . . .40, 44, 59, 91, 95, 110, 154, 156,214, 236, 244, 245, 272

Johnson, Ralph E. . . . . . . . . . . . . . . . . . 75juxtaposition . . . . . . . . . . . . . . . . . . . . . . 18

KKay, Alan . . . . . . . . . . . . . . . . . . 29, 40, 75Keller, Rudolf K. . . . . . . . . . . . . . . . . . . . 2Kent, W. . . . . . . . . . . . . . . . . . . . . . . . . . . 42keyword parameter . . . . . . . . . . . . . . 108Keyword Parameter pattern . 188, 224Kielman, Thilo . . . . . . . . . . . . . . . . . . . . . vKinds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42King, David . . . . . . . . . . . . . . . . . . . . . .144Knuth, Donald E. . . . . . . . . . . . . . . . . . .viKoschorek, Dirk . . . . . . . . . . . . . . . . . . . .vKruskal’s algorithm . . . . . . . . . . . . . . . 15

Llanguage

extensible . . . . . . . . . . . . . . . . . . . . 247functional programming . . . . . 160inter-collection . . . . . . . . . . . . . . . 124von Neumann . . . . . . . . . . . . . . . .268

language architecture . . . . . . . . . . . . 270language design . . . . . . . . . . . . . . . . . 233language support

Value Object versus . . . . . . . . . . 159late binding . . . . . . . . . . . . . . . . . . . . . . . 35Launchbury, John . . . . . . . . . . . . . . . . 144Lazarus, Mell . . . . . . . . . . . . . . . . . . . . . 93lazy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .180lazy cons . . . . . . . . . . . . . . . . . . . . . . . . . .15lazy evaluation . . . . . . . 14, 56, 100, 107lazy initialization . . . . . . . . . . . . . . . . 251lazy list . . . . . . . . . . . . . . . . . . . . . . . . . . 227lazy memo-function . . . . . . . . . . . . . . . . . 23lazy message mailbox . . . . . . . . . . . . . 59Lazy Object pattern . . . . . . . . . . . . . . .87,

90, 92, 112, 119, 120, 124, 126,130, 145, 146, 160, 162, 174,188, 199, 219, 222–228, 235,243, 244, 252, 253, 262–265, 270

lazy pattern matching . . . . . . . . . . . . . 28lazy pattern matching operator . . . . 28

Lazy Propagator pattern . . . . . . . . . 144lazy result . . . . . . . . . . . . . . . . . . . . . . . 162lazy semantics . . . . . . . . . . . . . . . . . . . 246lazy stream . . . . . . . . . . 87, 188, 227, 243lazy streams . . . . . . . . . . . . . . . . . . . . . . .22lazy tree . . . . . . . . . . . . . . . . . . . . . . . . . 119Lazy/RT pattern . . . . . . . . . . . . . . . . . 160LEDA . . . . . . . . . . . . . . . . . . . . 17, 271, 272LEGO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43lexical scoping . . . . . . . . . . . . . . . . . . . . 59library organization . . . . . . . . . . . . . . 240LIFE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24limitations of functional languages 23limited memory . . . . . . . . . . . . . . . . . .120Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65lingua franca . . . . . . . . . . . . . . 20, 145, 178LINUX . . . . . . . . . . . . . . . . . . . . . . . . . . . . .viLiskov Substitution principle . . . . . 31, 34LISP . . . . . . . . . . . . . . . . . . . . . 41, 267, 271list

lazy . . . . . . . . . . . . . . . . . . . . . . . . . . 227list comprehension . . . . . . . . . . . . . . . . 19local transformation . . . . . . . . . . . . . .207logical structure . . . . . . . . . . . . . . . . . . 211Lovegrove, Gillian L. . . . . . . . . . . . . . . .v

Mmachine code . . . . . . . . . . . . . . . . . . . . 201Macro-Command pattern . . . . . . . . 102magic square . . . . . . . . . . . . . . . . . 172, 186main function . . . . . . . . . . . . . . . . . . . . . 21main-computations . . . . . . . . . . . . . . . . . 58manifest constant . . . . . . . . . . . . . . . . . 18map function . . . . . . . . . . . . . . . . . . . . . .12mapper . . . . . . . . . . . . . . . . . . . . . . . . . . . 88mapping

homomorphic . . . . . . . . . . . . . . . . 212structure preserving . . . . . . . . . . 201

mapping elements to functions . . . 213Mariner space-probe . . . . 165, 176, 241Martin, Robert C. . . . . . . . . . . . . . . . . . .35matching

pattern . . .17, 28, 56–58, 88–90, 246Math

(1/0) . . . . . . . . . . . . . . . . . . . . . . . . . . 14

Page 336: A Functional Pattern System for Object-Oriented Design

318 Index

(2+3) . . . . . . . . . . . . . . . . . . . . . . . . . 14: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15== . . . . . . . . . . . . . . . . . . . . . . . . . . .171&& . . . . . . . . . . . . . . . . . . . . . . . . . . .171◦ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17add. . . . . . . . . . . . . . . . . . . . . . . . . . . .13add back. . . . . . . . . . . . . . . . . . . . . .172addX . . . . . . . . . . . . . . . . . . . . . . . . . . 13And . . . . . . . . . . . . . . . . . 171, 177, 180and. . . . . . . . . . . . . . . . . . . . . . . . . . . .20b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17client . . . . . . . . . . . . . . . . . . . . . . . . . . 28compose. . . . . . . . . . . . . . . . . . . . 13, 15eq. . . . . . . . . . . . . . . . . . . . . . . . . . . . 177even. . . . . . . . . . . . . . . . . . . . . . . .18, 19f . . . . . . . . . . . . . . . . . . . . . 13, 169, 180f ◦ g . . . . . . . . . . . . . . . . . . . . . . . . . 173False. . . . . . . . . . . . . . . . . . . . .177, 180filter . . . . . . . . . . . . . . . . . . . . . . . .18, 19flip . . . . . . . . . . . . . . . . . . . . . . . . . . . 108fold. . . . . . . . . .88, 172, 178, 180, 182fold1 . . . . . . . . . . . . . . . . . . . . . . . . . 172foldl. . . . . . . . . . . . . . . . . . . . . . .26, 180foldr . . . . . . . . . . . . . . . . . . . . . . 26, 180g . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13hd. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15inc . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13inps . . . . . . . . . . . . . . . . . . . . . . . . . . . 25m. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .20map12, 17, 34, 37, 88, 118, 119, 172,

180, 187merge. . . . . . . . . . . . . . . . . . . . . . . . . .28min. . . . . . . . . . . . . . . . . . . . . . . . . . . . 15n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20ones. . . . . . . . . . . . . . . . . . . . . . . . . . . 15p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20queens. . . . . . . . . . . . . . . . . . . . . . . . . 25quicksort. . . . . . . . . . . . . . . . . . . . . . . 24reduce. . . . . . . . . . . . . . . . . . . . . . . . 172reps. . . . . . . . . . . . . . . . . . . . . . . . . . . 25reverse. . . . . . . . . . . . . . . . . . . . . . . .172safe. . . . . . . . . . . . . . . . . . . . . . . . 20, 25select. . . . . . . . . . . . . . . . . . . . . . . . . . 14shows. . . . . . . . . . . . . . . . . . . . . . . . . .23

sneeuq. . . . . . . . . . . . . . . . . . . . . . . . . 25split . . . . . . . . . . . . . . . . . . . . . . . . . . . 24sum. . . . . . . . . . . . . . . . . . . . . . . . . . 169sumlists . . . . . . . . . . . . . . . . . 169, 170take. . . . . . . . . . . . . . . . . . . . . . . . 19, 27transfold. . . . . . . . . . . . . 170–172, 187transfold∗ . . . . . . . . . . . . . . . . . . . . . 173transmap. . . . . . . . . . . . . . . . . . . . . .170transpose. . . . . . . . . . . . . . . . . 169, 170x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13xs′ . . . . . . . . . . . . . . . . . . . . . . . . . . . . .28y . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13zip. . . . . . . . . . . . . . . . . . . . . . . . 20, 169

matrixstream . . . . . . . . . . . . . . . . . . . . . . . 178

matrix multiplication . . . . . . . . 154, 186Memento pattern . . . . . . . . . . . . . . . . 200memoing . . . . . . . . . . . . . . . . . . . . . . . . 126memoization . . . . . . . . . . . . . . . . . 23, 119Memoization pattern . . . . . . . . . . . . .146memory

limited . . . . . . . . . . . . . . . . . . . . . . . 120merge sort . . . . . . . . . . . . . . . . . . . . . . . . 28MergeSort . . . . . . . . . . . . . . . . . . . . . . . . 242message

notification . . . . . . . . . . . . . . . . . . . 113message send . . . . . . . . . . . . . . . . . . . . . 30metaphor

tools and materials . . . . . . . . . . . . 10method . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

equality . . . . . . . . . . . . . . . . . . . . . . 156hook . . . . . . . . . . . . . . . . . . . . . . . . . . 61template . . . . . . . . . . . . . . . . . . . . . . 61

method call . . . . . . . . . . . . . . . . . . . . . . . 30method simplification . . . . . . . . . . . . . 99Meyer, Bertrand . . . . . . . . . . . . . . . . . .238minimum value . . . . . . . . . . . . . . . . . . 126Mittelbach, Frank . . . . . . . . . . . . . . . . . .vimixed scheme . . . . . . . . . . . . . . . . . . . .158mixin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43ML . . . . . . . . . . . 17, 21, 56, 107, 124, 247modeling . . . . . . . . . . . . . . . 154, 157, 161modularity . . . . . . . . . . . . . . . . . . . . . . .123modularization . . . . . . . . . . . . . . . . . . 246module decoupling . . . . . . . . . . . . . . 120

Page 337: A Functional Pattern System for Object-Oriented Design

Index 319

monad . . . . . . . . . . . . . . . . . . . . . . . 22, 268monolithic algorithms . . . . . . . . . . . . . 99multi-dispatch . . . . . . . 37, 109, 203, 238multi-dispatching function . . . . . . . 255multi-media function object . . . . . . . 98multiple inheritance 31, 43, 44, 95, 197multiplication

matrix . . . . . . . . . . . . . . . . . . . 154, 186MUSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22mutable interface . . . . . . . . . . . . 151, 152mutable object . . . . . . . . . . . . . . . . 12, 149mutating interface . . . . . . . . . . . . . . . 161mutator operation . . . . . . .154, 155, 157mutual dependency cycle . . . . . . . . 218MVC-framework . . . . . . . . . . . . . . . . . .99

Nnames

role . . . . . . . . . . . . . . . . . . . . . . . . . . 223namespaces . . . . . . . . . . . . . . . . . . . . . . . 42native translation . . . . . . . . . . . . . . . . . 53Naur, Peter . . . . . . . . . . . . . . . . . . . . . . . .72Nil . . . . . . . . . . . . . . . . . . . . . . . . . . . 89, 191node interface . . . . . . . . . . . . . . . . . . . .202non-intrusive addition . . . . . . . . . . . 212non-reference type . . . . . . . . . . . . . . . 198non-strict . . . . . . . . . . . . . 11, 14, 119, 180

operator . . . . . . . . . . . . . . . . . . . . . . .14semantics . . . . . . . . . . . . . . . . . . . . . 16

non-termination . . . . . . . . . . . . . . . . . . .14non-uniform method names . . . . . .100None . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89normal form . . . . . . . . . . . . . . . . . . . . . . 11normal-order reduction . 11, 14, 15, 56Norvig, Peter . . . . . . . . . . . . . . . . . . . . 270note representation . . . . . . . . . . . . . . .213notification message . . . . . . . . . . . . . .113Null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89Null iterator . . . . . . . . . . . . . . . . . . . . . 192null value . . . . . . . . . . . . . . . . . . . . . . . . 249nullcase . . . . . . . . . . . . . . . . .248, 263, 266number

complex . . . . . . . . . . . . . . . . . 149, 153delayed . . . . . . . . . . . . . . . . . . . . . . 180fibonacci . . . . . . . . . . . . . 23, 119, 125

fraction . . . . . . . . . . . . . . . . . . . . . . 159numbers

hamming . . . . . . . . . . . .117, 118, 135

OOAKLISP . . . . . . . . . . . . . . . . . . . . . . . . . 271object

mutable . . . . . . . . . . . . . . . . . . . . . . 149small . . . . . . . . . . . . . . . . . . . . . . . . .154

object adapterclass versus . . . . . . . . . . . . . . . . . . 129

object containment . . . . . . . . . . . . . . . 159object diagram . . . . . . . . . . . . . . . . . . . . 80object encapsulation . . . . . . . . . . . . . . .33object identity . . . . . . . . . . . . . . . . . . . . .37object-orientation . . . . . . . . . . . . . 29, 196object-oriented bureau . . . . . . . . . . . . 32object-oriented decomposition 32, 235object-oriented paradigm . . . . . . . . . .29object-oriented secretary . . . . . . . . . . 32Objectifier pattern . . . . . . . . . . . . . . . . 111observer . . . . . . . . . . . . . . . . . . . . . . . . . 267Observer pattern . . . 113, 188, 212, 218,

219OMT . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222OMT diagram notation . . . . . . . . . . . . 79One Note Up . . . . . . . . . . . . . . . . . . . . . .70one-trick-pony . . . . . . . . . . . . . . . . . . . . 44OOPSLA ’87 . . . . . . . . . . . . . . . . . . . . . . .75OOPSLA ’97 . . . . . . . . . . . . . . . . . . . . . . . . 2open-closed principle . . . . . . . . . . . . . 44operation

copy . . . . . . . . . . . . . . . . . . . . . . . . . 158equality . . . . . . . . . . . . . . . . . 159, 161extrinsic . . . . . . . . . . . . . . . . . . . . . . .99generator . . 154, 155, 157, 158, 160,

161mutator . . . . . . . . . . . . . 154, 155, 157

operation receiver . . . . . . . . . . . . . . . .152operator

append . . . . . . . . . . . . . . . . . . . . . . . .23OPULUS . . . . . . . . . . . . . . . . . . . . . . . 46–54OPUS . . . . . . . . . . . . . . . . . . . . . . . . . . 46, 48ordered collection . . . . . . . . . . . . . . . . 164ordering

Page 338: A Functional Pattern System for Object-Oriented Design

320 Index

strict . . . . . . . . . . . . . . . . . . . . . . . . . 120ordering of arguments . . . . . . . . . . . . .27ordering of patterns . . . . . . . . . . . . . . . 27organization

library . . . . . . . . . . . . . . . . . . . . . . . 240orthogonality . . . . . . . . . . . . . . . . . . . . . 98Out-of-band . . . . . . . . . . . . . . . . . . . . . 127overhead

closure . . . . . . . . . . . . . . . . . . . . . . . 127space . . . . . . . . . . . . . . . . . . . . . . . . .212

Ppackage

function . . . . . . . . . . . . . . . . . . . . . .217paradigm

dynamic . . . . . . . . . . . . . . . . . . . . . 267paradigm mismatch . . . . . . . . . . . . . . . 22paradigm pattern . . . . . . . . . . . . . . . . 268parallel execution . . . . . . . . 18, 125, 242parallel programming . . . . . . . . . . . . 246parallel-reduce . . . . . . . . . . . . . . . . . . . . 242ParallelTournamentSort . . . . . . . . . . . . 242parameter

default . . . . . . . . . . . . . . . . . . . . . . . 108generic . . . . . . . . . . . . . . . . . . . . . . . 236

parameterization . . . . . . . . . . . . . . . . . . 98partial. . . . . . . . . . . . . . .107, 109, 234

parametric polymorphism 17, 34, 215,237

Pardo, Alberto . . . . . . . . . . . . . . . . . . . . . vParker, Charlie . . . . . . . . . . . . . . . . . . . 221parser . . . . . . . . . . . . . . . . . . . . . . . 120, 254part-object . . . . . . . . . . . . . . . . . . . . . . . 198partial application . . . . . . . . . . . . 13, 103partial evaluation . . . . . . . 105, 120, 123partial parameterization .107, 109, 234partial visitations . . . . . . . . . . . . . . . . 218particle . . . . . . . . . . . . . . . . . . . . . . . . . . 267

cosmic . . . . . . . . . . . . . . . . . . . . . . . . . velementary . . . . . . . . . . . . . . . . . . . 267

particle view . . . . . . . . . . . . . . . . . . . . . 256particles

elementary . . . . . . . . . . . . . . . . . . . 235PASCAL . . . . . . . . . . . . 9, 14, 33, 144, 258passing frequency . . . . . . . . . . . . . . . .154

patternAbstract Factory . . . . . . . . . . . . . .200Acyclic Visitor . . . . . . . . . . . . . . . .218Adapter . . . . . . . . . . . . . 112, 129, 203Bridge . . . . . . . . . . . . . . 102, 188, 200Chain of Responsibility . .113, 163,

188, 200Co-Routine . . . . . . . . . . . . . . . . . . .145Command . . . 87, 99, 101, 109, 111,

112, 146, 199, 261Composite . 113, 146, 163, 188, 197,

199, 218, 249consciousness . . . . . . . . . . . . . . . . . 72Constant Object . . . . . 152, 161, 199Convenience Methods . . . . . . . .109Decorator . . . . . . . . . . . . . . . .162, 203Default Value . . . . . . . . . . . . . . . . 222Dynamic Visitor . . . . . . . . . . . . . . 218Extensible Visitor . . . .254, 270, 271External polymorphism . . . . . . 218Facade . . . . . . . . . . . . . . . . . . . . . . . 244Facet . . . . . . . . . . . . . . . . . . . . . . . . . 218Factory Method . . . . . 146, 200, 270Flyweight . . . . . . . . . . . 199, 218, 249Function Object . . . . .87, 90, 92, 96,

98–104, 107, 110–113, 145, 146,161, 168, 188, 189, 208, 217,219, 222–228, 233, 235, 247,252, 261–264, 266, 270

Generic Function Object . . 89, 219,227, 228, 238

Identity Object . .160, 161, 199, 222Immutable Object . . . . . . . . . . . . 149Interpreter . . . . . . . . . . 217, 255, 262Iterator . . 88, 94, 112, 145, 166, 177,

188, 200, 268Keyword Parameter . . . . . 188, 224language . . . . . . . . . . . . . . . . . . . . . . 71Lazy Object . . . . . . . . . . . . . . . . . . . 87,

90, 92, 112, 119, 120, 124, 126,130, 145, 146, 160, 162, 174,188, 199, 219, 222–228, 235,243, 244, 252, 253, 262–265, 270

Lazy Propagator . . . . . . . . . . . . . 144Lazy/RT . . . . . . . . . . . . . . . . . . . . . 160

Page 339: A Functional Pattern System for Object-Oriented Design

Index 321

Macro-Command . . . . . . . . . . . . 102Memento. . . . . . . . . . . . . . . . . . . . .200Memoization . . . . . . . . . . . . . . . . . 146Objectifier . . . . . . . . . . . . . . . . . . . . 111Observer . . . 113, 188, 212, 218, 219paradigm . . . . . . . . . . . . . . . . . . . . 268Pipes and Filters . . . . . . . . . . . . . 145Procedure Object . . . . . . . . 109, 222proto-pattern . . . . . . . . . . . . . . . . . . 72Prototype . . . . . . . . . . . 113, 219, 222Proxy . . . . . . . . . . . . . . . . . . . .198, 200Serializer . . .124, 145, 179, 188, 218Singleton . . . . . . . 145, 197, 199, 219Singular Object . . . . . .160, 161, 222State . . . . 99, 111, 199, 200, 257, 258Strategy . .87, 99, 111, 161, 189, 200system . . . . . . . . . . . . . . . . . . . . . . . . 71template . . . . . . . . . . . . . . . . . . . . . . 76Template Method36, 43, 57, 61, 94,

200Transfold . . . . . . . . . . 88, 90, 92, 104,

112, 123, 124, 145, 161, 162,169, 173, 174, 177, 179, 181,188, 189, 218, 222–228, 240–243, 247, 252, 253, 262–265, 270

Translator . . . . . . . . . . . . . . . . . . v, 89,90, 92, 145, 162, 188, 204, 205,209, 213, 214, 217–219, 222–225, 227, 228, 238, 241, 252,255, 257, 262, 263, 265, 270

Unshareable Object . . . . . . . . . . .161Value Object . 90, 92, 113, 146, 154,

156, 158–161, 188, 199, 222–225, 227, 228, 236, 246, 252,253, 264, 265, 270

Visitor .110, 112, 188, 200, 203, 209,217, 218, 254, 263, 268, 270, 271

Void Command . . . . . . . . . . . . . . 199Void Memento . . . . . . . . . . . . . . . 200Void State . . . . . . . . . . . . . . . . . . . . 200Void Strategy . . . . . . . . . . . . . . . . .200Void Value . . . . . . v, 88–90, 92, 113,

146, 161, 189, 194–196, 198–200, 219, 222–225, 227, 228,234, 235, 246, 248–253, 262,

263, 265, 266, 270–272pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . 272pattern matching

branch . . . . . . . . . . . . . . . . . . . . . . . 207pattern discovery . . . . . . . . . . . . . . . . . 75pattern matching 16, 17, 27, 28, 56–58,

88–90, 246lazy . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

pattern roles . . . . . . . . . . . . . . . . . . . . . 221patterns

ordering of . . . . . . . . . . . . . . . . . . . . 27pattlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . .86

I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .86III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87IV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .88VI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88VII . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88VIII . . . . . . . . . . . . . . . . . . . . . . . . . . . 88IX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .89

Perlis, Alan J. . . . . . . . . . . . . . . . . . 21, 252persistence . . . . . . . . . . . . . . . . . . 124, 218pipe

stream . . . . . . . . . . . . . . . . . . . . . . . 180pipeline

calculation . . . . . . . . . . . . . . . . . . . 113pipeline-Composite . . . . . . . . . . . . . . 113Pipes and Filters pattern . . . . . . . . . 145PIZZA . . . . . . . . . . . . . . . . . . 235, 239, 272pointer comparison . . . . . 156, 160, 245pointers ≡ jumps . . . . . . . . . . . . . . . . . .17polar coordinates . . . . . . . . . . . . . . . . 156pollution

interface . . . . . . . . . . . . . . . . . . . . . 263polymorphism . . . . . . . . . . . . . . . . . . . . 10

ad-hoc . . . . . . . . . . . . . . . . . . . . . . . . 35external . . . . . . . . . . . . . . . . . . . . . . 203inclusion . . . . . . . . . . . . . . . . . .34, 215parametric . . . . . . . . 17, 34, 215, 237

POOL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35Poole, Ian . . . . . . . . . . . . . . . . . . . . . . . . 160poor encapsulation . . . . . . . . . . . . . . . . 95post-order traversal . . . . . . . . . . . . . . 227

Page 340: A Functional Pattern System for Object-Oriented Design

322 Index

POSTSCRIPT . . . . . . . . . . . . . . . . . . . . . 143predicate calculus . . . . . . . . . . . . . . . . . 18pretty-printing . . . . . . . . . . . . . . . . . . . 202prime numbers . . . . . . . . . . .15, 136, 140primitive recursion . . . . . . . . . . . . . . .242principle type . . . . . . . . . . . . . . . . . . . . . 17problem

eight queens . . . . . . . . . . . . . . . . . . .25samefringe . . . . . . . . . . . . . . . . . . . 227

procedural abstraction . 30, 32, 33, 255procedure

translucent . . . . . . . . . . . . . . . . . . . 219procedure object. . . . . . . . . . . . . . . . . .111Procedure Object pattern . . . . 109, 222processor . . . . . . . . . . . . . . . . . . . . . . . . 270product

inner . . . . . . . . . . . . . . . . . . . . . . . . . 170profile

execution . . . . . . . . . . . . . . . . . . . . 125Program Reference Table . . . . . . . . . . 75programming

dynamic . . . . . . . . . . . . . . . . . . . . . 126parallel . . . . . . . . . . . . . . . . . . . . . . .246

programming discipline . . . . . . . . . . .18programming environment . . . . . . .202programming in the large . . . . . . . . . 32programming in the small. . . . . .21, 32programming language

ADA 95 . . . . . . . . . . . . . . . . . . . . . . . 91ALGOL . . . . . . . . . . . . . . . . . 40, 41, 75ALGOL 60 . . . . . . . . . . . . . . . . . . . .144ALGOL 68 . . . . . . . . . . . . . . . . . . . . 110APL . . . . . . . . . . . . . . . . .179, 186, 187BETA . . . . . .41, 44, 91, 235, 245, 272C . . . . . . . . . . . . . . . . 14, 21, 33, 36, 37C++ . . . .3, 29, 33, 35, 40–42, 59, 75,

91, 95, 102, 105, 110, 154, 157,164, 166, 181, 187, 203, 214,218, 227, 237, 245

CECIL . . . . . . . . . . . . . . . . . . . . 37, 238CLOS . 33, 37, 41, 91, 109, 166, 238,

268, 271COBOL . . . . . . . . . . . . . . . . . . . . . . . . 85DYLAN . . . . . . . . . . . . . . . . . . . 41, 271EIFFEL . . . . . . . . 3, 29, 31, 33–35, 41,

43, 79, 85, 91, 96, 105–107, 144,149, 154, 156, 157, 159, 181,182, 191, 198, 214, 219, 236–238, 240, 242, 243, 245, 246,248, 250, 251, 253, 254, 259, 271

FORTRAN . . . 9, 21, 40, 85, 166, 179FP . . . . . . . . . . . . . . . . . 11, 20, 89, 241HASKELL . 10, 11, 22, 27, 28, 35, 91,

271HOPE . . . . . . . . . . . . . . . . . . . . . . . . . 16JAVA . . . 40, 44, 59, 91, 95, 110, 154,

156, 214, 236, 244, 245, 272LEDA . . . . . . . . . . . . . . . . 17, 271, 272LISP . . . . . . . . . . . . . . . . . .41, 267, 271ML . . . . . . . 17, 21, 56, 107, 124, 247OAKLISP . . . . . . . . . . . . . . . . . . . . . 271PASCAL . . . . . . . . .9, 14, 33, 144, 258PIZZA . . . . . . . . . . . . . . .235, 239, 272POOL . . . . . . . . . . . . . . . . . . . . . . . . . 35SAMPλE . . . . . . . . . . . . . . . . . . . . . . . 22SATHER . 35, 110, 143, 242, 243, 271SCHEME . . . . . . 9, 110, 124, 187, 247SELF . . . . . . . . . . . . . . . . . . . . . . 29, 234SIMULA . 31, 32, 34, 41, 75, 245, 259SISAL . . . . . . . . . . . . . . . . . . . . . . . . . 21SMALLTALK . . . . . . . . . . . . . . . . . . . .v,

29, 31, 41, 44, 59, 75, 91, 95, 98,106, 110, 112, 156, 160, 161, 166,187, 198, 199, 214, 235, 240,243, 254, 259, 262, 268

SMALLTALK-72 . . . . . . . . . . . . . . . .34TRANSFRAME . . . . . . . . . . . . . . . . 272UFO . . . . . . .v, 56, 248–250, 263, 271VISUAL BASIC . . . . . . . . . . . . . . . . . . 1

programming-by-contract . . . . . . . . . 34proliferation

subclass . . . . . . . . . . . . . . . . . . . . . . . 94properties

extrinsic . . . . . . . . . . . . . . . . . . . . . .203intrinsic . . . . . . . . . . . . . . . . . 203, 218

propertygeneric . . . . . . . . . . . . . . . . . . . . . . . 177

protectionaliasing . . . . . . . . . . . . . . . . . . . . . . 154

proto-pattern . . . . . . . . . . . . . . . . . . 72, 85

Page 341: A Functional Pattern System for Object-Oriented Design

Index 323

prototypespecialized function . . . . . . . . . . 217

Prototype pattern . . . . . . . 113, 219, 222prototyping language . . . . . . . . . . . . . 29Proxy pattern . . . . . . . . . . . . . . . .198, 200

Qquantified

existentially . . . . . . . . . . . . . . . . . . . 35universally . . . . . 236, 237, 242, 272

queens . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25quicksort . . . . . . . . . . . . . . . . . . . . . . . . . .20

lazy . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

RRAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54rational number object . . . . . . . . . . . . 33read-only access. . . . . . . . . . . . . . . . . .152readability

code . . . . . . . . . . . . . . . . . . . . . . . . . 234reasoning about programs . . . . . . . . .26reasoning about state . . . . . . . . . . . . . .26recalculation amount . . . . . . . . . . . . .212receiver

operation . . . . . . . . . . . . . . . . . . . . 152recipe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60recompilation . . . . . . . . . . . . . . . 205, 218Rectangle . . . . . . . . . . . . . . . . . . . . . . . . . . 65recursion

primitive . . . . . . . . . . . . . . . . . . . . . 242recursive combinators . . . . . . . . . . . . . 27recursive design . . . . . . . . . . . . . . . . . . .30redefinition

covariant . . . . . . . . . . . . . . . . . . . . .107reduction

applicative-order . . . . . . . . . . . . . . 11reduction semantics . . . . . . . . . . . . . . . 11reduction step . . . . . . . . . . . . . . . . . . . . .11reference

publishing . . . . . . . . . . . . . . . . . . . 245single . . . . . . . . . . . . . . . . . . . . . . . . 161subscribing . . . . . . . . . . . . . . . . . . .245unshareable . . . . . . . . . . . . . . . . . . 161void . . . . . . . . . . . . . . . . . . . . . . . . . .248

reference initialization . . . . . . . . . . . .198

reference semantics . . . . . . 12, 151, 243references

unshareable . . . . . . . . . . . . . . . . . . 245referential transparency . . . . . . . . 18, 68referential transparent . . . . . . . . . . . . .22regression test . . . . . . . . . . . . . . . . 66, 218relations

context . . . . . . . . . . . . . . . . . . . . . . . 258relationship

homomorphic . . . . . . . . . . . . . . . . 201rent income . . . . . . . . . . . . . . . . . . . . . . 203repeated inheritance . . . . . . . . . . . . . . .94repository . . . . . . . . . . . . . . . . . . . . . . . .160requirements

storage . . . . . . . . . . . . . . . . . . 129, 197result

lazy . . . . . . . . . . . . . . . . . . . . . . . . . . 162reusability . . . . . . . . . . . . . . . . . . . . . . . 235reuse . . . . . . . . . . . . . . . . . . . . . . . . . 98, 104

awkward . . . . . . . . . . . . . . . . . . . . . .95reverse assignment attempt . . . . . . 152reverse function . . . . . . . . . . . . . . . . . . . 23reverse logic . . . . . . . . . . . . . . . . . . . . . . 54Reynolds, John C. . . . . . . . . . . . . . . . . . . 3Rhombus . . . . . . . . . . . . . . . . . . . . . . . . . . 65ripple effect . . . . . . . . . . . . . . . . . . . . . . . 67robust iterator . . . . . . . . . . . . . . . . . . . .165role names . . . . . . . . . . . . . . . . . . . . . . . 223roles

pattern . . . . . . . . . . . . . . . . . . . . . . . 221rotate(a) . . . . . . . . . . . . . . . . . . . . . . . . . . . 65RTTI . . . . . . . . . . . . . . . . . . . . . . . . 213, 250rule of three . . . . . . . . . . . . . . . . . . . . . . . 85runtime error . . . . . . . . . . . . . . . . . . . . 217runtime type information . . . . 203, 213Russel, Bertrand . . . . . . . . . . . . . . . . . 261

Ssafe sharing . . . . . . . . . . . . . . . . . . . . . . 157samefringe problem . . . . . . . . . . . . . .227SAMPλE . . . . . . . . . . . . . . . . . . . . . . . . . . . 22Sargeant, John . . . . . . . . . . . . . . . . . . v, 24SATHER . . . . .35, 110, 143, 242, 243, 271scanner . . . . . . . . . . . . . . . . . . . . . . . . . . 120SCHEME . . . . . . . . . .9, 110, 124, 187, 247

Page 342: A Functional Pattern System for Object-Oriented Design

324 Index

schememixed . . . . . . . . . . . . . . . . . . . . . . . . 158

searchdepth first . . . . . . . . . . . . . . . . . . . . 119

secretaryobject-oriented . . . . . . . . . . . . . . . . 32

section . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13security . . . . . . . . . . . . . . . . . . . . . . . . . . 103SELF . . . . . . . . . . . . . . . . . . . . . . . . . .29, 234self call. . . . . . . . . . . . . . . . . . . . . . . . . . . .36semantics

copy . . . . . . . . . . . . . . . . . . . . . . . . . . 88denotational . . . . . . . . . . . . . . . . . 204eager . . . . . . . . . . . . . . . . . . . . . . . . .246lazy . . . . . . . . . . . . . . . . . . . . . . . . . . 246reference . . . . . . . . . . . . . 12, 151, 243strict . . . . . . . . . . . . . . . . . . . . . . . . . . 14target . . . . . . . . . . . . . . . . . . . . . . . . 210value . . . . . . . . . . . . 12, 124, 152, 227

separation . . . . . . . . . . . . . . 104, 196, 210Serializer pattern . . .124, 145, 179, 188,

218setter-routine . . . . . . . . . . . . . . . . . . . . 122shallow copy . . . . . . . . . . . . . . . . . . . . .156Shank, Roger C. . . . . . . . . . . . . . . . . . . . 74share subcomponent . . . . . . . . . . . . . 157sharing

safe . . . . . . . . . . . . . . . . . . . . . . . . . . 157sharing of values . . . . . . . . . . . . . . . . . 160short syntax . . . . . . . . . . . . . . . . . . . . . . .18side-effect . . . . . . . . . . . . . . 12, 14, 16, 18,

23, 25, 26, 42, 55–58, 68, 87, 92,95, 106, 111, 120, 124, 144, 149,151, 152, 155

Sieve of Erathostenes . . . . . . . . . 16, 136silly questions . . . . . . . . . . . . . . . . . . . . .12simplicity . . . . . . . . . . . . . . . . . . . 102, 155SIMULA . . . . 31, 32, 34, 41, 75, 245, 259simulation . . . . . . . . . . . . . . . . . . . . . . . . 32single reference . . . . . . . . . . . . . . . . . . 161single-dispatch . . . . . . . . . . . . . . . 37, 203Singleton pattern . . . 145, 197, 199, 219Singular Object pattern . .160, 161, 222SISAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21skeleton

algorithmic . . . . . . . . . . . . . . . . . . .242Sketchpad . . . . . . . . . . . . . . . . . . . . . . . . .75small interface . . . . . . . . . . . . . . . . . . . . 99small object . . . . . . . . . . . . . . . . . . . . . . 154SMALLTALK v, 29, 31, 41, 44, 59, 75, 91,

95, 98, 106, 110, 112, 156, 160,161, 166, 187, 198, 199, 214,235, 240, 243, 254, 259, 262, 268

SMALLTALK-72 . . . . . . . . . . . . . . . . . . . 34Smith, Ivan . . . . . . . . . . . . . . . . . . . . . . . 38sneeuq . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25Snelting, Gregor . . . . . . . . . . . . . . . . . . . .vsoftware

fat . . . . . . . . . . . . . . . . . . . . . . . . . . . 264friendly . . . . . . . . . . . . . . . . . . . . . . 265

software maintenance . . . . . . . . . . . . . 32solitaire . . . . . . . . . . . . . . . . . . . . . . . . . . . 26source domain . . . . . . . . . . . . . . . . . . . 201source structure . . . . . . . . . 205, 212, 215space constraints . . . . . . . . . . . . . . . . . 206space efficiency . . . . . . . . . . . . . . . . . . 126space overhead . . . . . . . . . . . . . . . . . . 212space-probe

Mariner . . . . . . . . . . . . . 165, 176, 241spanning tree . . . . . . . . . . . . . . . . . . . . . 15specialization inheritance . . . . . 31, 156specialized function . . . . . . . . . . . . . . 207specialized function object . . . . . . . 203specialized function prototype . . . 217specification

dependency . . . . . . . . . . . . . . . . . . 119spouse . . . . . . . . . . . . . . . . . . . . . . . . . . . 246square

magic . . . . . . . . . . . . . . . . . . . . 172, 186Squiggol school . . . . . . . . . . . . . . . . . . . 26squiggolist . . . . . . . . . . . . . . . . . . . . . . . . 241SRU. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .115Standard Template Library . . . . . . . 110start up time . . . . . . . . . . . . . . . . . . . . . 125state

abstract . . . . . . . . . . . . . . . . . .156, 159clean . . . . . . . . . . . . . . . . . . . . . . . . . 124concrete . . . . . . . . . . . . . . . . . . . . . .156error . . . . . . . . . . . . . . . . . . . . . . . . . 200

State pattern 99, 111, 199, 200, 257, 258

Page 343: A Functional Pattern System for Object-Oriented Design

Index 325

state transformer . . . . . . . . . . . . . . . . . 128stateful . . . . . . . . . . . . . . . . . . . . . . . . . . 169static combination . . . . . . . . . . . . . . . . .94static typing . . . . . . . . . . . . . . . . . . . . . 107storage requirements . . . . . . . . 129, 197Strategy pattern . .87, 99, 111, 161, 189,

200stream

fibonacci . . . . . . . . . . . . . . . . . . . . . 125finite . . . . . . . . . . . . . . . . . . . . . . . . . 128infinite . . . . . . . . . . . . . . . . . . . . . . . 128lazy. . . . . . . . . . . . . .87, 188, 227, 243

stream configuration . . . . . . . . . . . . . 125stream functions . . . . . . . . . . . . . . . . . 127stream matrix . . . . . . . . . . . . . . . . . . . . 178stream pipe . . . . . . . . . . . . . . . . . . . . . . 180strict ordering . . . . . . . . . . . . . . . . . . . .120strict semantics . . . . . . . . . . . . . . . . . . . .14string reduction . . . . . . . . . . . . . . . . . . . 14strong static typing . . . . . . . . . . . . . . . .10structure

intermediate . . . . . . . . . . . . . 205, 212logical . . . . . . . . . . . . . . . . . . . . . . . .211source . . . . . . . . . . . . . . .205, 212, 215target . . . . . . . . . . . . . . . 210–212, 215

structure exploration . . . . . . . . . . . . . 243structure preserving mapping . . . . 201structured analysis and design . . 9, 10style

functional . . . . . . . . . . . . . . . . . . . . . 19imperative . . . . . . . . . . . . . . . . . . . . 19

sub-compartment . . . . . . . . . . . . . . . . . 32subclass proliferation . . . . . . . . . . . . . .94subclass responsibility. . . . . . . . . . . . . .200subclassing . . . . . . . . . . . . . . . . . . . . . . . 31subcomponent

share . . . . . . . . . . . . . . . . . . . . . . . . .157subsystem boundaries . . . . . . . . . . . . .32subtype . . . . . . . . . . . . . . . . . . . . . . . . . . . 31subtyping . . . . . . .34, 156, 236, 242, 244superclass . . . . . . . . . . . . . . . . . . . . . . . . .243support

tool . . . . . . . . . . . . . . . . . . . . . . . . . . 266surface-reading-unit . . . . . . . . . . . . . .115symmetric function . . . . . . . . . . . . . . . 99

Synergy . . . . . . . . . . . . . . . . . . . . . . . . . . .59synonym . . . . . . . . . . . . . . . . . . . . . . . . .153syntactic sugar . . . . . . . . . . . . . . . . . . . . 16syntax-directed translation . . . . . . . 201system

fragment . . . . . . . . . . . . . . . . . . . . . 259generative . . . . . . . . . . . . . . . . . . . .221type . . . . . . . . . . . . . . . . . . . . . . . . . .253

system F . . . . . . . . . . . . . . . . . . . . . . . . . . 11system initialization . . . . . . . . . . . . . .125systems model . . . . . . . . . . . . . . . . . . . . 22

Ttabulation . . . . . . . . . . . . . . . . . . . . . . . .126tail suspensions

avoiding . . . . . . . . . . . . . . . . . . . . . 128tail-recursive function . . . . . . . . . . . . . 24take five . . . . . . . . . . . . . . . . . . . . . . . . . . 18takes-a . . . . . . . . . . . . . . . . . . . . . . . 62, 164target domain . . . . . . . . . . . . . . . . . . . . 201target semantics . . . . . . . . . . . . . . . . . .210target structure . . . . . . . . . .210–212, 215template method . . . . . . . . . . . . . . . . . . 61Template Method pattern . . 36, 43, 57,

61, 94, 200termination . . . . . . . . . . . . . . . . . .194, 241termination control . . . . . . . . . . . . . . .246test predicate . . . . . . . . . . . . . . . . . . . . . .96Tetris . . . . . . . . . . . . . . . . . . . . . . . . . . 67, 99theorem

free . . . . . . . . . . . . . . . . . . . . . . . . . . 241Thierbach, Dirk . . . . . . . . . . . . . . . . 46, 48thread

access . . . . . . . . . . . . . . . . . . . . . . . . 158tile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .255tiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .235time constraints . . . . . . . . . . . . . . . . . . 120time efficiency . . . . . . . . . . . . . . . . . . . 125time-dependent planning . . . . . . . . . 22timeless abstraction . . . . . . . . . . . . . . 152token . . . . . . . . . . . . . . . . . . . . . . . .120, 161tool support . . . . . . . . . . . . . . . . . . . . . .266tools and materials metaphor . . 10, 21Torvald, Linus . . . . . . . . . . . . . . . . . . . . .vitournament-sort . . . . . . . . . . . . . . . . . . . 242

Page 344: A Functional Pattern System for Object-Oriented Design

326 Index

transactionsbusiness . . . . . . . . . . . . . . . . . . . . . . .98

transfold . . . . . . . . . . . . . . . . . . . . . . . . . 104Transfold pattern . . . . . . 88, 90, 92, 104,

112, 123, 124, 145, 161, 162,169, 173, 174, 177, 179, 181,188, 189, 218, 222–228, 240–243, 247, 252, 253, 262–265, 270

transformation . . . . . . . . . . . . . . . . . . . 202local . . . . . . . . . . . . . . . . . . . . . . . . . 207

TRANSFRAME . . . . . . . . . . . . . . . . . . . . 272translation

syntax-directed . . . . . . . . . . . . . . .201Translator pattern . . . . . . . . . . . . . . v, 89,

90, 92, 145, 162, 188, 204, 205,209, 213, 214, 217–219, 222–225, 227, 228, 238, 241, 252,255, 257, 262, 263, 265, 270

translucent procedure . . . . . . . . . . . . 219traversal

in-order . . . . . . . . . . . . . . . . . . . . . . 227post-order . . . . . . . . . . . . . . . . . . . .227

traversal algorithm . . . . . . . . . . . . . . . . 96tree

abstract syntax . . . . . . . . . . . . . . . 227lazy . . . . . . . . . . . . . . . . . . . . . . . . . . 119

tree traversal . . . . . . . . . . . . . . . . . . . . . 144TreeSort . . . . . . . . . . . . . . . . . . . . . . . . . . 242tuple-Composite . . . . . . . . . . . . . . . . . 113Turing complete . . . . . . . . . . . . . . . . . . .45Turing machine . . . . . . . . . . . . . . . . . . . 38Turing tarpit . . . . . . . . . . . . . . . . . . . . . . 45Twain, Mark . . . . . . . . . . . . . . . . . . . . . 191type

basic . . . . . . . . . . . . . . . . . . . . .158, 160coinductive . . . . . . . . . . . . . . . . . . 127dependent . . . . . . . . . . . . . . . . . . . 238expanded . . . . . . .156, 159, 198, 245non-reference . . . . . . . . . . . . . . . . 198principle . . . . . . . . . . . . . . . . . . . . . . 17

type abstraction . . . . . . . . . . . . . . . . . . 255type error . . . . . . . . . . . . . . . . . . . . . . . . 151type inference . . . . . . . . . . . . . . . . . 10, 17type system . . . . . . . . . . . . . . . . . . . . . . 253type-checking . . . . . . . . . . . . . . . . . . . . 202

typingstatic . . . . . . . . . . . . . . . . . . . . . . . . . 107

UUFO . . . . . . . . . . v, 56, 248–250, 263, 271UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73understandability . . . . . . . . . . . . . . . . 155unfold . . . . . . . . . . . . . . . . . . . . . . .240, 254uniform invocation . . . . . . . . . . . . . . .103uniformity . . . . . . . . . . . . . . . . . . . . . . . 194unique value instances . . . . . . . . . . . 161unit

DAC . . . . . . . . . . . . . . . . . . . . . . . . . 116deemphasize . . . . . . . . . . . . . . . . . 117error-correction . . . . . . . . . . . . . . .116surface-reading . . . . . . . . . . . . . . .115

universally quantified . . 236, 237, 242,272

UNIX . . . . . . . . . . . . . . . . . . . 126, 143, 264unknown demands. . . . . . . . . . . . . . .119unrestricted flexibility . . . . . . . . . . . . . 95Unshareable Object pattern . . . . . . .161unshareable reference . . . . . . . . . . . . 161unshareable references . . . . . . . . . . . 245unstable elements . . . . . . . . . . . . . . . . 205up-front declaration . . . . . . . . . 119, 246update

destructive . . . . . . . . . . . . . . 155, 157incremental . . . . . . . . . . . . . . . . . . 212

update frequency . . . . . . . . . . . . . . . . 154updateable view . . . . . . . . . . . . . . . . . 267upper bound . . . . . . . . . . . . . . . . 123, 246upward-funarg . . . . . . . . . . . . . . . . 12, 14

Vvalue

immutable . . . . . . . . . . . . . . . . . . . 245initial . . . . . . . . . . . . . . . . . . . . . . . . 122null . . . . . . . . . . . . . . . . . . . . . . . . . . 249

value cache . . . . . . . . . . . . . . . . . . . . . . 146value initialization . . . . . . . . . . . . . . . 197value instances

unique . . . . . . . . . . . . . . . . . . . . . . . 161Value Object pattern . . . . . . . . . . . . . . 90,

92, 113, 146, 154, 156, 158–161,

Page 345: A Functional Pattern System for Object-Oriented Design

Index 327

188, 199, 222–225, 227, 228,236, 246, 252, 253, 264, 265, 270

Value Object versus language support159

value passingefficient . . . . . . . . . . . . . . . . . . . . . . 154

value semantics . . . . . .12, 124, 152, 227values

sharing of . . . . . . . . . . . . . . . . . . . . 160when to copy . . . . . . . . . . . . . . . . .158

view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162dual . . . . . . . . . . . . . . . . . . . . . . . . . .267particle . . . . . . . . . . . . . . . . . . . . . . .256updateable . . . . . . . . . . . . . . . . . . . 267wave . . . . . . . . . . . . . . . . . . . . . . . . .256

Visitor pattern . 110, 112, 188, 200, 203,209, 217, 218, 254, 263, 268,270, 271

VISUAL BASIC . . . . . . . . . . . . . . . . . . . . . .1VISUALWORKS . . . . . . . . . . . . . . 199, 263Vlissides, John . . . . . . . . . . . . . . . . . . . . 75Void . . . . . . . . . . . . . . . . . . . . . . . . . . 89, 192Void Command pattern . . . . . . . . . . 199Void Memento pattern . . . . . . . . . . . 200void reference . . . . . . . . . . . . . . . . . . . .248Void State pattern . . . . . . . . . . . . . . . . 200Void Strategy pattern . . . . . . . . . . . . .200Void Value pattern . . v, 88–90, 92, 113,

146, 161, 189, 194–196, 198–200, 219, 222–225, 227, 228,234, 235, 246, 248–253, 262,263, 265, 266, 270–272

von Neumann architecture . . . . . . . . 40von Neumann bottleneck . . . . . . . . . .40von Neumann hardware . . . . . . . . . . 39von Neumann language . . . . . . . . . .268

WWadler, Philip . . . . . . . . . . . . . . . . . . . . . 68wave . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267wave view . . . . . . . . . . . . . . . . . . . . . . . 256weak-head-normal-form . . . . . . . 49, 50weakest precondition . . . . . . . . . . . . . 18Wegner, Peter . . . . . . . . . . . . . . 38, 72, 73Weide, Bruce W. . . . . . . . . . . . . . . . . . . . . 2

Weinberg, Jerry . . . . . . . . . . . . . . . . . . . 69Weizenbaum, Joseph . . . . . . . . . . . . . . 25when to copy values . . . . . . . . . . . . . 158white-box reuse . . . . . . . . . 37, 43, 64, 87Who is who

Abelson, Hal . . . . . . . . . . . . . . . . . 273Alexander, Christopher . 69, 74, 86Allen, Fred . . . . . . . . . . . . . . . . . . . 163Appel, Andrew W. . . . . . . . . . . . . 22Auer, Ken . . . . . . . . . . . . . . . . . . . . 251Austern, Matthew . . . . . . . . . . . . 149Backus, John 20, 40, 41, 58, 89, 184,

268Barton, Bob . . . . . . . . . . . . . . . . . . . .30Bauer, Friedrich L. . . . . . . . . . . . . . 74Bosch, Jan . . . . . . . . . . . . . . . . . . . . 270Budd, Timothy . . . .3, 179, 267, 271Burke, Edmund . . . . . . . . . . . . . . . . vBusch, Wilhelm . . . . . . . . . . . . . . .115Conway, John Horton . . . . . . . . . 24Cook, Rich . . . . . . . . . . . . . . . . . . . . 41Cook, William . . . . . . . . . . . . . . . .267Coplien, James O. . . . . . . . . . . 73, 75Cunningham, Ward . . . . . . . . . . . 75Curry, Haskell B. . . . . . . . . . . . . . . 13da Vinci, Leonardo . . . . . . . . . . . . . 4Dijkstra, Edsger W. . . . . . . . . . . . 201Dyson, Nick . . . . . . . . . . . . . . . . . . . .vEbert, Jurgen . . . . . . . . . . . . . . . . . . . vEinstein, Albert . . . . . . . . . . . . . . . .21Escher, M.C. . . . . . . . . . . . . . . . . . . .74Floyd, Robert . . . . . . . . . . . . . . . . . .75Gamma, Erich . . . . . . . . . . . . . . . . . 75Goldberg, Adele . . . . . . . . . . . . . . .29Group, GOF . . . . . . . . . . . . . . . . . .166Group, PoSA . . . . . . . . . . . . . . . . . 221Group, Sather . . . . . . . . . . . . . . . . 165Hankin et al., Chris . . . . . . . . . .3, 39Helm, Richard . . . . . . . . . . . . . . . . .75Henhapl, Wolfgang . . . . . . . . . . . . .vHeraklit . . . . . . . . . . . . . . . . . . . . . . 149Hoare, C.A.R. . . . . . . . . . . . . . . . . . 17Hoffmann, H. J. . . . . . . . . . . . . . . . . .3Hsieh, Liu . . . . . . . . . . . . . . . . . . . . . 74Hudak, Paul . . . . . . . . . . . . . 3, 21, 23

Page 346: A Functional Pattern System for Object-Oriented Design

328 Index

Huffman, David . . . . . . . . . . . . . .123Hughes, John . . . . . . . .117, 263, 268Ihrig, Norbert . . . . . . . . . . . . . . . . . . vJacobson, Ivar . . . . . . . . . . . . . . . . . 66Johnson, Ralph E.. . . . . . . . . . . . . .75Kay, Alan . . . . . . . . . . . . . . .29, 40, 75Keller, Rudolf K. . . . . . . . . . . . . . . . .2Kent, W. . . . . . . . . . . . . . . . . . . . . . . .42Kielman, Thilo . . . . . . . . . . . . . . . . . vKing, David . . . . . . . . . . . . . . . . . . 144Knuth, Donald E. . . . . . . . . . . . . . . viKoschorek, Dirk . . . . . . . . . . . . . . . . vLaunchbury, John . . . . . . . . . . . . 144Lazarus, Mell . . . . . . . . . . . . . . . . . .93Lovegrove, Gillian L. . . . . . . . . . . . vMartin, Robert C. . . . . . . . . . . . . . . 35Meyer, Bertrand . . . . . . . . . . . . . . 238Mittelbach, Frank . . . . . . . . . . . . . . viNaur, Peter . . . . . . . . . . . . . . . . . . . . 72Norvig, Peter . . . . . . . . . . . . . . . . .270Pardo, Alberto. . . . . . . . . . . . . . . . . .vParker, Charlie . . . . . . . . . . . . . . . 221Perlis, Alan J. . . . . . . . . . . . . . 21, 252Poole, Ian . . . . . . . . . . . . . . . . . . . . 160Reynolds, John C.. . . . . . . . . . . . . . .3Russel, Bertrand . . . . . . . . . . . . . .261Sargeant, John . . . . . . . . . . . . . . .v, 24Shank, Roger C. . . . . . . . . . . . . . . . 74Smith, Ivan . . . . . . . . . . . . . . . . . . . .38Snelting, Gregor . . . . . . . . . . . . . . . .vThierbach, Dirk . . . . . . . . . . . . 46, 48Torvald, Linus . . . . . . . . . . . . . . . . . viTwain, Mark . . . . . . . . . . . . . . . . . 191Vlissides, John . . . . . . . . . . . . . . . . .75Wadler, Philip . . . . . . . . . . . . . . . . . 68Wegner, Peter . . . . . . . . . . .38, 72, 73Weide, Bruce W. . . . . . . . . . . . . . . . . 2Weinberg, Jerry . . . . . . . . . . . . . . . .69Weizenbaum, Joseph . . . . . . . . . . 25Wirfs-Brock et al., Rebecca . . . . . . 2Wirth, Nicklaus . . . . . . . . . . . . . . . . .4Wittgenstein, Ludwig . . . . . . . . 233

Wirfs-Brock et al., Rebecca . . . . . . . . . .2Wirth, Nicklaus . . . . . . . . . . . . . . . . . . . . 4Wittgenstein, Ludwig . . . . . . . . . . . . 233

workload distribution . . . . . . . . . . . . 125