Mfps Visitors

Embed Size (px)

Citation preview

  • 7/26/2019 Mfps Visitors

    1/20

    MFPS 2005

    A Type-theoretic Reconstruction of the VisitorPattern

    Peter Buchlovsky 1

    Computer LaboratoryUniversity of Cambridge

    Cambridge CB3 0FD, United Kingdom

    Hayo Thielecke 2

    School of Computer ScienceUniversity of Birmingham

    Birmingham B15 2TT, United Kingdom

    Abstract

    In object-oriented languages, the Visitor pattern can be used to traverse tree-likedata structures: a visitor object contains some operations, and the data structureobjects allow themselves to be traversed by accepting visitors. In the polymorphiclambda calculus (System F), tree-like data structures can be encoded as polymorphic

    higher-order functions. In this paper, we reconstruct the Visitor pattern from thepolymorphic encoding by way of generics in Java. We sketch how the quantifiedtypes in the polymorphic encoding can guide reasoning about visitors in general.

    Key words: Visitor pattern, polymorphic types, ob ject-orientedprogramming, Generic Java

    1 Introduction

    Tree-like data structures, such as abstract syntax trees or binary trees, andtheir traversal (often called tree walking) are ubiquitous in programming.Modern functional languages, such as ML and Haskell, provide constructsin the form of datatype definitions and pattern matching to deal with treesand more general recursive datatypes. However, in object-oriented languagessuch as Java, the situation is more complicated. Testing class membershipand branching on it is widely considered a violation of object-oriented style.

    1 Email: [email protected] Email: [email protected]

    This paper is electronically published inElectronic Notes in Theoretical Computer Science

    URL: www.elsevier.nl/locate/entcs

  • 7/26/2019 Mfps Visitors

    2/20

    Buchlovsky and Thielecke

    Instead, the Visitor pattern [6] has been proposed to define operations ontree-like inductive datatypes.

    The simplest example using visitors is that of a sum type of the formA+B.Since Java lacks a sum type, the Visitor pattern implements a class doing thejob of a sum type by a sort of double-negation transform. Concretely, the class

    has a method for accepting a visitor; the visitor itself has two methods, oneaccepting arguments of typeA, the other of type B. If we take a very idealizedview by taking methods as functions and objects as tuples of such functions,we can regard the above as an instance of a well-known isomorphism in thepolymorphic -calculus[8]:

    A+B =. ((A)(B))

    These isomorphisms are firmly grounded in programming language theory.Via the Curry-Howard correspondence, they also form part of a bigger picture

    in terms of the definability of logical connectives such as disjunction in higher-order logics.

    The present paper aims to flesh out this type-theoretic view of visitors.Specifically, a Design Pattern (such as the aforementioned Visitor pattern),by its very nature, is not so much a single unambiguous definition, but avariety of related instances. Hence we aim to clarify how variants of the Visitorpattern relate to the more idealized type-theoretic picture. We classify visitorsas internal or external, depending on whether the visitor itself or the treespecifies the traversal. Moreover these can be either functional (by returninga value) or imperative (having a voidreturn type and side-effects instead).

    The resemblance of variants of the Visitor pattern to the encoding of algeb-raic types into System F may be part of the type-theoretic folklore. However,formulating this precisely seems to be very useful, given the possibility ofsome technology transfer from the highly developed theory of polymorphiclambda calculi to less rigorous, but widely used design patterns. Our point isnot to translate object-oriented languages into functional ones or conversely.Rather, we can see that the same notion has a manifestation in both of thesevery different scenarios.

    The contributions of this paper include the following:

    We present a stylized polymorphic -calculus to make the relation of poly-

    morphic encodings to visitors perspicuous. We show the type soundness of the resulting visitors in Featherweight Gen-

    eric Java[9].

    We sketch how this abstract view of visitors could be useful for reasoningabout visitors.

    For completeness, an appendix (SectionA) gives more details on Feather-weight Generic Java. These details are not essential for understanding thepaper.

    2

  • 7/26/2019 Mfps Visitors

    3/20

    Buchlovsky and Thielecke

    2 Background

    We briefly recall the two areas of relevant background between which we aimto bridge: visitors as presented in the Design Patterns literature [6], andpolymorphic lambda calculi[7].

    We find it useful to give some object-oriented terminology:

    Interface A fully abstract class. Defines a set of methods but does not givetheir bodies. This corresponds to an ML signature.

    Class A class may implement an interface by providing a body for eachmethod header in the interface. This corresponds to an ML structure.

    We will consider Generic Java [3] (Java extended with type parameters onclasses, interfaces and methods) throughout. Briefly, the syntax is as follows.An interface or class definition of the form class C{...}defines a classC parameterized by the type variable . The type C instantiates the

    type parameter of class C to Int. A method definition of the form Tm(...){...}defines a method m in which the type variable is universallyquantified. A call to method m of the form o.m(...) instantiates toInt.

    The purpose of the Visitor pattern is to organize a program around the op-erations on a datatype as opposed to the constructors. The canonical exampleof the Visitor pattern consists of abstract syntax trees and their traversal byvarious phases of a compiler; this approach is used in SableCC[5]. We willconsider a simpler example based on binary trees of integer leaves and theoperation of summing up the leaves.

    The standard object-oriented implementation of binary trees is based aroundthe Composite pattern [6]. The datatype signature (sometimes called ele-ment) is represented as an interface and the constructor for each variant ofthe datatype becomes a class (sometimes called concrete element) whichimplements the interface. The interface includes method headers that specifythe signatures of all operations on the data. This forces each constructor classto provide a method to handle the appropriate case of the operation.

    This approach permits new datatype variants to be added without modi-fying existing code, something that is not possible in ML. The disadvantageis that adding new operations is difficult as every existing class has to be

    amended. The Visitor pattern turns the situation around. It becomes easy toadd new operations but difficult to add new variants.

    The Visitor pattern is used as follows. Every operation on the datatypeis packaged into a concrete visitorclass. The case for handling each variantis contained in a method typically named visitConswhere Consis the nameof the constructor class for that variant. Every concrete visitor implements avisitor interface. This specifies the types of visit methods that must be presentin a concrete visitor. It can be seen as a signature for concrete visitors.

    The constructor classes of the datatype are modified to include an accept

    3

  • 7/26/2019 Mfps Visitors

    4/20

    Buchlovsky and Thielecke

    method. Its role is to accept a visitor and call the visit method for the variantwhich the class implements. This is essentially a form of double dispatch onthe datatype variant and the visit method. Any components of the variantstored in fields inside the class must be passed to the visit method. It is alsonecessary to parameterize the accept methods and visitor interface since we

    cannot know in advance the type yielded by any concrete visitor class.To summarize, the visitor pattern consists of the following classes:

    Visitor This is an interface for visitors. It declares visit methods namedvisitCons for each Cons class.

    ConcreteVisitor One class for each operation on the data. It implementsthe Visitor interface and has to provide implementations for each of thevisit methods declared there.

    Data/Element This is an interface naming a datatype (e.g. BinTree). Itdeclares an accept method which takes a Visitor object as an argument.

    ConcreteData/ConcreteElement A Cons class for each variant of thedatatype (e.g. Leaf). This corresponds to an ML datatype constructornamed Cons. It implements the accept method that calls visitCons in theVisitor object.

    The Visitor pattern does not prescribe where a visitor should store interme-diate results or how it should return the result to the caller. We will distinguishbetween functional visitors which return intermediate results through the res-ult of the call to accept and imperative visitors which accumulate results insome field inside the visitor.

    Another aspect of the Visitor pattern is the choice of traversal strategies

    for composite objects. We could put the traversal code in the datatype. To dothis we ensure that the accept method is called recursively on any componentobjects and passes the results to the visitor in the call to visit. Alternatively,we could put the traversal code in the visitor itself. We will refer to these asinternal and external visitors respectively, by analogy with internal andexternal iterators [6].

    We will use the polymorphic lambda calculus (System F) to encode datatypes. In fact, we need a more powerful extension of System F with poly-morphic type constructors, called F, since it allows us to approximate gen-erics and interfaces better than we could with System F alone. Most features

    of this system will not be new to anyone familiar with advanced languageslike Haskell or ML: intuitively, F contains polymorphic functions and alsopolymorphic type constructors.

    See Figure1for a fairly standard presentation of F extended with finiteproducts. We write / to mean that is not among the free variablesin . We abbreviate ::.T as . T and similarly for ::.T. Emptyproducts are written as 1. We will also write ti for the projection it anda, b:AB .t for x :AB .[a1x, b2x] t.

    4

  • 7/26/2019 Mfps Visitors

    5/20

    Buchlovsky and Thielecke

    Terms

    t, s::= x | x:T .t | t t| ::K .t | t [T]| tii1..n| it

    Kinds

    K ::= |KK

    Types

    T ::= | TT | ::K .T |::K .T |T[T]|

    i1..nTi

    Contexts

    ::= |, x: T |, :: K

    Typing

    x: T(Var) x : T

    T1:: , x: T1 t : T2(Abs)

    x:T1 .t : T1T2

    t : T1T2 s : T1(App)

    t s: T2

    , :: Kt : T(TAbs) /

    ::K .t : ::K .T

    t : ::K .T1 T2::K(TApp) t [T2] : [T2] T1

    i 1..n ti:Ti(Tuple) tii1..n:

    i1..nTi

    t :

    i1..nTi(Proj) jt : Tj

    Kinding

    :: K

    (TVar) :: K, :: K1 T ::K2(KAbs) /

    ::K1 .T ::K1K2

    T1 :: K1K2 T2 :: K1(KApp) T1[T2] ::K2

    T1:: T2::(KArrow)T1T2::

    , :: KT ::(KAll) /

    ::K .T ::

    i 1..n Ti :: (KTuple)

    i1..nTi::

    Reductions

    (x :T .t) s [xs] t

    (::K .t)[T] [T] t

    jtii1..n tj

    (::K .T1)[T2] [T2] T1

    Fig. 1. System F with products.

    5

  • 7/26/2019 Mfps Visitors

    6/20

    Buchlovsky and Thielecke

    3 Visitors and algebraic type encodings

    In this section, we recall the encoding of algebraic types in System F [14](sometimes called the Bohm-Berarducci encoding[2]). The standard descrip-tion is in terms ofF-algebra. However, we find it useful to present it in a

    slightly different style: while isomorphic to the usual account, it makes theconnection to the Visitor pattern more explicit.

    3.1 Internal visitors

    We consider algebraic types of the form

    .i1..n

    Fi[]

    and we further restrict attention to Fi that are products of the recursive typevariable and type constants. Thus the types defined this way are various

    forms of trees, and we will see how visitors are tree walkers.Definition 3.1 We define internal visitors to be pairs of the formA, aii1..n,where A is a type (called the result type), and aii1..n is a tuple of functionsai:Fi[A]A (called the visit methods).

    In the presence of sums, we could define F[X] =

    i1..nFi[X]. Visitorsare essentially F-algebras, and their visit methods give the structure map.

    Next, we consider the encoding of algebraic types (that is to say initialF-algebras), but rephrased in terms of weakly initial visitors.

    We define an object T as follows:

    T =. (i1..n

    (Fi[]))

    Tcomes equipped with visit methods:

    consi : Fi[T]T

    consi = x :Fi[T]..v:

    j1..n

    (Fj []).vi(Fi[t :T .t[]v]x)

    Intuitively, we think of the consi as the constructors of the datatype T.

    Then T with consii1..n is weakly initial in the following sense. Let Atogether with ai: Fi[A]Ai1..n be any visitor. Then there is a function

    from T to Adefined by:t :T .t[A] aii1..n

    This explains the definition of T: any element t ofT has the ability toaccept any visitor with result A and visit methods visiti, and to yield anelement ofA.

    T =. (i1..n

    (Fi[] visiti[]

    )

    Visitor[]

    )

    6

  • 7/26/2019 Mfps Visitors

    7/20

    Buchlovsky and Thielecke

    More intuitively, the elements ofT can be thought of as trees; and they usethe visit methods to collapse themselves recursively into a single element ofA. This is achieved by letting the visitor visit any subtrees, thereby collapsingthem into elements of the result type, and then calling the appropriate visitmethods for the topmost node.

    consi = x :Fi[T]. constructor arguments

    accept visitor .v:

    j1..n

    (Fj[]). vicall visit method

    walk over subtrees (Fi[t :T .t[]v]x)

    Visitor morphisms that witness the initiality ofT can be seen as callingaccept on a Tobject and passing it a concrete visitor:

    a = t :T . t[A]call accept

    concrete visitor aii1..n

    3.2 External visitors

    An external visitor consists of a pair A, vii1..n, where A is a type andvii1..n is a tuple of functions vi : Fi[T] A. Note the T in the positionwhere an internal visitor would have another occurrence ofA. Intuitively, anexternal visitor has visit methods just like an internal one; the difference isthat these methods may accept trees of type T as arguments, rather thanautomatically collapsing the trees into elements of result type A.

    We define a structureSthat accepts external visitors in the same way that

    Taccepts internal ones:S= .(

    i1..n

    (Fi[T]))

    This object has the structure of visit methods:

    pi : Fi[S]S

    pi = x :Fi[S]..m:

    j1..n

    (Fj[T]).mi(Fi[s :S .s[T] consii1..n] x)

    The visitor structure onSinduces a function from the weakly initial visitorT.

    p: TSIntuitively, this map takes a tree and pattern matches it, so that it can bevisited by an external visitor.

    External visitors can themselves use this map for further pattern matchingof subtrees and thus traversal. However, without adding recursion, an externalvisitor can not traverse trees of arbitrary depth. Since the traversal of thewhole tree is no longer built-in the way it was in internal visitors, an externalvisitor would have to recur under its own steam, as it were, which requires afixpoint combinator in the visitor.

    7

  • 7/26/2019 Mfps Visitors

    8/20

    Buchlovsky and Thielecke

    4 Factorizing the encoding

    The overall view on visitors in this section is as follows. Our aim is to bringout the connection between the Visitor pattern and type encodings in SystemF by factoring various translations. We present an encoding Oof algebraic

    types into a polymorphic-calculus that is stylistically close to object-orientedlanguages. The calculus is a restricted subset of F (with products), andthe encoding corresponds to the classical encoding F of algebraic typesin System F (up to some reductions and type isomorphisms). On the otherhand, because our calculus resembles object-oriented languages, there is astraightforward embeddinginto Featherweight Generic Java (which is itselfa subset of Generic Java); and this lets us recover the internal variant of theVisitor pattern (by composition).

    Alg. TypesVisitor

    O

    F F

    GJ FGJ oo

    F

    We need a calculus for expressing visitors in such a way that we can thentransform them into both System F and FGJ. To do so, we will approxim-ate objects as tuples of methods (of a restricted function type). Anotheringredient is a form of parameterized letontypesthat we will use for approx-imating interfaces with generics. We define:

    let be T1 inT2 ( :: .T2) [. T1]

    Where there are no type parameters we omit and define:

    let be T1 in T2 ( :: .T2) [T1]This construct could be extended to include an arbitrary number of parametersby adding a tuple kind to the definition of F.

    We define some conventions: we write + to mean one or more occur-rences; for 1, . . . , p; [S] for [S1] . . . [Sp]; and S for S1, . . . ,Sp. Ourcalculus for defining visitor types is then as follows.

    Definition 4.1 Types ofoo are well-kinded types of F (with let) given byJ in

    J ::= (let be O in)+ [S] interface definitions

    O ::=i1..nMi object typeM ::= .(

    j1..mSj)S method type

    S ::=[S] interface instantiation

    | int integer type

    where , TyVar and int is a constant type. Note that this grammarrestricts all type variables to the kinds or . We also insist that all typevariables are bound and that all bound variables are distinct.

    8

  • 7/26/2019 Mfps Visitors

    9/20

    Buchlovsky and Thielecke

    Using Definition4.1, we can reformulate visitors as lettypes.

    Definition 4.2 IfT is a data type with constructors of type Fi[T]T, itsvisitor encoding is as follows:

    T =let

    Visitor[] be

    i1..n

    ( Fi[] visiti[]

    ) in let

    accept be . [] in

    It is easy to see that this is equivalent to the System F encoding by a seriesof reductions in F:

    T = let bei1..n

    (Fi[]) in let be . [] in

    = (:: .(.)[. []])[.i1..n

    (Fi[])]

    (:: .. [])[.i1..n

    (Fi[])]

    . (.i1..n

    (Fi[]))[]

    . (i1..n

    (Fi[]))

    But we can also recover visitors in Generic Java. To do so, we define a trans-lation.

    Definition 4.3 The translation from types ofoo to a sequence of FGJinterface definitions as follows: (To render this more compactly we slightlyabuse EBNF notation. Repeated occurrences on the LHS correspond to thoseon the RHS.)

    (let be O in)+ [S]= (interface {O})+

    i1..nMi= (Mi)i1..n

    .(j1..mSj)S= S m((Sj xj)

    j1..m

    );[S]=

    int= Int

    where m and xj are fresh.

    When considering the transform to FGJ it will be necessary to consider thefactors ofFi[] separately. We will take Fi[] to be

    j1..r1int

    kr..n.

    9

  • 7/26/2019 Mfps Visitors

    10/20

    Buchlovsky and Thielecke

    We define the following abbreviations:

    Intf = Int f1, . . . , Int fr1 (similarly for Intx)

    Dg = D gr, . . . , D gn (similarly for gand S y)

    Intf;= Int f1; . . . ; Int fr1;Dg;= D gr; . . . ; D gn; (similarly for g;)

    this.f=f;= this.f1=f1; . . . ; this.fr1=fr1;

    this.g=g;= this.gr=gr; . . . ; this.gn=gn;

    this.f = this.f1, . . . , this.fr1

    this.g.accept(v)= this.gr.accept(v), . . . , this.gn.accept(v)

    Applying to the let encoding of visitors results in

    let

    be i1..n(Fi[])

    in let

    be . []

    in

    = interface {( visiti(Int x, y);)

    i1..n

    }

    interface { accept( v);

    }

    Proposition 4.4 The translation fromoo to FGJ is type-preserving.

    Proof (Sketch)The proof is by induction on the structure of types, keeping

    track of the interface table generated.

    We have shown that a sequence of Java interfaces can be seen as types inSystem F. If interfaces are like types then classes implementing interfacesshould correspond to terms. This is indeed the case: for each constructorconsithere is a class Consi. Similarly, tuples of visit methods in F correspondto classes implementing the visitor interface. See Figure2for an overview ofthis correspondence.

    We would also like to be sure that both the interface and class definitionsare well-typed.

    Proposition 4.5 (Internal visitors are well-formed)Assuming a class tableC Tand an interface tableITwith the class and inter-face definitions shown in Figure2, for any, for eachi 1..n,

    (i) U ok

    (ii) ; x: Int, y: U, this: ConcVisei V andV

  • 7/26/2019 Mfps Visitors

    11/20

    Buchlovsky and Thielecke

    Internal visitor encoding Internal visitor in FGJ

    T =

    let bei1..n

    (Fi[]) in

    let be . [] in

    interface Visitor {

    ( visiti(Int x, y);)i1..n

    }

    interface D {

    accept(Visitor v);

    }

    consi : Fi[T]T

    consi = x :Fi[T].

    .v :

    j1..n

    (Fj []).

    vi(Fi[t :T .t[]v]x)

    class Consi implements D {

    Int f; D g;

    Consi(Int f, D g) {

    this.f = f; this.g = g;

    }

    accept(Visitor v ) {

    return v.visiti(this.f,

    this.g.accept(v));

    }

    }

    s :i1..n

    (Fi[U]U)

    s = x :Fi[U].eii1..n

    class ConcVis implements Visitor {

    (U visiti(Int x, U y) {return ei;})i1..n

    }

    Fig. 2. Correspondence between type encodings and internal visitors in FGJ.

    As a worked example, we consider the type B of binary trees with integersat the leaves, as given by the least fixed point ofF[X] = Z +XX. This is

    encoded as

    B= let be . (Z )(()) in letbe . [] in

    which is transformed into

    interface Visitor { visitLeaf(Int x); visitNode( x, y);

    }

    interface BinTree {

    accept(Visitor v);

    }

    The datatype constructors are

    leaf : Z B

    leaf(n) = . p, q:(Z )(()).pn

    node : (BB)B

    node(l, r) = . p, q:(Z )(()).ql[]p, q, r[]p, q

    11

  • 7/26/2019 Mfps Visitors

    12/20

    Buchlovsky and Thielecke

    The corresponding FGJ classes Leaf and Nodeare

    class Leaf implements BinTree {

    Int n;

    Nil(Int n) { this.n = n; }

    accept(Visitor v ) {return v.visitLeaf();

    }

    }

    class Node implements BinTree {

    BinTree l; BinTree r;

    Cons(BinTree l, BinTree r) { this.l = l; this.r = r; }

    accept(Visitor v ) {return v.visitNode(l.accept(v), r.accept(v));

    }

    }

    The type of a concrete visitor for binary trees is

    let be (Z Z)((Z Z) Z) in

    An operation for summing up the leaves of a tree can defined as follows:

    sum : B Z

    sum= t :T .t[Z]x :Z.x,x, y:Z Z.x+y

    which is equivalent to a call to accept on a BinTree with an instance of thefollowing concrete visitor

    class SumVisitor implements Visitor {Int visitLeaf(Int x) {

    return x;

    }

    Int visitNode(Int x, Int y) {

    return x + y;

    }

    }

    The generated code is valid, well-typed FGJ with interfaces code. Thismeans it is also almost correct Generic Java. Indeed, after minor modific-

    ations (adding the keyword public to method definitions, discarding typeparameter instantiation on calls to accept and adding a main method), itcompiles correctly using the Sun Java 1.5 compiler.

    5 From functional to imperative internal visitors

    The visitors in Figure2were purely functional and relied on generics. A moretypical rendition of an internal visitor using internal state instead is given inFigure3 (it is debatable whether visitNode()should be omitted, since there

    12

  • 7/26/2019 Mfps Visitors

    13/20

    Buchlovsky and Thielecke

    interface Visitor {

    void visitLeaf(int n);

    void visitNode();

    }

    interface BinTree {

    void accept(Visitor v);

    }

    class Leaf implements BinTree {

    int n;

    Leaf(int n) { this.n = n; }

    public void accept(Visitor v) {

    v.visitLeaf(n);

    }

    }

    class Node implements BinTree {

    BinTree left; BinTree right;Node(BinTree left, BinTree right) {

    this.left = left;

    this.right = right;

    }

    public void accept(Visitor v) {

    left.accept(v);

    right.accept(v);

    }

    }

    class SumVisitor implements Visitor {int s;

    SumVisitor(int s) { this.s = s; }

    public void visitLeaf(int n) { s = s + n; }

    public void visitNode() { }

    }

    Fig. 3. An imperative internal visitor in Java (without generics)

    is no information at internal nodes).

    In this section, we address the equivalence of functional and imperativevisitors, albeit in a very idealized setting. Consider the example of summingall the leaves of a binary tree. Functional visitors would do this by performingadditions at all the internal nodes. On the other hand, a more typical imper-ative use of the Visitor pattern would use a field in the visitor to accumulatethe sum. The field is initialized to 0, and at each leaf, its value is added to thefield; at an internal node, the left and right subtrees are simply traversed oneafter the other. After the traversal, the field holds the result. Since such animperative visitor uses state rather than a result value, its return type is void.

    13

  • 7/26/2019 Mfps Visitors

    14/20

    Buchlovsky and Thielecke

    We will model a visitor that updates a piece of state Sby a functionSS,as one does in the semantics of imperative languages, or the monadic view ofcomputation approach. Given (the functional internal visitor encoding of ) abinary tree t, we define its imperative counterpartt as follows:

    t= .c:Z .t[]c,

    where abbreviates function composition,

    =f, g:()(). x:. g(f x)

    Compared tot,tis more specialized: it only allows one to specify an operationat the leaves, which needs to transform a state of type , while the onlyoperation at internal nodes is to compose the state transformers of the leftand right subtrees; this composition of state transformations corresponds tocalling two void-returning methods in succession.

    To relate the state-transforming and the functional visitors, we want toshow that the state-transforming visitor (with initial state 0) yields the same

    result as the functional one:t [Z] (n :Z. s:Z.s +n) 0 = t[Z]idZ, +

    We sketch a proof using relational parametricity[13], specifically the reas-oning developed by Wadler as theorems for free [16]; see the latter paperfor an introduction and the relevant definitions.

    Let t be a binary tree for internal visitors, that is

    t: . ((Z )(()))

    Nowt [Z] (n :Z. s:Z.s+n) = t[Z Z] (n :Z. s:Z.s+n), Z. Hence wewant to show

    t[Z]idZ, += t[Z Z]addZ, Z 0

    where

    idZ = n :Z.n : Z Z

    addZ = n :Z. s:Z.s +n : Z Z Z

    Assuming parametricity[16], we have that for all relations R,

    t, t ((Z R)((RR)R))R

    We define a relation R : Z (Z Z) byn, f R ifff(x) =n + xfor allx.

    Then we have:idZ, addZ Z R

    +, Z (RR)R

    The latter of these holds because ifx, y, f, g RR, thenx+y, fgR.Since t maps related arguments to related results, we have

    t[Z]idZ, +, t[Z Z]addZ, Z R

    Hence, by the definition ofR,t[Z Z]addZ, Z 0 =t[Z]idZ, +, as required.

    14

  • 7/26/2019 Mfps Visitors

    15/20

    Buchlovsky and Thielecke

    Note that the proof made use of 0 being the neutral element of addition,and of the associativity of addition in establishing the relationR betweenfgand x+y:

    (f g)(z) =f(g(z)) =f(z+y) = (z+y) +x= z+ (x+y)

    The above argument of relating a functional and an imperative visitorby associativity is applicable to more substantial cases as well. Consider thestandard example of abstract syntax trees, and suppose we need to traverse thetree to add information into a symbol table. The most evident specification interms of a synthesized attribute would be essentially functional, merging thesymbol table of the subtrees at the inner nodes. An imperative visitor couldinstead start off with an empty symbol table and add entries by updatinga mutable symbol table during traversal. Showing the equivalence of thefunctional and the imperative version should be analogous to the parametricityargument above, with the empty symbol table being the neutral element, and

    merging of symbol tables as the associative operation.

    6 Conclusions

    We have reconstructed the internal Visitor pattern and a restricted form ofthe external variant within an idealized type-theoretic setting. To do so, wehad to make simplifying assumptions, and the fit is not perfect; this may beinevitable, since Java was not designed on top of a lambda calculus, unlikefunctional languages. But if one grants these idealizations, it is possible toglean essential features of visitors more easily than from lengthy Java code or

    class diagrams. We summarize our abstract reconstruction of visitors with thefollowing mapping from the terminology used in the patterns literature [6].

    Patterns literature Idealized view

    Visitor interface Type (determines a functor F)

    Concrete visitor Object of visitor type (= F-algebra)

    Visit method Component of the structure map of a visitor

    Data (or Element) Weakly initial visitor

    Accept method Witnesses initiality

    Concrete data Given by consi

    There is a large literature on the Visitor pattern, most of it concernedwith overcoming some of its inflexibility. This line of work goes back to Reyn-olds[12]. In the context of the present paper, the most relevant previous workis Felleisen and Friedmans textbook [4], in which variations on the Visitorpattern are developed, implicitly based on program transformations knownfrom functional programming. Setzer [15] also observes a connection between

    15

  • 7/26/2019 Mfps Visitors

    16/20

    Buchlovsky and Thielecke

    visitors and functional programming.

    As sketched in Section5, for functional (particularly internal) visitors, thepolymorphic typing immediately gives one reasoning principles, or theoremsfor free. It should be possible to transfer the structure of such arguments tomore realistic imperative visitors, where logical relations on parts of the heap

    would be used in place of the return type parametricity of the functional vis-itors. Instead of the result types of visitors, the relations could be built on aneffect system [10](adapted from functional to object-oriented languages [1]).In particular, the effect annotations tells us that the data structure to be tra-versed unleashes the effects of the visitor, but causes none itself. Apart fromeffect systems, Hoare logic may also be applicable to visitors. Specifically, itwould be interesting to see whether abstract predicates [11]for visitors can behandled analogously to the quantified result type.

    The visitor-style encoding in System F does not extend to datatypes thatuse subclassing. It may be worthwhile to consider similar encodings in F

  • 7/26/2019 Mfps Visitors

    17/20

    Buchlovsky and Thielecke

    [7] Jean-Yves Girard. Interpretation fonctionnel le et elimination des coupures delarithmetique dordre superieur. PhD thesis, Universite Paris 7, 1972.

    [8] Jean-Yves Girard, Paul Taylor, and Yves Lafont. Proofs and Types. CambridgeUniversity Press, Cambridge, 1989.

    [9] Atsushi Igarashi, Benjamin Pierce, and Philip Wadler. Featherweight Java:A minimal core calculus for Java and GJ. In Proceedings of the 14thACM Conference on Object-Oriented Programming, Systems, Languages, andApplications (OOPSLA99), Denver, Colorado, 15 November 1999, pages 132146. ACM Press, New York, 1999.

    [10] John M. Lucassen and David K. Gifford. Polymorphic effect systems. InProceedings of the 15th ACM Symposium on Principles of ProgrammingLanguages (POPL88), San Diego, California, 1315 January 1988, pages 4757. ACM Press, New York, 1988.

    [11] Matthew Parkinson and Gavin Bierman. Separation logic and abstraction.In Proceedings of the 31st ACM Symposium on Principles of ProgrammingLanguages (POPL05), Long Beach, California, 1214 January 2005, pages247258. ACM Press, New York, 2005.

    [12] John C. Reynolds. User-defined types and procedural data structures ascomplementary approaches to data abstraction. In S. A. Schuman, editor, NewDirections in Algorithmic Languages 1975, pages 157168. IRIA, Rocquencourt,France, 1976.

    [13] John C. Reynolds. Types, abstraction and parametric polymorphism. InR. E. A. Mason, editor, Information Processing 83, pages 513523. Elsevier

    Science Publishers B. V. (North-Holland), Amsterdam, 1983.

    [14] John C. Reynolds and Gordon D. Plotkin. On functors expressible in thepolymorphic typed lambda calculus. Information and Computation, 105:129,1993.

    [15] Anton Setzer. Java as a functional programming language. In H. Geuvers andF. Wiedijk, editors, Types for Proofs and Programs: International Workshop,TYPES 2002, Berg en Dal, The Netherlands, 2428 April 2002, number 2646in Lecture Notes in Computer Science, pages 279298. Springer, Berlin, 2003.

    [16] Philip Wadler. Theorems for free! In Proceedings of

    the 4th International Conference on Functional Programming and ComputerArchitecture (FPCA89), London, 1113 September 1989, pages 347359. ACMPress, New York, 1989.

    A Featherweight Generic Java

    Featherweight Generic Java [9] is a minimal Java-like calculus. It includestype parameterized classes and methods and its syntax is (almost) a subsetof Java, so all FGJ programs are also valid Java programs. (To convert an

    17

  • 7/26/2019 Mfps Visitors

    18/20

    Buchlovsky and Thielecke

    FGJ program into a Generic Java program it is necessary to add the keywordpublic before method implementations and to elide type instantiations onmethod calls.)

    Our description of FGJ includes interfaces which were not present in theoriginal definition. The extension is limited since it only permits single inher-

    itance in interface hierarchies and a class is permitted to implement at mostone interface. There are also some restrictions on type parameter bounds.Since we are not interested in casting we will omit that from our account.

    The syntax and typing of FGJ with interfaces are shown in Figures A.1and A.2. The computation rules are shown in Figure A.3. We omit someauxiliary rules due to lack of space. We abbreviate the keywords extendsas, implements as and return as . The metavariables , , range overtype variables; T, U, V range over types; Nand Orange over class types; and Qranges over interface types.

    Some abbreviations are also necessary for various sequences:

    f = f0, . . . ,fn (similarly forC,x,e, ,N,Qetc.)

    M = M0. . . Mn (similarly forH)

    Cf = C1 f1, . . . , Cn fn

    Cf;= C1 f1; . . . ; Cn fn;

    this.f=f;= this.f1=f1; . . . ; this.fn=fn;

    The empty sequence is written as and concatenation is denoted with acomma.

    Unparameterized classesCand methodsmcan be abbreviated toCandm. As in Java, unbounded parameters are assumed to have a bound ofObject.We also abbreviate C Object to C. A class does not have to implement aninterface, in which case we can omit Q from its definition. Unlike the classhierarchy, the interface hierarchy has no root so we also allow Qto be omittedfrom interface definitions. We will omit empty calls to super() and emptyconstructors.

    The class table CT is a mapping from class names to class declarations.The extended calculus also has an interface tableITwhich plays a similar rolewith respect to interfaces. The authors of FGJ define some sanity conditions

    on class tables and we assume that both CT and IT obey them. We assumethe existence of the special variable this, which may not be used as the nameof a field or method parameter. A program in FGJ with interfaces is a triple(CT, IT, e) of a class table, an interface table and an expression.

    18

  • 7/26/2019 Mfps Visitors

    19/20

    Buchlovsky and Thielecke

    Syntax

    CL ::= class C N Q{Tf; KM}

    IN ::= interface I Q {H}

    K ::= C(Tf){super(f); this.f =f;}

    H ::= T m (Tx);

    M ::= T m (Tx){ e;}

    e ::= x| e.f | e.m(e)| new N(e)

    T ::= | |N | Q

    N ::= C

    Q ::= I

    Subtyping

    S-Refl T

  • 7/26/2019 Mfps Visitors

    20/20

    Buchlovsky and Thielecke

    Method typing

    =