A Generic Framework for Genericity
Loc Correnson, Etienne Duris,
Didier Parigot and Gilles Roussel
INRIA Rocquencourt - France.
Gilles Roussel is with Universite de Marne-la-Vallee.
Abstract
Recently, generic programming becomes of a major interest in several programming paradigms. A recurrent idea to
achieve genericity is to specify algorithms on their convenient data structure, and to allow these speci cations to be
instantiated onto a large number of neighboring data structures.
Polytypic programming, shapely types and generic attribute grammars are generic programming methods related
to this approach. A framework for generic programming is
proposed to embed these methods. It consists in tools for
automatic generation of morphisms between data structures,
and for program composition.
Thanks to this compositional approach, the complete
specialization of generic programs could be advantageously
delegated to a general and powerful mechanism of \symbolic
composition", which performs deforestation and partial evaluation.
1 Introduction
In several programming paradigms, generic programming is
being emerging. Although this concept is not new, genericity currently raises a great interest in the programming and
software engineering community. In this area, one of the
recent issues is genericity according to the data structure.
When an algorithm is speci ed on a general data structure,
the notion of genericity appears with the possibility to reuse
it in several contexts, says, onto particular data structures.
A few years ago, Farrow et al. [7] devised a generic programming notion for attribute grammars [10, 14]. This
genericity is based on the observation that any function f
de ned by an attribute grammar on a type 2 could be instantiated on a type 1 . The only component needed, if it
exists, is a function m that implements a morphism between
terms of type 1 and terms of type 2 . The composition of
this function m with the function f performs the instantiation process. This approach has been revisited [11, 15, 2]
to enable automatic generation of the morphism m from
a simple speci cation of correspondence relation between
types. In this context, an ecient specialization process
Submitted to the 3rd ACM SIGPLAN International
Conference on Functional Programming (ICFP '98).
consists in applying a transformation that eliminates intermediate data structures occurring in the composition. For
attribute grammars, Ganzinger and Giegerich solved this
general problem by introducing their descriptional composition algorithm [8, 1]. Using this technique, the construction
of the intermediate representation of type 2 is discarded
from the composition, and thus the original function f is
transposed and actually specialized onto the type 1 .
This problem of intermediate data structure elimination,
usually called deforestation [17], has been widely studied in
the functional programming community [12, 16, 13]. The
comparison between functional and attribute grammar deforestation methods [6, 4] led us to propose a powerful deforestation method
for functional programs, the symbolic composition [5]1 , which is based on descriptional composition.
Since descriptional composition is an important component of the attribute grammar genericity framework, we
have compared in [3] attribute grammar genericity with
polytypic programming [9]. These methods seems to be
complementary, and we propose in this article a general
framework for genericity. This framework is based on function composition, automatic morphism generation and a
general deforestation method.
The major bene t for this approach is to really separate
the application of a generic algorithm from the specialization of this instantiation. The rst problem is solved by
morphism and composition, while the second is performed
by a general and powerful deforestation method. Automatic
morphism generation is still an open problem, even if methods already exist.
The paper is organized as follows. Section 2 presents the
general framework for genericity through the well-known example of uni cation algorithm. Section 3 presents two different methods to automatically generate morphisms, while
section 4 illustrates the deforestation power for instantiation
and specialization purposes.
2 Framework for Genericity
Let us consider the general uni cation algorithm to illustrate
our framework. Recall that it consists in comparing two
terms t and t that contains variables and in computing a
substitution verifying ( t) = ( t ), if it exists. Although
this problem is general, a particular implementation of the
general algorithm is required for each type of the terms t
and t .
0
0
0
1 This method deforests some functional programs for which existing functional methods fail.
Specialization process performs the actual instantiation.
To achieve genericity, we propose to specify it on the
following well-suited type term :
type term c a = var a j const c (list (term c a ))
Type c allows to distinguish the di erent constructors in
the term algebra, while type a allows to distinguish variables. Then, implementing the uni cation algorithm on this
type term is natural and it corresponds to classical algorithmic presentations. The advantage of this approach is to ease
the implementation and the readability of the generic speci cation. Indeed, everyone can understand the uni cation
algorithm speci ed on this type.
Let unify be the uni cation function. In order to use
uni cation in practice, this uni cation function has to be
composed with a morphism which instantiates the algorithm
on a particular type. Suppose that uni cation is needed
on a given type . This implies that some morphism M
from type to type term exists and can be implemented
in a function m. Since the uni cation gives a substitution
which associates values of type term to variables, the inverse
morphism m?1 is needed to translate back the result. The
uni cation on is instantiated by :
unify t t 0 = let = unify (m t ) (m t 0 ) in x :(m ?1 ( x ))
Then a classical deforestation method can be applied.
After the deforestation of function unify , the intermediate values of type ?term
are no longer constructed and the
functions m and m 1 are no longer used.
Let g be the function unify deforested with m and m?1 , the function unify
becomes :
unify t t 0 = let 0 = g t t 0 in x :(0 x )
Another approach to this uni cation problem could be
found in polytypic programming with PolyP [9].
In the example above, two parts appear. The rst one is
a coupling process that produces a morphism between two
types. This morphism is a function that translates a value
into an intermediate representation; it could be composed
with any program working on this intermediate representation. The second part applies a deforestation method in
order to specialize such compositions into an instantiated
algorithm. Consequently, the compositional framework for
genericity is based on the three following key points :
Generic programming is achieved by functions composition. These functions are divided into two kinds. These
of the rst kind (e.g. unify ) implement generic algorithms on a convenient
type. Functions of the second
kind (e.g. m and m?1 ) implement translations between
neighboring types.
Morphism speci cation provides the actual genericity.
Since it describes the translation between two types,
it also gives the way to perform the algorithm instantiation. Most of morphisms involved in the compositions could be automatically derived, thanks to small
speci cation in simple meta-languages. These metalanguages should not be super-languages of the original programming language used, in order to be reusable
and quickly developed. Nevertheless, these morphisms
could also be hand-written. Moreover, succesive morphisms can be composed.
The speci ed compositions have to be improved. In
fact, each generic function has to be customized with
respect to its composition context. Rather than applying an ad-hoc method for each particular generic system, it is worthwhile to use a general deforestation or
fusion method, that symbolically performs at one and
the same time composition, elimination of intermediate
values and partial evaluation.
Let M be the morphism speci cation, C the morphism
generation algorithm, alg the \generic" algorithm, m the
morphism function generated, alg M the expected instanciated algorithm, and ? the programming environment. Then
the framework for genericity is abstracted by the following
gure :
C m
M; ? )
alg ; M; ? ) alg m
alg m deforestation
)
alg M
alg m ) alg M
where is the standard composition in the original language,
and deforestation is a method like hylo [13], or Symbolic
Composition [5].
3 Morphism Generation
In this section, two case studies of automatic morphism generation are presented. The rst one was inspired by polytypic programming [9], but has been totally revisited to
match with our generic framework. The second case study
was inspired by attribute grammar genericity [11, 15, 2],
and is an illustration of cross-fertilization from di erent
paradigms. For clarity, technical details and algorithm
sketches are presented in annex A.
3.1 Compositional approach of polytypic programming
Classically, in functional languages, functions are speci ed
for a single given (polymorphic) type. However, most functions could be abstracted from any type. For example, the
number of leaves in a tree and the length of a list are specied by very similar functions.
To implement such generic functions, it is necessary to
specify them on a type that can represent the structure of
any value of any (polymorphic) type. We then propose the
following type Poly :
type Poly c o a = Sum c (Poly c o a )
j Prod (Poly c o a ) (Poly c o a )
j Par a
j Obj o
The type Poly is parameterized with three type variables.
Let be a given type. Intuitively, c is a type that identify
the constructors of , o is a type that identify other types
that could appear in (e.g. boolean, integer, etc.), and a is
the polymorphic variable of type . Thus, the classical type
list
type list a = nil j cons a (list a )
can be represented by the type (Poly c list o list a ) where :
type c list = list cons j list nil
type o list = list empty
2
An interesting point is that type Poly is de ned in the
original functional language and does not require any special notation. The translation from type list to type Poly
(resp. the backward translation) is performed by the function out list (resp. inn list ):
3.2
Previous section shows one way to generate a bijective morphism between any type and type Poly . This section proposes another method to generate morphism between datastructures. The aim is to infer a { not necessarily bijective {
morphism between two arbitrary types. Consider the following type representing a binary tree with one or two integers
at each node :
type clumsytree =
node elements clumsytree clumsytree j nothing
type elements = one int j two int int
Now, consider the type :
type
= leaf
j node1 ( ) ( )
j node2
( ) ( )
A morphism between type clumsytree and type can be
implemented with the following function :
let couplage t = match t with
node (one a1 ) t1 t2 !
(node1 a1 (couplage t1 ) (couplage t2 ))
node (two a1 a2 ) t1 t2 !
(node2 a1 a2 (couplage t1 ) (couplage t2 ))
nothing ! leaf
Two properties are veri ed by this morphism : type
clumsytree is associated to ( ), and type int is associated to . It is possible to denote these two properties by
the following relation :
C or (tree ) = fclumsytree g
C or ( ) = fint g
Such a relation is called a correspondence relation between types clumsytree and .
The aim is now to automatically derive the function
couplage from a given correspondence relation. In [11] we
propose such an inference algorithm for attribute grammars.
It is easy to translate it into functional programming, as described in annex B.
The basic idea of this algorithm is to associate a subterm of type clumsytree to a sub-term of type . Thus, the
algorithm yields the following associations :
the term (node (one a1 ) t1 t2 ) must be associated to a
term of type , composed with a tuple of type ( ; ; ).
So it is associated to the term (node1 a1 t1 t2 ).
the term (node (two a1 a2 ) t1 t2 ) must be associated
to a term of type , composed with a tuple of
type ( ; ; ; ). So it is associated to the term
(node2 a1 a2 t1 t2 ).
the term (nothing ) corresponds to a term of type composed with nothing else. So it is associated to (leaf ).
Then, from such an association, it is very easy to generate the expected function couplage . Sometimes, more complex associations have to be de ned.
In [11], we show that it is not always possible to nd the
associations, and we characterize these situations. A typical
example is :
type a = c1 n
C or (a ) = fa g
type n = c2 n n j c3 b
C or (b ) = fb g
type b = : : :
out list x = match x with
cons a b ! (Sum list cons (Prod (Par a ) (out list b )))
nil ! (Sum list nil (Obj list empty ))
inn list p = match p with
Sum list cons (Prod (Par a ) r ) ! (cons a (inn list r ))
Sum list nil (Obj list empty ) ! (nil )
! raise "not a list"
These two morphisms out and inn can be automatically generated from every type . Mutually recursive types
and type compositions can also be automatically abstracted
by morphism composition. Details can be found in annex
A.
Then a function that is independent from any type, but
that depends on the data structure of its variable can be
speci ed on type Poly . For instance, consider the functions
size and atten ; they respectively calculate the number of
Par occurrences in a value of type Poly , and their list :
size x = match x with
Sum c y ! (size y )
Prod y y ! (size y ) + (size y )
Par y
!1
Obj z
!0
atten x h = match x with
Sum c y ! ( atten y h )
Prod y y ! ( atten y ( atten y h ))
Par y
! (cons y h )
Obj z
!h
0
0
Correspondence relation
0
0
Then, functions that compute the size and the leaves list
on type tree (binary trees) and type list are obtained by the
following compositions :
type tree a = leaf a j node (tree a ) (tree a )
size list t = (size (out list t ))
atten list t h = ( atten (out list t ) h )
size tree t = (size (out tree t ))
atten tree t h = ( atten (out tree t ) h )
Applying deforestation will eliminate the construction of
the intermediate value of type Poly , and will lead to the
expected functions. For instance, for the type tree , deforestation leads to :
size tree t = match t with
node a b ! (size tree a ) + (size tree b )
leaf n ! 1
atten tree t h = match t with
node a b ! ( atten tree a ( atten tree b h ))
leaf n ! (cons n h )
The main advantage of type Poly is that a bijective morphism can be automatically derived from any (polymorphic)
type. But this type is not the most natural to implement
many algorithms. Actually, many algorithms need semantic information on values that are not necessary explicited
by the structure of the type. For instance, it is dicult for
the uni cation algorithm, to determine what and where are
variables in the type Poly .
0
0
0
0
0
3
0
0
0
0
The problem is due to the fact that a can \derive" into
an in nity of n which are not associated to anything by
the correspondence relation. Moreover, in [11], we try to
associate a term with a constructor instead of with a subterm. The general problem of \parsing" the leaves of a term
like (node (one a1 ) t1 t2 ) with the constructors of a given
type remains opened.
Once again, this method fails when semantic information
on types have to be taken into account in order to generate
the morphism. Moreover, the morphism is often not bijective.
Even if none of the two previously exposed methods are
powerful enough to infer almost expected morphisms, they
could yet be considered as tools to construct complex morphisms by composition of several simple ones.
To instantiate the uni cation algorithm on trees where
leaves are the variables, the two following morphisms are
used :
tree to term t = match t with
node a b !
(const 1 [tree to term a ; (tree to term b )])
leaf n ! (var n )
term to tree t = match t with
const 1 [a ; b ] !
(node (term to tree a ) (term to tree b ))
var n ! (leaf n )
! raise "not a tree !"
Then, uni cation on trees is speci ed by :
unify tree t t = (all unify term to tree tree to term ) t t
The aim is now to transform this instantiation speci cation { the only one the programmer has to write { into a
more specialized function.
0
4 Deforestation
This section illustrates the power of deforestation in order
to specialize instanciations of a \generic" program. After
giving notations for the uni cation example, we show some
critical steps of the complete deforestation process, for our
deforestation method, namely the symbolic composition [5].
Applying deforestation : the rst step consists in spe-
cializing the de nition of unify tree :
unify tree t t =
let s = (uni empty (tree to term t ) (tree to term t ))
in x :(term to tree (s x ))
Next, the composition of uni and tree to term is deforested into the function uni1 . In this function, many simplications have been performed, especially the partial evaluation of the function foldr :
uni1 s t t = match (t ; t ) with
(leaf x ; leaf x ) !
if x = x then s else (link s x (tree to term t ))
( ; leaf x ) ! (link s x (tree to term t ))
(node a b ; node a b ) ! (uni1 (uni1 s b b ) a a )
Now the composition of link and tree to term is deforested into the function link1 . Thus, the function uni1 is
updated into uni2 , and this leads to :
unify tree t t =
let s = (uni2 empty t t ) in x :(term to tree (s x ))
Uni cation example : recall that a well suited type for
0
this problem is :
type term c a = var a j const c (list (term c a ))
Suppose now that the uni cation algorithm is standardly
written for this simple type :
unify : (term c a ) ! (term c a ) ! (a ! (term c a ))
The expression (unify t t ) returns the substitution s if
t and t are equals modulo s. The substitution s is given
as a function from variables to terms. If the substitution
does not exist, the function raises the exception No unif .
The kernel of uni cation algorithm is implemented by the
function uni :
uni s t t = match (t ; t ) with
(var x ; var x ) ! if x = x then s else (link s x t )
( ; var x ) ! (link s x t )
(const c lt ; const c lt ) !
if c = c then
foldr ((a ; a ):r :(uni r a a )) s (zip lt lt )
0
0
0
0
0
0
0
else
raise
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
uni2 s t t = match (t ; t ) with
(leaf x ; leaf x ) !
if x = x then s else (link1 s x t )
( ; leaf x ) ! (link1 s x t ))
(node a b ; node a b ) ! (uni2 (uni2 s b b ) a a )
At this point, the substitution s still associates variables
to terms, and not variables to trees. But further deforestation is possible, and the function uni3 will pre-calculate the
composition of s with term to tree . At the end of the entire
process, substitutions are physically constructed (in a list
for instance). Then the substitution s is discarded and replaced by s1 , computed by the function uni4 . Consequently,
the empty substitution is replaced by empty1 . This leads to :
unify tree t t =
let s1 = (uni4 empty1 t t ) in z :(s1 z )
0
0
0
0
No unif
0
where (link s x t ) adds the substitution x = t to s if possible, and raises the exception No unif otherwise. We then
have :
unify t t = (uni empty t t )
where empty is the empty substitution.
Now, to perform genericity, we have to specify for every
type a morphism from to term . Since the uni cation returns a substitution, it is important to work with a bijective
morphism. The function all unify instantiates unify with
such a morphism de ned by the functions in : term ! tree
and out : tree ! term .
(all unify in out ) t t =
let s = (unify (out t ) (out t )) in x :(in (s x ))
0
0
0
0
0
0
0
0
0
0
0
0
4
0
[6] Etienne Duris, Didier Parigot, Gilles Roussel, and Martin Jourdan. Attribute grammars and folds: Generic
control operators. Rapport de recherche 2957, INRIA,
August 1996.
[7] Rodney Farrow, Thomas J. Marlowe, and Daniel M.
Yellin. Composable attribute grammars: Support for
modularity in translator design and implementation.
In 19th ACM Symp. on Principles of Programming
Languages, pages 223{234, Albuquerque, NM, January
1992. ACM press.
[8] Harald Ganzinger and Robert Giegerich. Attribute
coupled grammars. In ACM SIGPLAN '84 Symp. on
Compiler Construction, pages 157{170, Montreal, June
1984. Published as ACM SIGPLAN Notices, 19(6).
[9] P. Jansson and J. Jeuring. PolyP - a polytypic programming language extension. In 24th ACM Symp. on
Principles of Programming Languages, 1997.
[10] Donald E. Knuth. Semantics of context-free languages. Mathematical Systems Theory, 2(2):127{145,
June 1968. Correction: Mathematical Systems Theory
5, 1, pp. 95-96 (March 1971).
[11] Carole Le Bellec, Martin Jourdan, Didier Parigot, and
Gilles Roussel. Speci cation and Implementation of
Grammar Coupling Using Attribute Grammars. In
Maurice Bruynooghe and Jaan Penjam, editors, Programming Language Implementation and Logic Programming (PLILP '93), volume 714 of Lect. Notes
in Comp. Sci., pages 123{136, Tallinn, August 1993.
Springer-Verlag.
[12] E. Meijer, M. M. Fokkinga, and R. Paterson. Functional programming with bananas, lenses, envelopes
and barbed wire. In Conf. on Functional Programming
and Computer Architecture (FPCA'91), volume 523 of
Lect. Notes in Comp. Sci., pages 124{144, Cambridge,
September 1991. Springer-Verlag.
[13] Y. Onoue, Z. Hu, H. Iwasaki, and M. Takeichi. A calculational fusion system HYLO. In In Proc. IFIP TC
2 Working Conference on Algorithmic Languages and
Calculi, Le Bischenberg, France, February 1997.
[14] Jukka Paakki. Attribute grammar paradigms | A
high-level methodology in language implementation.
ACM Computing Surveys, 27(2):196{255, June 1995.
[15] Gilles Roussel. Algorithmes de base pour la modularite
et la reutilisabilite des grammaires attribuees. PhD thesis, Departement d'Informatique, Universite de Paris 6,
March 1994.
[16] Tim Sheard and Leonidas Fegaras. A fold for all seasons. In Conf. on Functional Programming and Computer Architecture (FPCA'93), pages 233{242, Copenhagen, Denmark, June 1993. ACM Press.
[17] Philip Wadler. Deforestation: transforming programs
to eliminate trees. In Theoretical Computer Science,
volume 73, pages 231{248, 1990. (Special issue of selected papers from 2'nd European Symposium on Programming).
uni4 s1 t t = match (t t ) with
(leaf x leaf x ) !
if x = x then s else (link2 s1 x t )
( leaf x ) ! (link2 s1 x t ))
(node a b node a b ) ! (uni4 (uni4 s b b ) a a )
0
0
;
0
;
0
;
0
0
;
0
0
0
0
0
This deforestation process seems to be complex, but it
only consists in multiple application of few rules. Moreover these simple rules are expressed independently from
any functional language. We are using an attribute grammar based formalism enriched by dynamic constructions in
order to take into account composition and partial evaluation into one single transformation, called symbolic composition. Thus, deforestation is the key tool to achieve genericity by composition, since a unique framework is available
independently from any programming language.
5
Conclusion
This article presents a general concept of generic programming, which is independent of any programming language.
Instead of bringing di erent approaches into con ict, it
expects large cross-fertilizations between di erent generic
methods. Each of them has advantages and limitations, and
o ers di erent { and complementary { kinds of genericity.
In order to ease the cross-fertilizations it seems worthwhile to separate morphism speci cation from algorithm instantiation and specialization. Then, symbolic composition
{ or other deforestation method { is the basic tool which
enables the specialization of an algorithm to be performed
over new structures via morphisms speci cations. As soon
as a deforestation method is available, many ways to achieve
genericity can be developed quickly, easily and eciently.
Besides, there exists certainly other generic programming and specializing methods that should be considered.
From our point of view, it will be interesting to carry out
some uni ed way to specify morphisms and to exhibit families of automatic or semi-automatic methods to generate
these morphisms.
References
[1] John Boyland and Susan L. Graham. Composing tree
attributions. In 21st ACM Symp. on Principles of Programming Languages, pages 375{388, Portland, Oregon, January 1994. ACM Press.
[2] Loc Correnson. Genericite dans les grammaires attribuees. Rapport de stage d'option, E cole Polytechnique, 1996.
[3] Loc Correnson. Programmation polytypique avec les
grammaires attribuees. Rapport de DEA, Universite de
Paris VII, September 1997.
[4] Loc Correnson, Etienne Duris, Didier Parigot, and
Gilles Roussel. Attribute grammars and functional programming deforestation. In Fourth International Static
Analysis Symposium { Poster Session, Paris, France,
September 1997.
[5] Loc Correnson, Etienne Duris, Didier Parigot, and
Gilles Roussel. Symbolic composition. Technical Report 3348, INRIA, January 1998.
5
Annex A
Morphism generation for Poly
The function out is derived with :
= : : : j ck 1 : : : n j : : :
` x i ; i ) t i
out x = match x with
(ck x1 : : : xn ) !
(Sum ( ck ) (Prod t1 (Prod t2 : : : tn )))
:::
The following global-naming conventions are assumed:
Gsum is the type that represents the constructors of every
type . Thus, the constructor c of type is denoted by
the constructor c of type Gsum .
Gobj is the type that represents values of any simple type
appearing in some type . For instance, if contains
integers and booleans :
type Gobj = : : : int int j bool bool : : :
Generation of inn : Here, the inference rule notation
is :
t; t
where is the current type and ' the function to apply
; ' `
; ' `
)
t; t
0
t ; (out t )
; ' ` ( )
)
0
(
y = match y with
t !t
! raise \error
x ; (unshift x )
new name
0
0
00
0
Then :
= : : : j ci i1 : : : in j : : :
; ' ` ik ) pik ; tik
unshift ' x = match x with
(Sum ( ci ) (Prod p1i : : : pni )) ! (ci t1i : : : tni )
:::
where is the current type, x is a term of type , and t is
the term x translated from type to type Poly .
With these notations :
)
(out x )
And nally :
parshift x = match x with
(Par x ) ! x j ! raise \error
inn x = (unshift parshift x )
` x i ; i ) t i
i = 1; 2
` x; (1 2 ) ) (let (x1 ; x2 ) = x in (Prod t1 t2 ))
Example
` x; ) (Obj ( x ))
` y; ) t
` x; ( ) ) (shift (y :t ) (out x ))
0
)
0
t
(Par x ) ` x; ( )
t ; (' t ) ; ' ` ( )
0
ence rules. Their general scheme is :
conditions
)
)
; ' ` ) (Obj ( u )); u
` i ) ti ; ti
i = 1; 2
; ' ` (1 2 ) ) (Prod t1 t2 ); (t1 ; t2 )
Generation of out : The algorithm is de ned by infer-
` x;
0
0
Notice that all the morphisms have to be infered before
the generation of types Gsum and Gobj . But since type Poly
is a polymorphic type, this is not a problem for separate
compilation. Types are de ned according to the following
grammar :
::= c1 1 j : : : j cn n constructors
::=
the polymorphic variable
j
basic type
j
( )
type composition
j
type product
)
)
where Par values are expected { useful for type compositions. For any type , the algorithm generates the pattern t
of type Poly that represents a term of type , and its backward translation t of type .
out is the function that implements a morphism between
type and type Poly ; inn is the reciprocal function.
` x;
conditions
; ' `
00
To illustrate how type composition is processed, let us consider the following example :
type ower a = rose int a (tree ( ower a )))
It leads to :
type Gobj = ower int int j : : :
type Gcons = ower rose j tree node j tree leaf j : : :
out ower x = match x with
rose a b c ! (Sum ( ower rose ) (Prod
( ower int a )
(Prod (Par b ) (shift out ower (out tree x ))))
inn ower x = unshift ower parshift x
unshift ower f x = match x with
Sum ower rose (Prod ( ower int a ) (Prod p r )) !
(rose a (f p ) (unshift tree inn ower r ))
0
The function shift is needed when type composition occurs. Actually, to compute the morphism from ( ) to Poly ,
a solution is : rst, compute the morphism from to Poly ;
second, compose this rst morphism with the one from ( )
to ( ). The second morphism is simply computed by the
function shift , where f is supposed to be a morphism from
to Poly .
shift f x = match x with
(Par u ) ! (f u )
(Prod a b ) ! (Prod (shift f a ) (shift f b ))
(Sum c u ) ! (Sum c (shift f u ))
(Obj o ) ! (Obj o)
6
Annex B
Correspondence relations
From this association, the couplage function is easily inferred. Of course, many improvements could be done about
correspondence relations. In the last step of the algorithm,
the problem to solve is how to associate a closed tree of
type 1 to some value of type 2 . The solution proposed
here is quite simple by associating signatures to constructors. Commutativity, associativity, parsing may be taking
into account to nd more complex associations.
Let C or be a correspondence relation from type 1 to type
2 . We de ne the following objects :
Components : each type consists of several components.
Each constructor, each argument of a constructor and
each occurrence of a (sub-)type are components. The
correspondence relation links components from type 1
to components from type 2 .
Key component : a component is a key one if it is linked
by the correspondence relation.
Neutral component : a component is a neutral one if it
contains a key or neutral sub-component.
Dead component : a component that contains neither key
nor neutral is dead.
Then, from a correspondence relation, it is possible to tag
each component of a type. This de nes the Tag annotation.
For instance, with the previous exemple (c; i stands for the
i-th argument of constructor c) :
Tag (nothing ) = key (tree12 )
Tag (one ) = neutral (elements )
Tag (one ; 1 ) = key ( )
Tag (node ) = key (tree12 )
Tag (node ; 1 ) = neutral (elements )
Tag (node ; 2 ) = key (tree12 )
etc :
To generate the morphism, it is necessary to look for
closed -terms. A closed term is a nite term, whose root
and leaves are tagged by key, and that contains only neutral internal constructors (dead components are discarded).
For instance, (node (two a1 a2 ) t1 t2 ) is closed. But
(node e1 t1 t2 ) is not closed, since e1 is tagged by neutral.
And (node (one a1 ) (nothing) t2 ) is nor closed, since the
constructor nothing replaces an internal key component.
It is easy to generate the closed-terms by a transitive
closure (or x-point) algorithm. The idea is to recursively
replace a neutral leaf of a non-closed term by any sub-term
of type . Of course, there exist conditions to insure the
termination of the algorithm. See [11] for more details.
Now, the notion of signature is needed. The signature
of a term is the list of its root and leaves key -tags. For
instance, with the previous example, the closed-terms and
their signature are :
node (one a1 ) t1 t2 : !
node (two a1 a2 ) t1 t2 :
!
nothing : () !
Signature is extended to constructors of type 2 . Thus,
the signature of the constructor node1 is the same as of the
closed term (node (one a1 ) t1 t2 ) one. Then, each closedterm of type 1 is associated with one constructor of type
2 that has the same signature. With the previous example,
the following association is obtained :
node (one a1 ) t1 t2 ) node1
node (two a1 a2 ) t1 t2 ) node2
nothing ) leaf
7