Visitor: Just Do It Didier Verna

Introduction C++ Revisiting the Visitor: the “Just Do It” LISP

Step 1: plain LISP Step 2: brute force Step 3: first class Step 4: mapping Didier Verna Step 5: generic map

State Step 6: objects [email protected] Step 7: closures http://www.lrde.epita.fr/˜didier step 8: visit schemes

Conclusion ACCU 2009 – Friday, April 24th

1/25 Introduction

Visitor: Just Do It Necessary literature Didier Verna The GOF Book: Design , Elements of Introduction Reusable Object-Oriented Software. Gamma, Helm, C++

LISP Johnson, Vlissides.

Step 1: plain LISP Step 2: brute force The POSA Book: Pattern-Oriented Software Step 3: first class Step 4: mapping Architecture. Buschmann, Meunier, Rohnert, Step 5: generic map Sommerlad, Stal. State Step 6: objects Step 7: closures step 8: visit schemes What is a software ?

Conclusion I Context (POSA) I Problem I Solution I Consequences (GOF)

2/25 A constatation

Visitor: Just Do It Peter Norvig (Object World, 1996) Didier Verna About the GOF book: Introduction

C++ 16 of 23 patterns are either invisible or simpler

LISP [...] in Dylan or Lisp

Step 1: plain LISP Step 2: brute force Step 3: first class Step 4: mapping Step 5: generic map Peter Norvig is right, so State I is the GOF book (70%) wrong ? Step 6: objects Step 7: closures I are patterns (70%) useless ? step 8: visit schemes

Conclusion

3/25 Some clues from the GOF book itself

Visitor: Just Do It Didier Verna Although describe object-oriented designs, they are based on Introduction practical solutions that have been implemented in C++ mainstream object-oriented programming LISP Step 1: plain LISP languages [. . . ] Step 2: brute force Step 3: first class Step 4: mapping Step 5: generic map State Similarly, some of our patterns are supported Step 6: objects Step 7: closures directly by the less common object-oriented step 8: visit schemes languages. Conclusion

I That’s what people usually miss

4/25 Patterns descriptions / organizations

Visitor: Just Do It GOF: Creational, Structural, Behavioral

Didier Verna I usage-oriented

Introduction POSA: Architectural, Design, Idioms

C++ I abstraction-oriented

LISP

Step 1: plain LISP Step 2: brute force Idioms according to POSA Step 3: first class Step 4: mapping An idiom is a low-level pattern specific to a Step 5: generic map

State programming language. An idiom describes how to Step 6: objects Step 7: closures implement particular aspects of components or the step 8: visit schemes relationships between them using the features of Conclusion the given language. [. . . ] They address aspects of both design and implementation.

I GOF’s design patterns are closer to POSA’s idioms

5/25 The risk: blind pattern application

Visitor: Just Do It POSA’s advice: Didier Verna [. . . ] sometimes, an idiom that is useful for one Introduction programming language does not make sense into C++ another. LISP

Step 1: plain LISP Step 2: brute force Step 3: first class Step 4: mapping GOF’s Visitor example: Step 5: generic map

State Use the when [. . . ] many distinct Step 6: objects Step 7: closures and unrelated operations need to be performed on step 8: visit schemes objects in an object structure, and you want to Conclusion avoid “polluting” their classes with these operations.

I But who said operations belong to classes ?

6/25 Table of contents

Visitor: Just Do It Didier Verna 1 Visiting in C++

Introduction 2 Visiting in LISP C++ ISP LISP Step 1: plain L Step 1: plain LISP Step 2: brute force visiting Step 2: brute force Step 3: first class Step 3: first class generic functions Step 4: mapping Step 5: generic map Step 4: mapping State Step 6: objects Step 5: generic mapping Step 7: closures step 8: visit schemes

Conclusion 3 Visiting with state Step 6: objects Step 7: lexical closures Step 8: dynamic visitation schemes

7/25 Visiting in C++

Visitor: Just Do It Problems: Didier Verna Original hierarchy R/O Introduction

C++ Abstract the visiting process away

LISP

Step 1: plain LISP Step 2: brute force Solution: Step 3: first class Step 4: mapping 1 Equip original hierarchy for visits Step 5: generic map

State I A Visitable abstract class Step 6: objects I An accept method in each visitable component Step 7: closures step 8: visit schemes 2 Write independent visitors Conclusion I A Visitor abstract class I A visit method for each visitable component

9/25 Step 1: plain LISP

Visitor: Just Do It Classes Didier Verna ( defclass class (superclass1 superclass2 ...) Introduction ((slot :initform

:initarg :slot :accessor slot) ...) C++ options ...)

LISP ( make−instance ’class :slot ...) Step 1: plain LISP Step 2: brute force Step 3: first class Step 4: mapping Step 5: generic map Generic functions, methods State ( defgeneric func (arg1 arg2 ...) Step 6: objects (:method ((arg1 class1) arg2 ...) Step 7: closures step 8: visit schemes body ) options ...) Conclusion ( defmethod func ((arg1 class1) arg2 ...) body )

I Methods are outside the classes (ordinary function calls) I Multiple dispatch (multi-methods)

11/25 Summary of step 1

Visitor: Just Do It 1 Original hierarchy untouched

Didier Verna I Generic function model (outside the classes)

Introduction 2 Abstract the visiting process away

C++ I Still needs to be done

LISP

Step 1: plain LISP Step 2: brute force Step 3: first class Step 4: mapping Step 5: generic map

State Step 6: objects Step 7: closures step 8: visit schemes

Conclusion

12/25 Step 2: brute force visiting

Visitor: Just Do It Abstract the visiting process away

Didier Verna I OK: the accept generic function

Introduction C++ But what’s wrong with this picture ?

LISP Step 1: plain LISP obj visitor Step 2: brute force Step 3: first class Step 4: mapping Step 5: generic map

State Step 6: objects accept visit Step 7: closures step 8: visit schemes obj visitor Conclusion obj visitor obj visitor ......

I One indirection too many

13/25 Step 3: first class (generic) functions

Visitor: Just Do It Notion of first class / order Didier Verna (Christopher Strachey, 1916–1975) Introduction storage (in variables) C++ aggregation (in structures) LISP

Step 1: plain LISP Step 2: brute force argument (to functions) Step 3: first class Step 4: mapping return value (from functions) Step 5: generic map

State anonymous manipulation Step 6: objects Step 7: closures dynamic creation step 8: visit schemes

Conclusion ...

I Generic functions are first class objects in LISP

14/25 The better picture

Visitor: Just Do It Didier Verna obj

Introduction

C++

LISP Step 1: plain LISP accept visitor Step 2: brute force Step 3: first class obj Step 4: mapping obj Step 5: generic map obj State ... Step 6: objects Step 7: closures step 8: visit schemes

Conclusion Retrieving function objects in LISP

(function func) ;; => # # ’ func ;; => #

15/25 Step 4: mapping

Visitor: Just Do It Prominent concept in

Didier Verna I Along with folding (reduction), filtering etc.

Introduction Thanks to first class functions

C++ I Argument passing

LISP

Step 1: plain LISP Step 2: brute force Typical mapping example Step 3: first class Step 4: mapping "foo" "bar" "baz" Step 5: generic map ( mapcar # ’ string−upcase ’ ( )) ;; =>("FOO""BAR""BAZ") State Step 6: objects Step 7: closures step 8: visit schemes I “visiting” is a form of structural mapping Conclusion

16/25 Step 5: generic mapping

Visitor: Just Do It Having to specialize mapobject is boring

Didier Verna I Mapping over lists, vectors, arrays, even class slots

Introduction should be written only once

C++

LISP The CLOS Meta-Object Protocol (MOP)

Step 1: plain LISP Step 2: brute force CLOS itself is object-oriented Step 3: first class Step 4: mapping I The CLOSMOP: a de facto implementation standard Step 5: generic map I The CLOS components (classes etc.) are State Step 6: objects (meta-)objects of some (meta-)classes Step 7: closures step 8: visit schemes We have reflexive (introspective) access to class slots Conclusion I

17/25 Step 6: objects

Visitor: Just Do It How about a component counter visitor ? Didier Verna C++: left as an exercise. . . Introduction

C++ LISP: how does that fit with first class functions ?

LISP I Global state (yuck !) Step 1: plain LISP I Behavior + state = objects ! Step 2: brute force Step 3: first class Step 4: mapping So we’re back to visitor objects ? Step 5: generic map State There has got to be a better way. . . Step 6: objects I Step 7: closures step 8: visit schemes

Conclusion

19/25 Step 7: lexical closures

Visitor: Just Do It Behavior + State without the OO machinery Didier Verna

Introduction Typical functional example (with anonymous function) C++ ( defun make−adder ( n ) LISP (lambda (x) (+ n x)))

Step 1: plain LISP Step 2: brute force ( funcall ( make−adder 3) 5) ;; =>8 Step 3: first class Step 4: mapping Step 5: generic map

State Closures with mutation (impure functional programming) Step 6: objects Step 7: closures ( l e t (( count 0 ) ) step 8: visit schemes ( defun increment () ( incf count ))) Conclusion (increment) ;; =>1 (increment) ;; =>2 ;;...

20/25 Step 8: dynamic visitation schemes

Visitor: Just Do It How about a component nesting counter visitor ? Didier Verna C++: left as an exercise. . . Introduction

C++ LISP: modification of the visit process required

LISP 1 increment nesting level before visiting an object Step 1: plain LISP 2 actual visit Step 2: brute force Step 3: first class 3 decrement nesting level afterwards Step 4: mapping Step 5: generic map Do we need a dedicated mapobject for that ? State Step 6: objects Step 7: closures No ! We have the MOP’s generic function protocol step 8: visit schemes I

Conclusion

21/25 The generic function protocol

Visitor: Just Do It Generic function invocation Didier Verna Before Method Introduction

C++

LISP

Step 1: plain LISP call−next−method Return value Step 2: brute force Around Method Primary Method Step 3: first class Step 4: mapping Step 5: generic map

State Step 6: objects Step 7: closures After Method step 8: visit schemes

Conclusion Methods are CLOS meta-objects Methods can be added/removed dynamically

22/25 Summary

Visitor: Just Do It Decoupling from original hierarchy: n/a

Didier Verna I Generic function model (outside the classes)

Introduction Visiting infrastructure:

C++ I First class generic functions (as argument) LISP I CLOSMOP (introspection) Step 1: plain LISP Step 2: brute force Generic machinery in 10 lines of code Step 3: first class I Step 4: mapping Step 5: generic map Visiting with state:

State I Lexical closures Step 6: objects I First class functions (anonymous) Step 7: closures step 8: visit schemes I Generic function protocol (before/after)-methods Conclusion I 5–10 more lines of code (original code untouched)

23/25 Conclusion The “iceberg” metaphor

Visitor: Just Do It Didier Verna Programmer’s Application Lisp

Introduction C++ What you need to write

LISP

Step 1: plain LISP Notice the difference ? Step 2: brute force DESIGN PATTERN Step 3: first class Step 4: mapping Step 5: generic map

State Step 6: objects What’s already here Step 7: closures step 8: visit schemes

Conclusion

Programming Language

24/25 Next LISP Events

Visitor: Just Do It ELS’09: 2nd European LISP Symposium Didier Verna May 27-29 2009, Milan, Italy

Introduction http://www.european-lisp-symposium.org C++ ELW’09: 6th European LISP Workshop LISP

Step 1: plain LISP July 6 2009, Genova, Italy Step 2: brute force Step 3: first class co-located with ECOOP. Step 4: mapping http://elw.bknr.net/2009 Step 5: generic map

State Step 6: objects Step 7: closures step 8: visit schemes

Conclusion

25/25