Complexity and Implementation of Nominal Algorithms

advertisement
Complexity and Implementation
of Nominal Algorithms
Christophe François Olivier Calvès
Department of Computer Science
King's College London
A thesis submitted for the degree of
Doctor of Philosophy
1
I would like to dedicate this thesis to my loving grandparents and
the teachers who believed in me ...
Acknowledgements
I want to express the deepest gratitude to my supervisor, Maribel Fernández, for
all the support, the constant help and the dedication she put into teaching me
how to be a good researcher. I wish to be one day, for my students, as nice as
she has been for me.
I wish to thank particularly Olivier Danvy for having invited me at BRICS
in Aarhus. It was a wonderful and very rewarding experience, as much on the
research level as the personal level. Without him the part of this thesis about
continuations would have been much lighter. I thank Mathieu Boespug and Zoé
Drey for the great time it was searching and living with them in Aarhus. I wish
the stay could have continued more, but reaching the end of the PhD, time was
delimited.
I thank James Cheney, Murdoch J. Gabbay, Andrzej Filinski, Aad Mathijssen,
Dale Miller, Andrew Pitts and Christian Urban for the precious discussions we
had. The experience their shared with me helped directing my eorts in the right
direction.
I thank Agi Kurucz for having been my second supervisor and the nice time
it was to be her teaching assistant.
I thank the Engineering
and Physical Sciences Research Council
for the grant
that enabled me to live three years studying a subject I like.
Last but not least, I thank particularly Clémentine for her p atience, lo ve and
the u nlimite d motivat ion she gives to me.
And many thanks to
Maribel, Zoé
and
3
Clémentine
for the proofreading.
Abstract
Nominal terms generalise rst-order terms by introducing abstraction
and name swapping constructs. It aims to be a natural and simple way
to represent systems that involve binders.
-equivalence, unication,
matching and rewriting can be generalized to nominal terms.
This thesis shows that rst-order and nominal theories are strongly
related. In particular, we extend rst-order unication algorithms,
such as Patterson and Wegman's linear one and Martelli and Montanari's almost linear one, to nominal unication. We then present
a modular algorithm to solve nominal
-equivalence
and matching
problems. The approach relies on name management abstraction and
stream manipulation. We show how choosing an appropriate name
management strategy leads, in some cases, to linearity in time. The
matching algorithm is then adapted to nominal rewriting, improving
in some cases the time complexity.
Handling name management, freshness constraints and specic reduction strategies simultaneously makes nominal algorithms intricate.
This thesis presents a high level and modular approach to the implementation of nominal algorithms. Each nominal aspect is implemented as a monadic layer. We show how the CPS hierarchy can
be used to separate the signature of each layer from its actual implementation thus enabling to use dierent interpretation functions.
We also present a rewriting framework based on the previous nominal
rewriting algorithm and a term navigation monadic layer based on a
zipper.
For each of the algorithms, an implementation is available either in
the Haskell language or in the Objective Caml.
Contents
Acknowledgements
3
Abstract
4
1 Introduction
10
I Generalities
14
2 Presentation of Haskell and Continuations
15
2.1
Haskell Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
2.2
Monads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
2.3
Continuations . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
3 First-Class Monadic Signatures
21
3.1
A simple example . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
3.2
A specialized continuation monad . . . . . . . . . . . . . . . . . .
24
3.3
A more complex example . . . . . . . . . . . . . . . . . . . . . . .
25
3.4
General morphism
. . . . . . . . . . . . . . . . . . . . . . . . . .
27
3.5
Monadic Tower and the CPS Hierarchy . . . . . . . . . . . . . . .
30
3.5.1
Lifting operations . . . . . . . . . . . . . . . . . . . . . . .
32
3.5.2
Well behaved Error Monad . . . . . . . . . . . . . . . . . .
33
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
3.6
4 Nominal Theory
36
4.1
Graphs and Trees . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
4.2
Nominal Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37
5
CONTENTS
4.3
4.4
4.2.1
Standard Nominal Term . . . . . . . . . . . . . . . . . . .
40
4.2.2
Compact Syntax . . . . . . . . . . . . . . . . . . . . . . .
41
4.2.3
Reduced Compact Syntax . . . . . . . . . . . . . . . . . .
43
4.2.4
From Standard Terms To Compact Terms . . . . . . . . .
45
4.2.5
From Nominal to First-Order . . . . . . . . . . . . . . . .
48
Nominal Problems . . . . . . . . . . . . . . . . . . . . . . . . . .
49
4.3.1
Morphisms . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
Complexity and Implementation of Permutations and Sets . . . .
57
4.4.1
Implementation . . . . . . . . . . . . . . . . . . . . . . . .
58
4.4.2
Optimisation . . . . . . . . . . . . . . . . . . . . . . . . .
60
4.4.3
Abstraction of sets and permutations . . . . . . . . . . . .
61
4.4.4
Complexity . . . . . . . . . . . . . . . . . . . . . . . . . .
62
II Unication
63
5 Nominal Unication Terms and Problems as Directed Acyclic
Graphs
65
6 Quadratic Unication Algorithm
69
6.1
Nominal Unication as a Relation on Term Nodes . . . . . . . . .
69
6.2
A Quadratic Nominal Unication Algorithm . . . . . . . . . . . .
73
7 A Simple and Ecient Nominal Unication Algorithm
80
7.1
A Nominal Union-Find Algorithm . . . . . . . . . . . . . . . . . .
81
7.2
An Almost Quadratic Nominal Unication Algorithm
. .
86
Complexity . . . . . . . . . . . . . . . . . . . . . . . . . .
91
7.2.1
(AQNU)
8 Nominal Unication via Graph Rewriting
8.1
93
A polynomial Algorithm via graph rewriting . . . . . . . . . . . .
96
8.1.1
. . .
96
8.1.1.1
Normalisation of the graph . . . . . . . . . . . .
96
8.1.1.2
Properties . . . . . . . . . . . . . . . . . . . . . .
99
A graph rewriting algorithm to solve
constraints .
8.1.1.3
-rules .
8.1.1.4
Computing permutations: Neutralisation . . . . . 111
. . . . . . . . . . . . . . . . . . . . . . 106
6
CONTENTS
8.1.1.5
Putting all together . . . . . . . . . . . . . . . . 113
8.1.1.6
An upper bound on the number of iterations . . . 114
8.1.1.7
An upper bound on the number of normalisation
steps . . . . . . . . . . . . . . . . . . . . . . . . . 116
8.1.1.8
Cost of the rules . . . . . . . . . . . . . . . . . . 118
8.1.2
Freshness constraints . . . . . . . . . . . . . . . . . . . . . 121
8.1.3
Cost . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
8.1.4
Total cost in time for the unication algorithm . . . . . . . 124
III -equivalence, Matching and Rewriting
127
9 A modular algorithm
129
9.1
Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
9.2
Core algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
9.3
Checking the validity of -equivalence constraints . . . . . . . . . 138
9.4
Solving Matching Problems . . . . . . . . . . . . . . . . . . . . . 139
10 Implementation
141
10.1 The Freshness Layer . . . . . . . . . . . . . . . . . . . . . . . . . 142
10.2 Only one environment: The environment layer . . . . . . . . . . . 143
10.3 Combining the phases: Streams . . . . . . . . . . . . . . . . . . . 146
10.4
-equivalence and Matching
. . . . . . . . . . . . . . . . . . . . . 149
10.5 Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.6 Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
11 A Simple Nominal Rewriting Framework
156
11.1 Nominal rewriting . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
11.2 A nominal rewriting algorithm: two improvements on the matching
algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
11.3 Complexity of nominal rewriting . . . . . . . . . . . . . . . . . . . 161
11.3.0.1 Special cases . . . . . . . . . . . . . . . . . . . . 162
11.4 The Zipper Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
7
CONTENTS
IV Conclusions
164
Related Works
165
Future Works
168
Conclusion
170
Bibliography
176
A Haskell Code
177
A.1 Base denitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
A.2 Fist-Class Monadic Signatures . . . . . . . . . . . . . . . . . . . . 180
A.3 Error Handling Layer . . . . . . . . . . . . . . . . . . . . . . . . . 185
A.4 Store Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
A.5 Nominal Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
A.6 Sets and Permutation Layer . . . . . . . . . . . . . . . . . . . . . 198
A.7 Nominal Union-Find Algorithm . . . . . . . . . . . . . . . . . . . 210
A.8 Unication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
A.9 Environment Layer . . . . . . . . . . . . . . . . . . . . . . . . . . 228
A.10 -equivalence and Matching Algorithms
. . . . . . . . . . . . . . 235
A.11 Zipper Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
B Objective CAML Code
250
B.1 Quadratic Nominal Unication
8
. . . . . . . . . . . . . . . . . . . 250
List of Algorithms
1
First-Order Linear Unication
(FLU): Part 1
. . . . . . . . . . . .
74
2
First-Order Linear Unication
(FLU): Part 2
. . . . . . . . . . . .
75
3
Quadratic Nominal Unication
(QNU): Part 1
. . . . . . . . . . .
76
4
Quadratic Nominal Unication
(QNU): Part 2
. . . . . . . . . . .
77
5
Quadratic Nominal Unication
(QNU): Part 3
. . . . . . . . . . .
78
6
Tarjan's union-nd algorithm
. . . . . . . . . . . . . . . .
82
7
Nominal Union-Find Algorithm
(NUF): Part 1
. . . . . . . . . . .
84
8
Nominal Union-Find Algorithm
(NUF): Part 2
. . . . . . . . . . .
85
9
Unication and normalizing functions . . . . . . . . . . . . . . . .
88
10
Equation storing function . . . . . . . . . . . . . . . . . . . . . . .
90
11
Interpretation of Freshness when Solving an -equivalence or matching problem
(TUF)
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
12
Phase 2 and 4
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
13
Streamed version of Phase 1
. . . . . . . . . . . . . . . . . . . . . 147
14
Streamed version of Phase 3
. . . . . . . . . . . . . . . . . . . . . 148
15
The core algorithm
. . . . . . . . . . . . . . . . . . . . . . . . . . 149
17
-equivalence Interpretation of core
Matching Interpretation of core . .
18
Interpretation of Freshness for matching terms in context
16
9
. . . . . . . . . . . . . . . . . 150
. . . . . . . . . . . . . . . . . 150
. . . . . 160
Chapter 1
Introduction
This thesis is the result of my three years of research as a PhD student at the
Department of Computer Science
at
King's College London.
The subject was to
study nominal algorithms, both from a theoretical point of view by studying their
complexity and from a practical one by studying their implementations.
The notion of a binder is ubiquitous in computer science. Programs, logic
formulas, and process calculi are some examples of systems that involve binding.
Program transformations and optimisations, for instance, are dened as operations on programs, and therefore work uniformly on
-equivalence
classes. To
formally dene a transformation rule acting on programs, we need to be able to
distinguish between free and bound variables, and between meta-variables of the
transformation rule and variables of the object language. We also need to be
able to test for -equivalence, and we need a notion of matching that takes into
account -equivalence.
In
Jones
The Implementation of Functional Programming Languages
shows how manipulating terms up to
-equivalence
[41]
Peyton-
is a big issue in the
implementation of functional programming language.
Nominal techniques were introduced to represent in a simple and natural
way systems that include binders [24, 42, 48]. The nominal approach to the
representation of systems with binders is characterised by the distinction, at
the syntactical level, between
abstracted (we use the notation
variables
atoms
(or object-level variables), which can be
[a]t, where a is an atom and t is a term), and meta-
(or just variables), which behave like rst-order variables but may be
10
decorated with atom permutations. Permutations are generated using swappings
(a b) t means swap a and b everywhere in t). For instance, (a b)[a]a =
[b]b, and (a b)[a]X = [b](a b)X (we will introduce the notation formally in
(e.g.,
chapter 4.2). As shown in this example, permutations suspend on variables. The
idea is that when a substitution is applied to
be applied to the term that instantiates
X.
X
in
As atoms are just names, we want to identify
(a b)X , the permutation will
[a]a and [b]b and even [a]X and
[b]X with some conditions. Permutations of atoms are one of the main ingredients
in the denition of -equivalence for nominal terms, they are the translation tables
from one term to another. Obviously such translations are subject to conditions,
[a]X and [b]X can only be identied if neither a nor b are unabstracted in X . The
constant management of such translations and conditions is what makes nding
and implementing good nominal algorithms not an easy task.
As we will see in this thesis, we managed to show that unication of nominal
terms can be done in quadratic time and that -equivalence and matching problems can even sometimes be done in linear time and space. Implementing these
algorithms was not an easy task because of the amount of subtle atom management details. We found an elegant and easy way of programming with nominal
terms, handling as many details as possible automatically and eciently.
Such an implementation was possible thanks to the visit the author made at
the
BRICS
[1] PhD school with Olivier Danvy. This is where the second big
aspect of this thesis was developed: continuations. Chapter 3 is a result of the
thought developed during the visit. The whole
Haskell
implementation is based
on it. It enabled the implementation to be clearer, more exible and manageable.
The main contributions of this thesis are:
A study of relations between nominal theory and rst-order theory. We
present a morphism between nominal theory and rst-order theory. This
morphism enabled us to extend algorithms and properties in rst-order
theory to nominal theory.
A quadratic nominal unication algorithm, the most ecient unication
algorithm yet.
11
An almost quadratic nominal unication algorithm: while being almost as
ecient as the one above, it is also very easy to use, to maintain and to
extend, which makes it often the best choice in practice.
A polynomial nominal algorithm based on graph reduction: it gives inter-
An ecient, very modular algorithm for nominal -equivalence and match-
esting results and techniques for rewriting up to -equivalence.
ing: its complexity is linear on ground problems, log-linear on linear problems, which is very frequent in practice.
An implementation of monadic classes which makes them rst-class citizen
of the language and makes the instantiation explicit: it makes monadic
towers much simple by giving them a concrete representation. It can also
bring monadic classes to some languages which do not natively support
them.
A nominal framework made of monadic layers handling dierent nominal
aspects (permutations, sets of atoms, freshness contexts, environments, . . . )
to ease the development of program manipulating nominal terms. With is
programming with nominal terms is almost as easy as programming with
rst-order terms.
The thesis is structured as follows:
The rst part presents general results. Chapter 2 presents briey the
Haskell
programming, language, its syntax, monads in computer science and continuations. Chapter 3 introduces nominal theory: nominal terms with several syntaxes
and how they are related, nominal problems and implementation of permutations
and sets.
The second part is about solving nominal unication problems. Chapter 5
shows how to represent nominal terms as directed acyclic graphs. This representation is used to present, in chapter 6 a quadratic algorithm for nominal
unication based on a linear rst-order algorithm. Chapter 7 introduces a more
practical and still ecient algorithm. Finally chapter 8 presents a polynomial
algorithm based on graph rewriting.
12
The third part is about solving -equivalence and matching problems. Chap-
ter 9 presents, in an abstract way, a very modular algorithm to solve -equivalence
and matching problems. Chapter 10 describes its implementation and proves its
complexity. Finally, chapter 11 presents a simple nominal rewriting algorithm
based on this algorithm and a zipper.
The last part of the thesis concludes and shows the actual implementation
of the algorithms written in
Haskell
and
Objective Caml.
The code has been
put in the appendices for two reasons: because it is the most precise description
of the algorithms presented in the thesis, and as a real world example of the
implementation techniques developed in the thesis. The code can also be found
at [5].
13
Part I
Generalities
14
Chapter 2
Presentation of Haskell and
Continuations
Most of the implementations presented in this thesis are written in the
Haskell
programming language. This section presents briey the main aspects and the
syntax of the language to enable the reader to understand the examples. For
a detailed description, please refer to the
Haskell 98 Revised Report
[3]. The
implementations use many extensions not present in the report but described
in the
GHC
documentation [2]. To learn about the language, the
Haskell
web
site [4] is a good entry point.
Haskell
is dened in the
Haskell 98 Revised Report
Haskell is a general purpose,
as
purely functional
programming lan-
guage incorporating many recent innovations in programming language design. Haskell provides higher-order
functions, non-strict
semantics, static polymorphic typing, user-dened algebraic datatypes,
pattern-matching, list comprehensions, a module system, a monadic
I/O system, and a rich set of primitive datatypes, including lists, arrays, arbitrary and xed precision integers, and oating-point numbers. Haskell is both the culmination and solidication of many years
of research on non-strict functional languages.
Purely functional means that the value of any expression in
Haskell
only
depends on the expression itself and not the context. As a consequence, values are
15
2.1 Haskell Syntax
persistent and when applied to the same arguments, a function always returns the
same value. Non-strict semantics means that it is possible that an expression
returns a value (not
?) even if some of its subexpressions evaluate to ?.
example, let us consider the following
1
2
3
let
in
Haskell
For
code:
loop () = loop ()
f
x = 0
f ( loop ( ) )
where loop is a recursive function taking () as argument and looping on itself
0
and f is a function ignoring its argument and returning . loop () never terminates
0
but f (loop ()) still returns .
2.1 Haskell Syntax
Denitions
1
Variables are dened by:
var = e x p r e s s i o n
where var is the variable name and expression an expression. Variables are immutable in Haskell, so variables can be seen as identiers for their expressions. In
Haskell,
denitions are recursive, which means that var can appear in expression.
0
For example, the following code denes an innite list of :
1
list = 0 : list
Functions
1
In
\ arg1 . . . argn
where n >
Haskell,
a function is an expression of the form:
> expression
0 and arg1 to argn are the n arguments of the function. The character \
represents in -calculus. Because functions are expressions, they can be assigned
to variables as any expression. For example the function
f
dened above could
have been dened by:
1
f = \x
> 0
For the sake of simplicity, a simpler way to dene functions is:
1
f arg1 . . . argn = e x p r e s s i o n
16
2.1 Haskell Syntax
where f is the name of the function.
If an argument is not used in the body of the function, it can be replaced by
_.
Type declarations
type is written variable
by:
1
2
5
::
type . For example,
list and f could have been dened
Integer
list :: [
]
list = 1 : list
3
4
In Haskell, declaring that the variable variable has the type
f :: a >
f x = 0
Integer
which means list is a list of integers and f is a function whose argument can
be of any type and returns an integer.
Pattern-Matching
Haskell
supports pattern-matching. It can be used in sev-
eral ways:
1
2
3
4
by a
case expression:
case e x p r e s s i o n where
pattern1
...
patternn
> expression1
> expressionn
where expression, expression1,
:::,
expressionn are expressions and pattern1 to
patternn are patterns. The value of the
case expression is the value of the
expression of the rst pattern (from pattern1 to patternn) to match the value
of expression.
1
2
3
in a function denition:
f pattern1
...
f patternn
= expression1
= expressionn
which corresponds to
17
2.1 Haskell Syntax
1
f x =
2
3
4
case
of
x
pattern1
...
patternn
> expression1
> expressionn
User-dened algebraic datatypes
Users can dened their own algebraic
datatypes. The syntax is:
1
2
3
4
data
DataTypeName a r g s =
Constructor1 args1
| ...
| Constructorp argsn
where DataTypeName is the name of the data type, args a list of type variables,
Constructor1 to Constructorn
n constructors where args1 to argsn are the lists of the
types of the arguments of their corresponding constructor.
For example, the following code denes a parametrized binary tree data type:
1
2
3
4
data
Tree l e a f t y p e =
Empty
| Leaf l e a f t y p e
| Node ( Tree l e a f t y p e ) ( Tree l e a f t y p e )
Type classes
Type classes are the
Haskell
way to implement ad-hoc polymor-
phism. Let us consider the equality function (==), which takes two arguments
and returns whether or not they are equal. In
1
2
3
this function has the type
Bool. (Eq a) => is a type constraint, it means that
the type a has to be an instance of the class Eq which is dened as:
class Eq a where
(==) : : a > a > Bool
(/=) : : a > a > Bool
which means that to be an instance of Eq, a has to provide the two functions
(==) and (/=), both of type a > a > Bool.
(==) :: (
Eq a) =>a
Haskell
>a
>
18
2.2 Monads
2.2 Monads
Monads are a general way to represent hidden computation [36]. In
Haskell,
among other things, they provide a simple way to implement side eects. A
monad in Haskell is a datatype which is an instance of the Monad class (simplied
version):
1
2
3
class Monad m where
return : : a
(>>=)
:: m a
> (a
> m b)
>m a
>m b
The idea is that you can inject a value into a monad (return) and bind a
monad with a function ((>>=)) but you cannot, in general, retrieve a value from
a monad. For example, the datatype of lists is a monad:
1
2
3
instance Monad [ ] where
return x = [ x ]
m >>= f = concat (map f m)
return x returns the list containing only the element x and (>>=) maps every
element of the list m by the function f of type a
> [b] and returns the list of all
the generated elements.
IO.
Input/Output operations are implemented in
Haskell
via an abstract monad
On the contrary to most programming languages, input/output operations
have to be executed only in the
IO monad and nowhere else.
2.3 Continuations
Continuations were introduced by Landin [32] to be the functional counterpart
of jumps in programming languages (GOTO). They have known since a big development. In this thesis we will not use any of the many control operators that
have been introduced since Landin's J operator but plain and simple code in con-
tinuation passing style
in the form of a slight variation of the usual
continuation monad (simplied version):
1
data
Cont r a = Cont ( a
> r)
> r
19
Haskell
2.3 Continuations
which means that a code in continuation passing style which takes a function
as argument called the
continuation and returns value.
The continuation rep-
resent the rest of the computation. For example, the following code just gives
the value
1
5 to the rest of the computation:
g i v e 5 = Cont (\ k
> k 5)
The following code, if the second argument is
0 does not execute the rest of
the computation, instead it returns directly a string saying there was an error:
1
2
3
div
x y = Cont $ \k
>
if
y == 0
" d i v i s i o n by z e r o "
k (x / y)
then
else
Code in continuation passing style can decide whether or not they want to
continue the computation and even how many times to do it. For a more detailed
description of continuation passing style (CPS) and how to how to transform any
code into CPS, please refer to [15, 16, 46].
20
Chapter 3
First-Class Monadic Signatures
A monadic
signature is an abstract monad along with a set of operations on it.
The usual way of doing it is, in Haskell, to dene a class. In Objective
would declare a module
signature
rst-class citizens in either
CAML,
we
even if it is not the same thing. Classes are not
Haskell
or
Objective CAML.
We present a way to do
so in Haskell. The implementation relies on the use of existential quantication in
datatypes, rank
n polymorphism and higher kinded type constructors.
be possible to do the same in
That is why we present it in
Objective Caml,
but not as easy as with
It might
Haskell.
Haskell.
The idea is similar to defunctionalization [17, 46]. The abstract monad op-
erations are represented by a datatype, the abstract monad being implemented as
a concrete continuation monad. As in defunctionalization, concrete instantiations
of the monad are implemented by an interpretation function.
3.1 A simple example
In this example we consider a class with logging capabilities. It contains a single
operator write taking the string to log and returning the number of char written.
The usual way of doing it in Haskell is the following:
1
2
class (Monad m) => Log m where
w r i t e : : String > m Int
The constraint (Monad m) => ensures that m is also a monad. Let us consider
a piece of code using write
21
3.1 A simple example
1
2
3
4
=> Int
t e s t : : ( Log m)
m
test =
x < w r i t e " F i r s t w r i t e \n"
y < w r i t e " Second w r i t e \n"
(x + y)
do
return
Indeed the constraint (Log m) => ensure that m is an instance of Log. Two
instances of Log could be: the state monad with a string state (State
write s would append s to the state and the IO monad (
String),
IO), write s would print s
on the standard output:
1
2
3
4
5
instance
String ) where
Log ( S t a t e
w r i t e s = modify (\ l
instance
Log
write s =
> l ++ s ) >>
return ( length
IO where
putStr s >> return ( length
s)
s)
and the monad m in test can be instantiated as any of them:
1
2
testState : : State
testState = test
3
4
5
String Int
IO Int
testIO : :
testIO = t e s t
Let us dene writeState and writeIO as
1
2
3
4
5
String
> State
String
>
writeState : :
writeState s = write
writeIO
writeIO
::
s = write
String Int
IO Int
We present now another way to implement and instantiate the class Log without class constraints, based on continuations. The Log class is implemented as
the datatype :
1
data
DLog r = DWrite
String ( Int
> r)
and the abstract previous monad m satisfying the class Log is implemented as the
concrete continuation monad.
The idea is, when an operation of the class is called, to stop the computation
and return the constructor corresponding to the invoked call, with its arguments
and the captured continuation. DWrite would be something like
22
3.1 A simple example
1
2
d w r i t e s t r i n g = Cont $ \ c o n t i n u a t i o n >
DWrite s t r i n g c o n t i n u a t i o n
Unfortunately this is not that simple. Returning DWrite string continuation requires that r is of type DLog r. Furthermore, the rest of the computation may
contains other calls, so other DWrite string' continuation' and nally, if it exists, the
nal value of the computation. To make all this work nely we need to introduce
a data type for such a sequence of calls possibly ended by a nal value:
1
2
3
data
SCall datatype f i n a l v a l u e =
End f i n a l v a l u e
| C a l l ( datatype ( SCall datatype f i n a l v a l u e ) )
when datatype is DLog, it gives (in pseudo Haskell) :
1
2
3
data
SCall DLog f i n a l v a l u e =
End f i n a l v a l u e
| C a l l ( DWrite
(
String Int
> SCall DLog f i n a l v a l u e ) )
what is what we need.
dwrite is then implemented as
1
2
String
Int
dwrite : :
> Cont ( SCall Log f i n a l v a l u e )
d w r i t e s t r i n g = Cont (\ cont > C a l l ( DWrite s t r i n g cont ) )
Instantiation is made by an interpretation function for DWrite string continuation.
This function actually writes string and resume the continuation with the returning value. For example, the interpretation functions for the two previous instances
(State
1
2
String and IO) are:
: : DLog a > S t a t e
( DWrite s k ) =
r <
interpretIO
interpretIO
: : DLog a >
( DWrite s k ) =
do
3
4
5
6
String
interpretState
interpretState
IO a
do
7
a
writeState s
(k r )
return
r <
writeIO s
(k r )
return
the nal building block is an instantiation function. It takes two arguments:
an interpretation function (such as interpretState and interpretIO) and an abstract
computation and interprets the stream of calls with the interpretation function,
thus instantiating the computation:
23
3.2 A specialized continuation monad
1
2
3
4
Monad
=>
instantiate : : (
m)
( t h e c l a s s ( SCa ll t h e c l a s s a )
> Cont ( SCall t h e c l a s s a ) a
>m a
> m ( SCall t h e c l a s s a ) )
5
6
7
8
9
i n s t a n t i a t e i n t e r p r e t t = aux ( runCont t End)
aux (End
r) =
r
aux ( C a l l c ) =
n < interpret c
aux n
where
return
do
For example test is simply
1
2
3
4
do
return
and instantiated with
1
2
5
String
dtestState : : State
a
dtestState = instantiate interpretState dtest
3
4
Int
d t e s t : : Cont ( SCall DLog e )
dtest =
x < d w r i t e " F i r s t w r i t e \n"
y < d w r i t e " Second w r i t e \n"
(x + y)
IO
dtestI O : :
a
dtestI O
= instantiate interpretIO dtest
3.2 A specialized continuation monad
We introduce a variation of the continuation monad, specialized to our needs.
We have seen in the previous section that the continuation was of type
a
> SCall datatype nal
nal can also be universally quantied.
8final (a ! SCall datatype final) ! SCall datatype final
which gives in
1
2
Haskell
newtype
SCont datatype a = SCont { unSCont : :
f o r a l l r . ( a > SCall datatype r ) > SCall datatype r ) }
SCont theclass is a monad:
24
3.3 A more complex example
1
2
3
instance Monad
return a
( SCont t h e c l a s s )
= SCont $ \k
( SCont m) >>= f = SCont $ \k
where
> k a
> m (\ x
> unSCont ( f x ) k )
and
return a >>= f
m >>= return
(m >>= f ) >>= g
,
fa
,
m
, m >>= (x ! fx >>= g)
This a particular case of the general continuation monad:
1
2
newtype
LContT m a = LContT { unLContT : :
forall r . (a > m r ) > m r }
which is just the usual ContT continuation monad transformer (see section 3.5)
with universally quantied return type.
Jaskelio calls in [28] this monad the
codensity monad.
Jaskelio's coden-
sity monad seems to come from considerations in category theory. For the scope
of this thesis, this is just a continuation monad.
3.3 A more complex example
Let us consider a monadic class for exception handling:
1
2
3
class Monad m => MonadError m error where
throwError : : error > m a
c a t c h E r r o r : : m a > ( error > m a ) > m a
This is not the real
library
Haskell
MonadError dened in the
Monad transformer
but a simplied version of it without error messages. The semantic of
throwError and catchError is not important here as our only concern is correctly
implementing the signature and instantiation. As before we want to implement
the class MonadError as a data type DMonadError and throwError and catchError as
1
2
error
dthrowError
SCont $ \k
=
> C a l l ( DThrowError
dcatchError v h
SCont $ \k
=
> C a l l ( DCatchError v h
error
3
4
5
25
k)
k)
3.3 A more complex example
error is a parameter of MonadError, it has to be also a parameter of DMonadError.
a is a universally quantied type variable in throwError and catchError, it has to be
also universally quantied in DThrowError and DCatchError. Furthermore, because
the abstract monad m satisfying MonadError will be implemented as the concrete
error) monad. The arguments of dcatchError have to be of
type SCont (DMonadError error) a and error > SCont (DMonadError error) a instead
of m a and error > m a. The datatype implementing MonadError is then:
data DMonadError error r =
SCont (DMonadError
1
2
3
4
5
f o r a l l a . DThrowError ( a > r )
| f o r a l l a . DCatchError ( SCont ( DMonadError
) a)
(
> SCont ( DMonadError
(a > r )
error
error
error )
a)
In practice it is easier to dene DMonadError as
1
2
3
4
5
data
error
DMonadError
mc r =
f o r a l l a . DThrowError ( a > r )
| f o r a l l a . DCatchError (mc a )
(
> mc a )
(a > r )
error
and modify SCall to instantiate mc to SCont theclass:
1
2
3
4
5
data
SCall datatype f i n a l v a l u e =
End f i n a l v a l u e
| C a l l ( t h e c l a s s ( SCont datatype )
( SCall datatype f i n a l v a l u e )
)
As expected dthrowError and dcatchError are
1
2
3
error
error =
dthrowError : :
dthrowError
SCont $ \k
> SCont ( DMonadError
> C a l l ( DThrowError
error )
error
a
k)
4
5
6
7
8
9
error
error
d c a t c h E r r o r : : SCont ( DMonadError
) a
> (
> SCont ( DMonadError
) a)
> SCont ( DMonadError
) a
d c a t c h E r r o r v h = SCont $ \k > C a l l ( DCatchError v h
error
error
k)
The interpretation function follows the same principles as before but we
can not directly give the arguments stored by dcatchError to catchError because
26
3.4 General morphism
dcatchError arguments are of types SCont (DMonadError
(
error
> SCont (DMonadError
m a and
error
error) a and
error) a whereas catchError accepts arguments of types
> m a where m is an abstract monad satisfying the MonadError class.
So we have to map arguments from SCont (DMonadErorr error) to m what is exactly
what instantiation is. The interpretation function is then:
1
2
3
4
5
error
i n t e r p r e t E r r o r : : ( MonadError
m)
DMonadError
( SCont ( DMonadError
( SCall ( DMonadError
> m ( SCall ( DMonadError
) a)
error
error
6
7
8
9
i n t e r p r e t E r r o r ( DThrowError
v < throwError
$ k v
do
error
return
error
=>
error ) )
error ) a )
k) =
10
11
12
13
14
15
i n t e r p r e t E r r o r ( DCatchError m h
k) =
m'
= instantiate interpretError
h' e = instantiate interpretError $
v < c a t c h E r r o r m' h '
$ k v
let
in do
m
h e
return
Obviously the instantiation function from SCont (DMonadError error) to m is
1
2
error m) =>
error ) a > m a
i n s t a n t i a t e E r r o r : : ( MonadError
SCont ( DMonadError
3
4
instantiateError = instantiate interpretError
3.4 General morphism
On a general manner we can see all previous monadic operations as
1
op
:: 8:
F
m!my
where F is a functor from the category of monads to a category
In the case of write,
C
C.
is the category of strings (String). For throwError, C is
the categories of values of type
error.
For catchError, C is the category of values
of type
27
3.4 General morphism
(m a ,
error
> m a)
. write (resp. throwError) takes the same arguments of dwrite (resp. dthrowError)
because in this case
=>F m = F (SCont DLog) (resp.
(MonadError error m) =>F m = F (SCont (DMonadErorr error))
(Log m)
. But in the case of catchError,
(MonadError error m) ) F m 6= F (SCont (DMonadErorr error))
so to interpret DCatchError we had to map the arguments from
SCont (DMonadError
error)
= instantiate interpretError
= instantiate interpretError $
m
h e
to m. The piece of code
1
2
3
let
in
m'
h' e
...
is actually equivalent to
1
let
(m' , h ' ) = F ( i n s t a n t i a t e i n t e r p r e t E r r o r ) (m , h )
in
...
because instantiate interpretError is a function of type
(MonadError
error m) =>SCont (DMonadError error m) a =>m a
so F ( instantiate interpretError) maps arguments of dcatchError to arguments of catchError.
Let us formalize the transformation and consider the following class:
1
2
3
4
class Monad m ) TheClass m where;
op1 :: 8 1 : F1 m ! m y1 ;
: : :;
opp :: 8 p : Fp m ! m yp
where (Monad m) => indicates that m has to be a monad. opi are the p
operations that m has to implement. and i are lists of distinct type variables.
28
3.4 General morphism
opi
1
:: 8(m :: ! ) i : (TheClass m overalpha) ) Fi m ! m yi
are class parameters common to every opi whereas i are not parameters of the
class but specic to every opi . Such a class is called a monadic class signature.
The complete type of each opi is
The class TheClass is implemented as the algebraic data type with existential
quantication TheDataType, called a
monadic signature datatype:
data DataType n r =;
8 1 : Op1 (F1 n) (y1 ! r);
j ::: ;
j 8 p : Opp (Fp n) (yp ! r)
1
2
3
4
Every Opi is the constructor corresponding to opi . It has type
Opi
1
:: 8 n r i : Fi n ! (yi ! r) ! DataType n r
The general form of SCall and SCont are
1
2
3
4
5
6
7
data
SCall datatype e =
End e
| C a l l ( t h e c l a s s ( SCont datatype )
( SCall datatype e )
)
newtype
8
9
}
SCont datatype a = SCont { unSCont : :
f o r a l l e . ( a > SCall datatype e ) > SCall datatype e
For the correctness of the transformation, using directly the constructors End
and Call will be forbidden. The following function call will be used to implement
monadic calls:
1
2
3
c a l l : : ( f o r a l l r . ( a > r ) > datatype ( SCont datatype ) r )
> SCont datatype a
c a l l a = SCont (\ k > C a l l ( a k ) )
every op_i is then implemented as:
29
3.5 Monadic Tower and the CPS Hierarchy
:: 8 i : Fi (SCont datatype) ! SCont datatype yi;
t = call (Opi t)
2
dopi
dopi
1
interpret (Opi
1
2
t) = do r opi (Fi (instantiate interpret) t);
return (k r)
and interpretation functions as:
The instantiation function becomes:
1
2
3
4
5
6
7
8
9
Monad
=>
instantiate : : (
m)
( datatype ( SCont datatype ) ( SCall datatype a )
> m ( S Call datatype a )
)
> SCont t h e c a l l a > m a
i n s t a n t i a t e i n t e r p r e t t = aux ( unSCont t End)
aux (End
r) =
r
aux ( C a l l c ) =
n < interpret c
aux n
where
return
do
3.5 Monadic Tower and the CPS Hierarchy
Monad transformers [29] are a way to build a new monad on top of another one.
It enables us to combine monads and thus their eects instead of having to dene
a monolithic monad. For example, let m be a monad. To add a global state to m,
we can simply use the StateT monad transformer dened in the
1
newtype
StateT s m a = StateT { runStateT : : s
GHC library
as:
> m (a , s ) }
StateT s m is the monad with as a global state of type s and two operations get
and put to fetch the state or put one. Because it has been made from m, it has
the behavior or m.
We can use another monad transformer on it: for example, to give StateT s m
the capabilities of the list monad, we can use the ListT transformer:
1
newtype
ListT m a = ListT { runListT : : m [ a ] }
ListT (StateT s m) is then a monad with the behavior of m, a global state and
multiple computation branch capabilities. Monad transformers provide a function
30
3.5 Monadic Tower and the CPS Hierarchy
to lift any computation in a monad m (m a) to a computation in the transformed
monad (t m a) where t is the transformer. For example, for StateT, the lifting
function is:
1
2
3
4
Monad
=>
lift :: (
m)
m a > StateT s m a
l i f t m = StateT $ \ s >
a < m
(a , s
do
return
Stacking transformers this way gives what is called a
transformer in the tower is called a
layer.
monadic tower.
Each
The lowest layer is the initial monad
and the uppermost layer is the transformer at the top of the stack.
The transformation to rst-class monadic signatures can be extended very
easily to monad transformers. The implementation can be found in appendix A.2
and practical examples of monadic towers made of rst-class monadic signatures
are all around the thesis and the implementation.
Let us consider an example of tower made of monadic classes and its transformed version using rst-class monadic signatures. Let us take Log as the lowest
layer, then add a monad transformer implementing a state monad and nally one
implementing exception handling:
1
exampleoftower
:: (
2
3
4
=>
5
6
7
MonadState
MonadTrans
MonadError
MonadTrans
Log m)
t ( t1 m) [
s < get
s
lift $
lift $
exampleoftower =
do
8
9
10
if null
then
else
Char ]
( t ( t1 m) ) ,
Char ]
( t1 m) ,
[
t,
[
t1 ,
Char ]
throwError "empty s t a t e "
l i f t $ write s
t and t1 are monad transformers, t1 implements a MonadError, t implements
a MonadState and m has to satisfy Log. The same example can be expressed with
out approach:
1
2
3
4
dexampleoftower : : SContT ( DState
( SContT ( DMonadError
( SCont
DLog
dexampleoftower =
s < dgetT
do
31
[
[
Char ] )
Char ] )
)) [
Char ]
3.5 Monadic Tower and the CPS Hierarchy
if null s
then l i f t $
else l i f t $
return "Ok"
5
6
7
8
dthrowErrorT "empty s t a t e "
l i f t $ dwrite s
We believe it to be much easier to read and in practice and because rst-class
monadic signatures use explicit instantiation, it also makes the life of the typing
inference algorithm better.
Remark 1
First-class monadic signatures being implemented via continuations.
Towers made of First-class monadic signatures are actually in the
chy [9, 18]
CPS hierar-
3.5.1 Lifting operations
To catch the error thrown in the previous example, we would naively write:
1
c a t c h E r r o r exampleoftower (\ _ >
return
"Not OK" )
Unfortunately it does not work because exampleoftower is not of type (t1 m) [Char]
but t (t1 m) [Char]. It is even clearer with dexampleoftower: dcatchError expects a
SContT (DMonadError
error) DOTS
but dexampleoftower is a SContT (DState [Char]) DOTS.
Fortunately there exists a technique to lift monadic operators.
Let us consider a monadic operation op over a monad m:
op :: 8 : m a ! m a
and a monad transformer t. We want to lift op to t m. op does not accept
arguments of type t m a but it does accept arguments of type m (t m a). The
return value is then also of type m (t m a). By lifting it we get t m (t m a). We
only have to join it to get a value of type t m a.
The lifting operation is then for op:
1
liftedOp t =
join
$ l i f t $ op (
return
t)
On a general manner, for a monadic operation op:
op :: 8 : F m ! m y
the lifted operation is
32
3.5 Monadic Tower and the CPS Hierarchy
1
liftedOp t =
join
$ l i f t $ op (F
return
t)
This technique has been used by Filinski [23] to implement its monadic layers.
The composition
join .
lift corresponds to glue function.
For catchError we get:
1
2
3
Monad
liftedCatchError : : (
( t m) , MonadTrans t , MonadError e m)
t m a > (e > t m a) > t m a
liftedCatchError t h =
$ l i f t $ catchError (
t) (
join
return
=>
return
. h)
4
5
6
7
8
9
10
11
12
13
14
Monad
=>
liftedDCatchError : : (
m)
SContT
theclass
( SContT ( DMonadError
) m) a
> (
>
SContT t h e c l a s s
( SContT ( DMonadError
>
SContT t h e c l a s s
( SContT ( DMonadError
) m) a
liftedDCatchError t h =
$ liftSContT $
dcatchErrorT (
error
error
error )
join
m) a )
error
return
t) (
return
. h)
what is exactly what we want. Unfortunately, even it the types are correct,
this transformation is not correct for every monad. It is not for the
Maybe,
Either error and ErrorT error m monads provides in the Monad Transformer Library.
Jaskelio presents in [27, 28] a general lifting way. We present in the next
section a way to get the monad right right using backtracking. It seems that
Jaskelio's approach is the categorical general counterpart of the backtracking
ad-hoc solution.
3.5.2 Well behaved Error Monad
The usual error monad is as follows
1
2
data Either
e r r r o r value =
|
Left error
Right v a l u e
and its implementation is
1
2
3
instance Monad ( Either error ) where
return v = Right v
m >>= f = case m of
33
3.5 Monadic Tower and the CPS Hierarchy
Left error
Right v a l u e
4
5
Left error
>
> f value
Let us introduce a very simple monad transformer, the identity transformer:
1
2
newtype I d e n t i t y T
deriving (Monad)
m a = IdentityT { runIdentityT : : m a }
3
4
5
6
7
8
l i f t I d e n t i t y T : : m a > IdentityT m a
l i f t I d e n t i t y T = IdentityT
instance
MonadTrans I d e n t i t y T
l i f t = liftIdentityT
where
Now let us try to lift catchError to IdentityT (Either error)
1
2
3
4
5
6
7
liftedCatchError t h
=
$ l i f t $ catchError (
=
$ l i f t $ catchError (
=
$ lift $
t
=
$ IdentityT (
t)
= IdentityT (
t ) >>= (\ x
= t
join
join
join
join
return t ) ( return . h )
Right t ) ( return . h )
Right
Right
Right
> x)
so whatever t is, liftedCatchError will never catch the exception. Let us assume
that t is an exception.
right returning
Right t.
Right t is not one,
so catchError (Right t) (return . h) is
The problem is the computation should backtrack when
t is reached so that h can be called. Because continuations are very well suited
for backtracking, we add a continuation layer to the monad: the well behaved
Error
error monad is thus dened as LContT (Either error).
throwError is straightforward:
1
2
error
throwError : :
> LContT (
throwError e = l i f t $
e
Left
Either error )
a
catchError m h runs m and if and exception is raised, backtracks on h. The
backtracking is done by running m and h with the current continuation where
catchError is called:
1
catchError : :
2
3
4
tryErrorT
Either error ) a
error
Either error )
Either error ) a
LContT (
> (
> LContT (
> LContT (
m h = LContT $ \k >
34
a)
3.6 Conclusion
5
6
7
case unLContT m k of
Left e > unLContT
Right v > Right v
(h e ) k
Now the operations of the exception handling layer can be lifted to any layer.
3.6 Conclusion
We have seen in this chapter how to implement monadic signatures via the CPS
Hierarchy and and how a continuation monad can transform a problematic monad
into a well behaved one. The
Haskell
code provided in appendices relies a lot on
rst-class monadic signatures. One of the goals of doing so was to prove that,
using it, even towers with lots of layers can still be readable and manageable.
Another positive aspect of the approach is eciency: using the continuation
monad to implement a given monad can be faster if there are few calls to the
operations of the monad.
35
Chapter 4
Nominal Theory
This chapter introduces nominal terms and problems. We present several syntaxes
for nominal terms. Each of them has its own benets. We show how theses
syntaxes are related and that they are all dierent facets of the same objects:
terms with names.
4.1 Graphs and Trees
Denition 1 A directed graph is a pair (Sn ; Se ) where Sn is a set of elements
called nodes and Se Sn Sn a set of edges. A edge e is a pair of nodes (o; d)
written o !e d. o is the origin and d the destination of the edge. e is said to be
an incoming edge for d and an outcoming edge for o.
An undirected graph is a graph whose edges (o; d) and (d; o) are identied.
Its edges have neither origin nor destinations but two extremities (without order).
A
root of a directed graph is a node without incoming edge.
A
leaf is a node
without outcoming edge.
Examples of graphs are given in section 5
Denition 2
A
path in the graph is a sequence
(n1 !e n2); (n2 !e n3); : : : ; (nl 1 !e nl )
such that the origin (resp. destination) of an edge is the destination (resp. origin)
of the previous (resp. following) one in the sequence. For example
(n1 !e n2); (n2 !e n3); (n3 !e n4)
36
4.2 Nominal Terms
is a path but
is not.
n1
(n1 !e n2); (n3 !e n4); (n2 !e n3)
is called the origin of the path and
nl
its destination.
Denition 3 A directed acyclic graph is a directed graph without cyclic path.
A tree is a directed acyclic graph with exactly one root and every other node
has exactly one incoming edge.
Size
Denition 4 (Size)
The
size
of an object
e is written j e j.
Denition 5 The size of a tuple (e1 ; : : : ; en ), a sequence e1 ; : : : ; en , a list [e1 ; : : : ; en ]
or a set S , when considered as mathematical objects, is dened as
j (e1; : : : ; en) j=j e1 j + + j en j
j e1; : : : ; en j=j e1 j + + j en j
j [e1; : : : ; en] j=j e1 j + + j en j
j S j= Px2S j x j
When considered as concrete objects (in the implementation), their size is the
size of their representation.
Denition 6
The size of a graph
d = (Sn ; Se ) is dened as
j d j=j Sn j + j Se j
4.2 Nominal Terms
In this section, we present several syntaxes of nominal terms. Let
be a denu-
function symbols f , g, . . . ; X a denumerable set of variables
X; Y; : : :; and A a denumerable set of atoms a; b; : : : We assume that , X , and
A are pairwise disjoint. , X , and A are dened for the whole thesis: unless
merable set of
stated otherwise, all atoms (resp. variables or functions) will be considered as
elements of
A (resp. X
or
).
37
4.2 Nominal Terms
Denition 7 (Size)
f is dened as
The size of an atom
a,
a variable
X
or a function symbol
j a j=j X j=j f j= 1
In the intended applications, variables will be used to denote meta-level variables (unknowns), and atoms will be used to represent object level variables.
swapping is a pair of (not necessarily distinct) atoms, written (a b). Permutations are lists of swappings, generated by the grammar:
A
::= Id j (a b) where
Id
is the
identity permutation.
We write
1
for the permutation ob-
tained by reversing the list of swappings in . We denote by 0 the permutation
containing all the swappings in followed by those in 0 . Set set of permutations
is written
.
Denition 8 (Size)
The size of a swapping
(a b) is dened as
j (a b) j= 2
Because permutations are sequences of swappings, their size is the size of sequences.
Denition 9 (Terms) In all the syntaxes presented here, nominal terms, denoted s; t; u; : : : , are trees made of atoms, variables, function symbols, abstractions [a]t, tuples and permutations. A(t) (resp V(t)) denotes the set of elements
of A (resp. X ) that occur in t. A term t is ground if V(t) = ;. For example,
[a]a is a ground term, and V([a](X; X )) = fX g.
and a variable X is called a suspended variable,
written X ; we say that is suspended on X . The application of a permutation
on a term t is denoted t.
A pair of a permutation
Denition 10 (Size)
The size of an abstraction
ned as
j [a]t j= 3+ j a j + j t j
38
[a]t or a suspension t is de-
4.2 Nominal Terms
j t j= 1+ j j + j t j
The denition of the size will depend on the syntax but will always be the size
of the graph representing them.
A
substitution over the set of terms T
is generated by the grammar:
::= Id j [X 7! t]
where
t
2 T . ST
denotes the set of substitutions over
permutation, written 0 , is dened as:
T.
The composition of
= 0
= [X 7! t]( 0)
Denition 11 (Size) The size of [X 7! t] is dened as
Id 0
([X 7! t]) 0
j [X 7! t] j= 3+ j X j + j t j
Because substitutions are sequences, their size is the size of sequences.
Substitutions can be applied to terms:
Denition 12
over
T
Let
T
be a term set.
is a function from
ST T
to
A
T
substitution application function
such that
8t 2 T
t Id
= t
8t 2 T ; ; 0 2ST t ( 0) = (t ) 0
where
t denotes the application of
will be written
t.
on
t.
When there is no ambiguity,
t = [X1 7! t1] : : : [Xn 7! tn]. is called a standard (resp.
n
compact, reduced, encoded, . . . ) substitution if (ti )i=1 are terms of the standard
Denition 13
Let
(resp. compact, reduced, encoded, . . . ) syntax.
Denition 14
For any function
f
on
T , f can be extended to ST
by
f ([X1 7! t1 ] : : : [Xn 7! tn ]) = [X1 7! f (t1 )] : : : [Xn 7! f (tn )]
39
4.2 Nominal Terms
Permutations will act top-down and accumulate on variables whereas substitutions act on variables.
nominal structure the triple (T ; ;
Denition 15
We call a
set of terms,
a function from
). T
denotes the
T to T representing the application of a
permutation on a term, written t. And
a substitution application function
over T .
Denition 16 A morphism from the nominal
is a function ' from T1 to T2 such that
structure
(T1; 1; 1) to (T2; 2; 2)
8t 2 T1; 2 '( 1 t) = 2 '(t)
and
8t 2 T1; 2ST1 '(t 1 ) = '(t) 2 '()
where '([X1 7! t1 ] : : : [Xn 7! tn ]) = [X1 7! '(t1 )] : : : [Xn 7! '(tn )]
4.2.1 Standard Nominal Term
Standard Nominal terms, or just standard terms for short, are trees built
from atoms, suspended variables, tuples, abstractions, and function applications. More precisely, standard terms are generated by the grammar:
s; t ::= a j X j (s1 ; : : : ; sn ) j [a]s j f t
We follow standard notational conventions, omitting brackets and abbreviating
Id
X
as
X
when there is no ambiguity. The set of standard nominal terms is
written Ts .
For example:
(a; X; f c), [a]f ((a b)X; Y; [c](c; d)), and [a](c d)(d e)X
standard nominal terms but [X ]a is not.
A() and V() are dened on standard terms inductively by:
A(a)
= fa g
A((a1 a2 ) : : : (an 1 an )X ) = fa1 ; : : : ; an g
A(f t)
= AS(nt)
A((t1 ; : : : ; tn ))
= i=1 A(ti)
A([a]t)
= fag [ A(t)
40
are
4.2 Nominal Terms
and
(a)
((a1 a2) : : : (an
(f t)
((t1; : : : ; tn))
([a]t)
V
V
V
V
V
1 an )X )
= ;
= fX g
= VS(nt)
= i=1 V(ti)
= V(t)
on standard term t is dened by induction:
Id s t = t and ((a b) ) s t = (a b) s ( s t),
Application of permutation
where
(a b) s a
(a b) s b
(a b) s c
(a b) s (X )
(a b) s (f t)
(a b) s [n]t
(a b) s (t1; : : : ; tn)
=
=
=
=
=
=
=
We dene the instantiation of a
duction:
where
b
a
c
c 62 fa; bg
((a b) )X
f (a b) s t
[(a b) s n](a b) s t
((a b) s t1; : : : ; (a b) s tn)
standard term t by a substitution if
by in-
t Id = t and t[X 7! s] = (t[X 7! s])
a[X 7! s]
([a]t)[X 7! s]
(X )[X 7! s]
(Y )[X 7! s]
(t1; : : : ; tn)[X 7! s]
(ft)[X 7! s]
Denition 17
=
=
=
=
=
=
a
[a](t[X 7! s])
s s
Y
(t1[X 7! s]; : : : ; tn[X 7! s])
f (t[X 7! s])
Ts with s and the above substitution application function forms
a nominal structure called the
standard nominal structure
4.2.2 Compact Syntax
We present here another way to dene nominal terms.
terms are terms generated by the following grammar:
s; t ::= a j X j f
Compact nominal
j (t1; t2) j [a]s j t
This grammar is called the compact syntax. Variables, atoms and constants
are called leaves.
t is called a suspended term.
41
Leaves, abstraction and pairs
4.2 Nominal Terms
are called
Tc.
non suspended terms.
The set of compact nominal terms is written
(a; X ), [a](e f )(f; ((a b)X; (Y; [c](c; d)))), and [a](c d)(d e)(X; c)
are compact nominal terms but f a and (a; b; X ) are not.
Denition 18 Let t be a compact nominal term then n (t) denotes the number
of nodes in t the number of subterms of the form u)
A() and V() are dened on compact terms inductively by:
A(a)
= fa g
A(X )
= ;
A(f )
= ;
A((a1 a2 ) : : : (an 1 an )t) = fa1 ; : : : ; an g [ A(t)
A((t1 ; t2 ))
= A(t1) [ A(t2)
A([a]t)
= fag [ A(t)
For example:
and
(a)
= ;
(f )
= ;
(X )
= fX g
((a1 a2) : : : (an 1 an)t) = V(X )
((t1; t2))
= V(t1) [ V(t2)
([a]t)
= V(t)
In the standard syntax, s t is not a term but denotes the application of on t as dened above but in the compact syntax t is the compact term made
V
V
V
V
V
V
of the pair of the permutation
a permutation
c t = t.
and the compact term t. The application of
on a term t, written
c t is simply the syntactic operation
Substitution application is dened inductively by:
t
c
Id
= t and t c [X 7! s] = (t c [X 7! s])
where
a
(t1; t2)
([a]t)
f
(t)
X
c
c
c
c
c
c
[X 7! s]
[X 7! s]
[X 7! s]
[X 7! s]
[X 7! s]
[X 7! s]
c
= a
= (t1 c [X 7! s]; t2 c [X 7! s])
= [a](t c [X 7! s])
= f
= c (t c [X 7! s])
= s
42
4.2 Nominal Terms
Tc
Denition 19
with permutation application and substitution application as
above forms a nominal structure called the
compact nominal structure.
4.2.3 Reduced Compact Syntax
Let
RedudeCompactT erms be the following reduction system on compact nom-
inal terms:
t
a
f
(t1 ; t2 )
[a]t
(0 t)
Proposition 1
Proof
Let
t
)
)
)
)
)
)
Idt (applied only once, before any other rule!)
(a)
f
(t1; t2)
[(a)](t)
( 0)t
This system always terminates and is conuent
be a compact term. Let
subterm position in the tree of
w
be the function associating to each
t a natural integer. w is dened by the following
equations:
w(a)
= w (f ) = w (X ) = 0
w((u1 ; u2 )) = 1 + w(u1 ) + w(u2 )
w([a]u)
= 1 + w(u)
w(u)
= w(u)
P
Let us consider the measure m(t) = ( u2P w(u); j P j) where P is the set of
subterms of t of the form :. with the usual lexical ordering on pairs of integers.
All the rules make the measure to decrease:
w(a) = w((a)) = 0 and the number of suspended subterm decreases
w(f ) = w(f ) = 0 and the number of suspended subterm decreases
w((t1 ; t2)) = 1 + w((t1 ; t2 ))
w([a]t) = 1 + w([(a)](t))
So the system terminates.
43
4.2 Nominal Terms
The system is also locally conuent. The critical pairs are joinable:
(0 a)
(0 f )
(0 (t1 ; t2 ))
(0 [a]t)
(0 (00 t))
=)
=)
=)
=)
=)
( 0)(a)
f
(( 0)t1; ( 0)t2))
[(( 0)(a)](( 0)t)
( 0 00)t
So the system is conuent.
Normal forms are terms generated by the grammar
s; t ::= a j X j f
reduced compact terms.
and called
written Tr .
For example:
(a; IdX ), [a](a; b)
(a b)a, [e](c d)(X; a) is not.
j (r1; r2) j [a]s
The set of reduced compact terms is
are reduced compact nominal terms but
When reduction rules are only applied on the head of the term, head normal
forms are called
head reduced compact nominal terms
the grammar:
where t,
t1
and
s ::= a j X j f
t2
and correspond to
j (t1; t2) j [a]t
are not head reduced compact terms but ordinary nominal
compact terms. The set of head reduced compact nominal terms is written Th .
For example: a a b a; X and f; a b a are nominal terms but a b f; X
[ ]( )(
is not.
th ).
)
( ( ) )
( )(
Let t be a term, its normal form (resp. head normal form) is written
)
tr (resp.
Reduced terms and head reduced terms being compact terms, the denitions
of A
() and V() for compact term hold for reduced and head reduced terms.
Remark 2
Reduced compact nominal terms are standard nominal terms
Denition 20
The application of a permutation on reduced compact nominal
terms is dened as
=t
c
r
.
r t = tr .
t r
reduced nominal
Substitution application is dened as
(Tr ; r ; r ) forms a nominal structure called the
44
4.2 Nominal Terms
structure. '(t) = tr
is a morphism from the compact nominal structure to the
reduced nominal structure.
t be a compact nominal structure and a permutation:
'( c t) = c tr = tr = r '(t).
Let t; s 2 Tc ; X 2 X :
Proof
Let
'(t
r
[X 7! s]) = t r [X 7! s]rr
= t c [X 7! s]r r
= tr c [X 7! s]r r
= '(t)'([X 7! s])
= '(t) r '([X 7! s])
4.2.4 From Standard Terms To Compact Terms
t be a standard nominal term. t can be encoded as a compact term with 3
new function symbols O, C and F not in : O means Open Tuple, C Close
Tuple and F Function.
Let
The encoding is dened by:
(a)
(X )
([a]u)
(f u)
((t1 ; : : : ; tn ))
=
=
=
=
=
a
X
[a](u)
(F; (f; (u)))
(O; ((t1); (: : : ; ((tn); C ))))
(O; (t1; (: : : ; (tn; C )))) is the usual encoding of list by nested pairs. For
example (()) = (O; C ), ((t)) = (O; (t; C )), ((t1 ; t2 )) = (O; (t1 ; (t2 ; C ))) and so
where
on.
are called compact encoded standard nominal
encoded terms for short. They are a particular case of reduced
Terms in the image of
terms,
or
terms (and so of compact terms) generated by the following grammar called the
compact encoded standard nominal syntax, or encoded syntax for short:
s; t ::= a j X j [a]s j (F; (f; t)) j (O; (t1 ; (: : : ; (tn ; C ))))
45
4.2 Nominal Terms
62 fF; O; C g. The set of encoded terms is written Te.
For example ((f X; a; Y )) = (O; ((F; (f; (IdX ))); (a; (IdY; C ))))
Encoded terms being compact terms, the denition of A() and V() for compact
where
f
term hold for encoded terms.
The inverse encoding is dened on encoded terms by:
! (a )
!(X )
!([a]t)
!((F; (f; t))
!((O; (t1 ; (: : : ; (tn ; C )))))
Proposition 2
= a
= X
= [a]!(t)
= f !(t)
= (!(t1); : : : ; !(tn))
8t 2 Ts !((t)) = t
8t 2 Te (!(t)) = t
Proof
!((a))
!((X ))
=
=
=
!(([a]t))
=
=
!((f t))
=
=
=
!(((t1 ; : : : ; tn ))) =
=
! (a ) = a
!(X )
X
!([a](t))
[a]!((t))
!((F; (f; (t))))
!((F; (f; (t))))
f !((t))
!((O; ((t1 ); (: : : ; ((tn ); C )))))
(!((t1)); : : : ; !((tn)))
(!(a))
(!(X ))
=
=
=
(!([a]t))
=
=
(!((F; (f; t))))
=
=
=
(!((O; (t1 ; (: : : ; (tn ; C )))))) =
=
46
(a) = a
(X )
X
([a]!(t))
[a]!((t))
(f !(t))
(F; (f; (!(t))))
f !((t))
!((O; ((t1 ); (: : : ; ((tn ); C )))))
(!((t1)); : : : ; !((tn)))
4.2 Nominal Terms
Proposition 3 is a morphism from the standard nominal structure to the reduced nominal structure.
t be a standard nominal term, a permutation:
( s t) = r (t)
Proof
Indeed:
Let
(a)
= (a) r
= (a)
0
(( X ))
= (( 0)X )
= ( 0)Xr
= (0X ) r
= ((0X ))
([a]t)
= ([(a)]t)
= [(a)](t)r
= [(a)](t)r
= [(a)]r(t)
= [a](t)r
= ([a]t)
= ([a]t)
((f t)))
= (F; (f; (t)))r
= (F; (f; (t) ))r
= (F; (f;r(t)))
= (f t)
((t1 ; : : : ; tn ))) = (O; ((t1 ); (: : : ; ((tn ); C ))))
r
= (O; ((t1); (: : : ; ((tn); C ))))
= (O; ((t1); (: : :r; ((tn); C ))))r
= ((t1; : : : ; tn))
Let be a substitution over Ts :
(t) = (t) r () by induction on the structure of t and .
Proposition 4 ! is a morphism from the reduced nominal structure to the standard nominal structure.
Proof
The proof is the same as for .
Proposition 5 =!
and (Te ; r ; r )
is an isomorphism between the standard nominal structure
47
4.2 Nominal Terms
4.2.5 From Nominal to First-Order
Nominal terms (either standard or compact) not containing atoms (A
(t) = ;) are
actually rst-order terms. In the rest of this section we use compact terms but
the denition and properties can easily be transposed any of the above syntax.
Denition 21
The set of rst-order terms, written
Tf , is dened as
ft j t 2 Tc ^ A(t) = ;g
.
When applying a permutation on a standard or reduced nominal term, permutations accumulate on variables but in rst-order syntax, variables represent
rst-order terms so
rst-order terms as
Proposition 6
X
= X.
Thus we dene the permutation application on
8t 2 Tf ; 2 f t = t
Tf with f as permutation application function and the standard
substitution application function forms a nominal structure called the
structure.
We present here a morphism
F
rst-order
from compact terms to rst-order terms. Let
, X and A. F associate to t a rst-order
term with two new function symbols and [] of arity respectively 0 and 1. will
represent atoms and [] abstraction. F forgets the nominal details of t:
F (a)
= F (X )
= X
F (f )
= f
F (t)
= F (t)
F ((t1 ; t2 )) = (F (t1 ); F (t2 ))
F ([a]t)
= [] F (t)
For example: F (([a](a; ((a b)X; Y )); f )) = ([](; (X; Y )); f )
t
be a compact nominal term over
Proposition 7 F
is a morphism from the compact nominal structure to the rst-
order structure.
48
4.3 Nominal Problems
Proof
F (t).
Let t be a compact term and
a permutation, F ( c t) = F (t) = f
We check inductively on the structure of
F (s)]
t
7! s]) = F (t)[X 7!
j t j max2t j j (j F (t) j
that
F (t[X
Proposition 8 Let t be a compact nominal term:
+n (t)) where 2 t ranges over the permutations appearing in t.
Proof
Because the only dierence in size between rst-order terms and nom-
inal terms are permutation sizes.
4.3 Nominal Problems
# species a freshness relation between atoms and terms, and
denotes alpha-equivalence. Constraints have the form a#t or s t.
The predicate
Denition 22 (Size)
The size of constraints is dened as follows:
j a#t j= 3+ j a j + j t j
j s t j= 3+ j s j + j t j
A set P r of constraints is called an alpha-equivalence problem. Intuitively,
a#t means that if a occurs in t then it must do so under an abstractor [a]-. For
example, a#b, and a#[a]a but not a#a. We sometimes write a; b#s instead of
a#s; b#s, or write A#s, where A is a set of atoms, to mean that all atoms in A are
fresh for s. In the absence of variables, a#s and s t are structural properties
(to check a#s we just check that every a in s occurs under an abstractor), but
in the presence of variables both predicates may depend on assumptions a#X
about what will get substituted for the variables. A set of assumptions A # X
is called a
freshness context
and written
inductively,
by a system of axioms and rules, using # in the denition of (see below).
We write ds(; 0 )#X as an abbreviation for fn#X j n 2 ds(; 0 )g, where
ds(; 0 ) = fn j n 6= 0 ng is the set of atoms where and 0 dier (i.e., their
Formally, we dene
dierence set). Below
# and .
under the freshness context
a; b are any pair of distinct atoms.
49
4.3 Nominal Problems
With the standard syntax:
` a#s1 ` a#sn
` a #s
(#tup)
(#
)
` a #b
` a#(s1; : : : ; sn)
` a#fs f
1
(#absa) ` a#s (#absb) ` a#X (#X )
` a#[a]s
` a#[b]s
` a#X
a#X2
(# )
` a #X 0
(a) ` ds(; )#X (X )
` a a
` X 0X
` s1 t1 ` sn tn
` s t
(tup)
( )
` (s1; : : : ; sn) (t1; : : : ; tn)
` fs ft f
` s t
` s (a b)t a#t
(absa)
(absb)
` [a]s [a]t
` [a]s [b]t
(#ab)
-
With the compact syntax:
` a#s1 ` a#s2
(#pair )
` a #b
` a #f
` a#(s1; s2)
1
(#absa) ` a#s (#absb) ` a#t (# )
` a#[a]s
` a#[b]s
` a#t
a#X2
(# )
` a #X ( )
( )
( )
` a a a ` f f f
` X X X
` s1 t1 ` s2 t2
`
ds(; 0 )#t
( )
( )
` (s1; s2) (t1; t2) tup
` t 0t ` s t
` s (a b)t a#t
(absa)
(absb)
` [a]s [a]t
` [a]s [b]t
We remark that is indeed an equivalence relation (see [51] for more details).
We write a # t and t s) when there is no ambiguity on (usually when is
empty or when ).
(#ab)
(#f )
-
50
4.3 Nominal Problems
Below we dene reduction relations (also called rewriting relations) on standard terms or problems. If
=) is a reduction relation, we will denote by =)
its
transitive and reexive closure. Irreducible terms are called
normal forms.
The following set of simplication rules from [51], acting on problems in the
standard syntax, where
check
a; b
denote any pair of distinct atoms, can be used to
the validity of -equality constraints.
a#b; P r
a#fs; P r
a#(s1 ; : : : ; sn ); P r
a#[b]s; P r
a#[a]s; P r
a#X; P r
=) P r
=) a#s; P r
=) a#s1; : : : ; a#sn; P r
=) a#s; P r
=) P r
=) 1a#X; P r
6=
a a; P r =) P r
(l1; : : : ; ln) (s1; : : : ; sn); P r =) l1 s1; : : : ; ln sn; P r
fl fs; P r =) l s; P r
[a]l [a]s; P r =) l s; P r
[a]l [b]s; P r =) l (a b)s; a#s; P r
X 0 X; P r =) ds(; 0 )#X; P r
-
Id
This reduction relation can be adapted to problems in the compact syntax by
adding the rules
a#t; P r
=)
1 a#t; P r 6= Id
-
=) th u P r
=) t uh P r
Denition 23 Let P r be a problem and a freshness context. P r is said valid
in the context , written ` P r if and only if for any constraint a # t and
s u in P r, ` a # t and ` s u. Otherwise it is not valid.
t u; P r
t u; P r
P r0
The above rules generate a reduction relation on problems:
is obtained from
Pr
Pr
=) P r0 if
by applying a simplication rule. To check constraints
we proceed as follows: Given a problem
P r,
we apply the rules until we get an
of constraints of the form
a#X are left, then the original problem is valid in the context (i.e., ` P r).
irreducible problem, i.e., a normal form. If only a set
51
4.3 Nominal Problems
Denition 24
Let
be a freshness context and problem dened as
a substitution.
is the
= fa # X j a # X 2 g
.
Denition 25 A nominal -equivalence problem, or -equivalence problem for short, is, given a problem P r, to know whether there exists a freshness
context such that ` P r . Such a is called the solution of the nominal
-equivalence problem.
[a]X [b]X reduces to the set of constraints
a#X; b#X ; therefore a#X; b#X ` [a]X [b]X , as mentioned in the introduction. A problem such as X a is not valid since it is irreducible; however, in
For example, the problem
this case X can be made equal to a by instantiation (i.e., applying a substitution)
and we say that this constraint can be
solved.
nominal unication problem, or unication problem for
short, is, given a problem P r to know whether it exists a freshness context and
a substitution such that for any constraint a # t and s u in P r , ` a # t
and ` s u .(; ) is called a solution of the nominal unication problem.
Denition 26
A
A most general solution to a nominal unication problem P r is a pair
(; )
of a freshness context and a substitution, obtained from the simplication rules
above enriched with two
instantiating rule labelled with substitutions:
X 7!-1 u
(X 62 V(()u))
(X 62 V(()u))
If we impose the restriction that in a constraint s t the variables in t
X u; P r
u X; P r
=)
X 7! u
=)
-1
P r[X 7! 1 u]
P r[X 7! 1 u]
-
cannot be instantiated and the variables in left-hand sides are disjoint from the
variables in right-hand sides, then we obtain a nominal
we also require
obtain a
s
matching
problem. If
to be linear (i.e., each variable occurs at most once in s), we
linear nominal matching problem.
52
4.3 Nominal Problems
Denition 27
A
short, is, given a
of
nominal matching problem, or matching problem for
problem P r where variables appearing on the left-hand side
are distinct from variables appearing on the right-hand side of :
fX j X 2 V(si) si ti 2 P rg \ fY j Y 2 V(ti) si ti 2 P rg = ;
and a substitution such that for
any constraint a # t and s u in P r , ` a # t and ` s u .(; ) is
to know whether it exists a freshness context
called a solution of the nominal matching problem.
A most general
solution to a nominal matching problem P r is a pair (; )
of a freshness context and a substitution, obtained from the simplication rules
above enriched with an
instantiating rule labelled with substitutions:
X u; P r
X 7!-1 u
=)
P r[X 7! 1 u]
-
Note that there is no need to do an occur-check because left-hand side variables
are distinct from right-hand side variables in a matching problem.
As shown in [51], these rules dene sound and complete nominal unication
and nominal matching algorithm, where the most general solution to the input
problem is obtained by computing its normal form
and by composing the
substitutions used in the instantiation rules. We give an example and refer to [51]
for more details.
Example 1
Since there is a reduction sequence:
[a]X [b]b =)
the most general solution of
X a; a#b
X 7!a
=) a#b =) ;
[a]X [b]b is (;; [X 7! a]).
Although in the case of rst order terms we can, without loss of generality,
restrict ourselves to matching problems with ground right-hand sides (it is sufcient to consider all variables as constants), this is not the case for nominal
problems: when terms contain variables we may need to compute dierence sets
of permutations.
53
4.3 Nominal Problems
4.3.1 Morphisms
Denition 28 A nominal -equivalence structure is a 5-tuple (T ; ; ; ; #) where (T ; ; ) is a nominal structure, an equivalence relation on terms
and
# a relation between atoms and terms such that:
` a # s ^ ` s t
) `a#t
8 2 ` s t
) ` s t
8 2 ` a # t
) ` (a) # t
8 2ST ` s t ^ 0 ` ) 0 ` s t
8 2ST ` a # t ^ 0 ` ) 0 ` a # t Proposition 9
All the nominal structures dened in the previous section, with
-equivalence and freshness relations, are nominal -equivalence
We write the -equivalence and freshness relation on:
their corresponding
structures.
s and #s
compact terms c and #c
rst-order terms f and #f
standard terms
Proof
All equations are properties of the -equivalence and freshness relations
on standard terms. Please refer to [51] for more details. Proofs on standard terms
can be easily adapted to compact terms. First-order syntax, ignoring all naming
details, all the above equations are straightforward.
Denition 29 A morphism ' from the nominal -equivalence structure (T1 ; 1
; 1 ; 1 ; #1 ) to (T2 ; 2 ; 2 ; 2 ; #2 ) is a morphism from (T1 ; 1 ; 1 ) to (T2 ; 2 ; 2 )
such that ` a #1 t ) ` a #2 '(t) and ` s 1 t ) ` '(s) 2 '(t)
Proposition 10
All the morphisms between nominal structures dened in the
previous section are morphism between nominal
-equivalence structures.
Let 'c7!r be the morphism from compact terms to reduced terms. We
need to prove that
` a c t ) ` a c 'c7!r t and ` s c t ) `
Proof
#
#
54
()
4.3 Nominal Problems
'c7!r (s) c 'c7!r (t).
-equivalence:
so
t tr .
Let 's7!e
All the rules of the system RedudeCompactT erms preserve
t
Idt
a
(a )
f
f
(t1 ; t2) (t1 ; t2 )
[a]t [(a)](t)
(0 t) ( 0 )t
the morphism from standard terms to encoded terms. Let
t1
and
t1 #s t2 , t1 #c t2 and a #s t1 , a #s t1 . Indeed,
on encoded terms, the -equivalence and freshness axioms in the standard (resp.
t2
two encoded terms,
compact) syntax can all be proved in the compact (resp. standard) syntax.
Let
F
the morphism from one of the nominal syntax to its corresponding
`a#t)
` a # F (t) and ` s t ) ` F (s) F (t). ` a # t ) ` a # F (t)
rst-order structure. We need to prove that for any term
t
that
is trivial because an atom is always fresh in a term without atoms. Note that in
rst-order syntax a variable
a # X is always true.
of s and t:
X
can only be substituted by a rst-order term, so
The latter equation is proved by induction on the structure
If s is an atom a , t as to be the same atom a and F (a) is the constant .
If s is a constant or a variable, then F (s) = s to the equation is trivial.
If s is a pair (s1; s2) then t has to be a pair (t1; t2 with s1 t1 and
s2 t2 , by induction hypothesis F (s1 ) F (t2 ) and F (s2 ) F (t2 ) and
F (s) = (F (s1 ); F (s2 )) so F (s) = F (t).
If s is an abstraction [a]s0 then t has to be an abstraction [b]t0 and s t
implies s0 (a b)t0 a # [b]t0 by induction hypothesis F (s0 ) F (t0 ) and
of course a # F (t), F (t0 ) = F ((a b)t0 ).
Denition 30 Let ' a morphism from the nominal -equivalence structure (T1 ; 1
; 1 ; 1 ; #1 ) to (T2 ; 2 ; 2 ; 2 ; #2 ). It can be extended to:
55
4.3 Nominal Problems
freshness constraints by '(a #1 t) = a #2 '(t)
-equivalence constraints by '(s 1 t) = '(s) 2 '(t)
a problem P r by applying it to all of the constraints in P r.
Proposition 11 Let ' be a morphism from the nominal -equivalence structures
(T1; 1; 1; 1; #1) to (T2; 2; 2; 2; #2) and P r a problem whose terms are in
T1. Then
if is a solution of the -equivalence problem P r, then is solution of the
-equivalence problem '(P r).
if (; ) is a solution of the unication problem P r, then (; '()) is solution of the unication problem '(P r ).
if (; ) is a solution of the matching problem P r, then (; '()) is solution
of the matching problem '(P r ).
be a solution of the -equivalence problem P r. For any constraint a # t and s u in P r, ` a # t and ` s u. ' is a morphism so ` a #1 t ) a #2 '(t) and ` s 1 t ) '(s) 2 '(t).
` a #2 '(t) = '( ` a #1 t) and ` '(s) 2 '(t) = '( ` s 1 t)
Which means is solution of '(P r)
Let (; ) be a solution of the unication problem P r. For any constraint
a #1 t and s 1 u in P r, ` a #1 t and ` s 1 u. '( ` a #1 t) =
` a #2 '(t)'() and '( ` s 1 t) = ` '(s)'() 2 '(t)'(). So
(; '()) is solution of the unication problem '(P r)
Let (; ) be a solution of the matching problem P r. For any constraint
a #1 t and s 1 u in P r, ` a #1 t and ` s 1 u. '( ` a #1 t) =
` a #2 '(t)'() and '( ` s 1 t) = ` '(s) 2 '(t)'(). So (; '())
is solution of the matching problem '(P r)
Proof
Let
This result is interesting because it relates solutions of problems in dierent
syntaxes. For example it says that if the rst-order problem corresponding to
a nominal problem does not have any solution, then neither have the nominal
problem. Because rst-order unication and matching problems can be solved
56
4.4 Complexity and Implementation of Permutations and Sets
in linear time and space, it provides a fast way to decide if some problems are
solvable. It also says that solutions of nominal problem and rst-order problems
have the same structure, which means the same function symbol, abstractions,
variables, tuples at the same position. From an implementation point of view, it
says that an algorithm for the compact syntax can be used to solve a problem
in the standard syntax because
and
!
provide an isomorphism between the
encoded and standard structures. Note that the complexity of the translation
has to be taken in account in the complexity of the algorithm.
So for the rest of the thesis, we will use the standard or compact syntax
depending on which one is the more adapted to the situation.
Denition 31
-equivalence, unication or matching
and space into a single constraint s t
In the compact syntax, any
problem can be encoded in linear time
such that the set of solutions is preserved.
Proof
Indeed the following equalities form a rewriting system whose normal
fs tg.
s1 t1 s2 t2 , (s1 ; s2 ) (t1 ; t2 )
fa1; : : : ; ang # t , (a1 a2)(a2 a3) : : : (an 1 an)(an c)[c]t [c]t
forms are problems of the form
where
c is a fresh atom (not already present in the problem).
The system termi-
and # present in the problem because
one or one # is consumed every time but none is created.
nates in linear time in the number of
4.4 Complexity and Implementation of Permutations and Sets
The complexity of the algorithms presented in this thesis depends a lot on the
implementations of permutation and sets of atoms. Because we will have to use
dierent implementations of permutations and sets of atoms even for the same
algorithm, we need to abstract their actual implementation to make the algorithm
to be able to use several implementations.
57
4.4 Complexity and Implementation of Permutations and Sets
4.4.1 Implementation
This section describes the implementation and abstraction of permutations and
sets. It has been implemented in the
in the
Objective Caml
Haskell
module A.6. The implementation
B.1 does not need all the mechanisms presented here, it
only needs the array implementation.
The operations on permutations and sets of atoms used in the algorithms will
be :
a 2 A:
membership test
A [ f a g:
add an atom to a set
A n fag:
remove an atom from a set
A [ A0 :
compute the union of two sets
A \ A0 :
compute the intersection of two sets
a:
compute the image of an atom by a permutation
1:
compute the inverse of a permutation
0 : compose two permutations
supp():
compute the support of a permutation
(A) = f(a) j a 2 Ag:
compute the image of a set by a permutation.
Operations in this list are called the
basic operations on permutations and
sets of atoms.
Denition 32
We call
updating
the action of adding/removing an atom to a
set or to compose a permutation by another one.
Denition 33 (Implementation as arrays) A and can be implemented respectively as mutable arrays arrA and arr indexed by atoms. Let a be an atom,
arrA is dened as arrA [a] = (a 2? A) and arr [a] = (a).
58
4.4 Complexity and Implementation of Permutations and Sets
Mutable arrays have constant access time but computing Afag requires either
to modify the array in place by
that
arrA [a] := > or to create another array arr0 such
arr0 [b] =
(
=
>
if b a
otherwise
arrA [b]
Modifying arrA in place to get arr0 takes a constant time but modies all the
other occurrences of A which become A0 . To keep A, we need to create a new
array arr0 but creating the array takes is linear in time and space in the size of
arrA .
Denition 34 The size of an array arr is dened as j arr j= l n where l is
the length of arr (the size of its domain) and n the size taken, in the array, by
an element. If the elements are pointers to other structures, the size an element
is the size of the pointer and not the size of the structure pointed.
Another implementation is using persistent
(resp. maps) are
size balanced binary trees
sets and maps.
Persistent sets
whose nodes are elements in the sets
(resp. a pair of a key and a value). They correspond to the
Haskell
module
Data.Set (resp. Data.Map) and are described in [8, 39].
Persistent maps represent partial functions. Their domain of denition is a
pm be a persistent map. For a
key k in the domain of denition of pm, the value associated to k is pm[k ].
set of keys for which they associate a value. Let
Denition 35 (Size)
Because such data are trees, their size is the size on graphs.
Denition 36 (Implementation as persistent data) A can be implemented
directly as the corresponding persistent set psA can be implemented as the nite
map pm whose domain of denition is the support of and
8a 2 supp()
pm [a] = (a)
Membership tests and value look up take logarithmic time. Computing A nfag
(resp.
(a b)) returns a new set (resp.
map) and takes logarithmic time too.
The original set (resp. map) remains unmodied. Thus using persistent data,
sets permutations can be passed away without risks of side eects, so no need to
copy them.
59
4.4 Complexity and Implementation of Permutations and Sets
4.4.2 Optimisation
The type of sets is actually
1
data
Set s e t
2
=
|
SetEmpty
SetNative set
where set represents an abstract implementation of set like arrays, persistent sets,
. . . Adding the value SetEmpty enable optimisations:
there is no need to create a whole array, which takes a linear time and space
in the size of
A0 , for the empty set
A [ ; or A \ ; can be computed in constant time
The type of permutation is:
1
2
3
4
data
Perm s e t perm =
PermId
| PermSwap
Atm
| PermNative perm
Atm
perm
( Set s e t )
where set is as before and perm represents an abstract implementation of permu-
id permutation and
PermSwap a b the swapping (a b). PermNative p i s represents any permutation where p is the native implementation of , i the native implementation of 1
tations like arrays or persistent data. PermId represents the
and s its support. It also enable lots of optimisations:
the identity permutation or a swapping do not depend on the native implementation
composing by PermId is done in constant time
in the case of arrays, composing in place by PermSwap a b can be done in
constant time whereas it would take a linear time in the size of A0 otherwise
getting the inverse of a permutation and its support is done in constant
time because it is already there.
60
4.4 Complexity and Implementation of Permutations and Sets
When permutations are composed, we only need to update the inverse and
the support and not to recompute them. For example, let 0 be a permutation
( (a b)) 1 = (a b) 1
( (a b)) is computed by adding or removing a and b whether ( (a))a =
a and ( (a))a = a.
and supp
4.4.3 Abstraction of sets and permutations
To abstract the native implementation of sets we add another layer in the monadic
tower for set handling. This layer is implemented by the rst-class monadic signature SetCall set where set represents the native type of sets. SetCall set provides
the following calls:
SetIsIn checks if an atom is in a set
SetIsSubset checks if the rst set is subset of the second
SetSize returns the size of a set
SetSet add or remove an atom from a set, of the rst argument is
> the
modication can be done in place otherwise a new set has be returned
SetEmptyNative returns a native empty set
SetToList returns the list of the elements of a set
SetUnion/ SetInter compute the union/intersection of two sets. The rst ar-
gument tells if the modication can be done in place and if yes, on which
set.
The native implementation of permutation is also abstracted. A new layer is
added to the monadic tower for permutation handling. It is implemented by the
rst-class monadic signature data type PermCall perm where perm represent the
native type of permutations. The following calls are provided:
PermImage returns the image of an atom by a permutation
61
4.4 Complexity and Implementation of Permutations and Sets
PermSwapRight composes a permutation by a swapping on the right side
PermCompose composes two permutations. The rst argument tells if the
composition can be done in place and if yes on which permutation.
PermSupp computes the support of a permutation
PermIdNative the native identity permutation
PermUnshare makes a fresh copy of a permutation
4.4.4 Complexity
Because it will happen that the same algorithm uses dierent implementations
of permutations and sets of atoms we need to have a way to abstract the actual
implementation also in complexity proofs.
Denition 37
we dene
For a given implementation of permutations and sets of atoms
CA as an upper bound of the time complexity of the basic operations on
permutations and sets of atoms ( cf. section 4.4.1).
A. Instead we
take a big enough set such as the set A0 of atoms in the problem. CA , in every
In practice it is impossible to handle the innite set of atoms
implementation considered, depends on the number of atoms (i.e. the size of
written
j A0 j).
AO ,
Denition 38 CA for the implementation of permutations and sets of atoms as
arrays is j A0 j where A0 is the set of index of the arrays.
Proof
Every basic operation time complexity is bounded by CA
Denition 39 CA for the implementation of permutations and sets of atoms as
persistent data is log (j A0 j) j A0 j where A0 is the set of atoms used in the
implementation.
Every basic operation time complexity is bounded by CA
The interpretation of SetCall and PermCall as persistent data is given in the
Proof
appendix A.6.
62
Part II
Unication
63
An implementation of the unication algorithm using trees to represent terms,
is exponential. For instance, the problem:
X1
Y1
:::
Xn 1
Yn 1
Xn
Yn
X1
(X2; X2)
(Y2; Y2)
(Xn; Xn)
(Yn; Yn)
a
a
Y1
which does not involve abstraction, requires a number of reduction steps that is
exponential in the size of the problem because the solution size, without subterm
sharing, is exponential in the size of the problem.
For rst-order unication problems, there are linear-time unication algorithms that represent terms as directed acyclic graphs. However, it is well-known
that these algorithms cannot be generalised to higher-order problems (i.e. unication of -terms modulo
and
);
higher-order unication is undecidable. In
the case of unication modulo , no linear algorithm is known, but we will show
that using a representation of problems as directed acyclic graphs we can obtain
a quadratic algorithm which is the most ecient implementation so far.
We will start by presenting in chapter 5 a representation of nominal terms and
problems as directed acyclic graphs. Then we will present
3 nominal unication
algorithms in chapters 6, 7 and 8. The rst one is the most ecient (it is quadratic
in time and space) but not the easiest to to maintain and extend. The second
one is a almost as ecient as the rst one and easier to use in practice. The last
one presents a polynomial algorithm based on graph rewriting.
64
Chapter 5
Nominal Unication Terms and
Problems as Directed Acyclic
Graphs
This chapter presents an encoding of compact nominal terms as directed acyclic
graphs. We use compact terms because we will deal with permutations in a lazy
way: permutations will suspend always, unless performing the permutation is
necessary to trigger a simplication rule in a problem.
We will dene a recursive function to transform terms, and more generally
problems, into graphs. Nodes are represented by circles, annotated by a con-
[]
structor. The term-constructors are: abstraction , pairs
mutations , permutation application , variables
(), constants f , per-
X , atoms a and sets of atoms
A. In addition we use two constructors for freshness and -equality: #, ,
and a node P r which is the root of the problem. The constructors []; #; ; ; ()
are binary, f is unary, X; ; a are 0-ary, P r has variable arity, equal to the the
number of constraints in the problem, respectively.
We will often overload the notation, by using a node annotated with a term
t, as in the following diagram:
t
to denote the root node of the subgraph representing t. In the same way, we
65
sometimes annotate a node with a constraint Pi to denote the root of the subgraph
representing the constraint.
Denition 40
The graph representation of a term, and more generally a prob-
lem, is inductively dened by:
c where c is an atom a, a constant f or a variable X :
c
(t1; t2):
()
t1
t2
[a]t:
[]
a
t
t:
t
In particular, the graph representing
X.
X
A # t, where A is a set of atoms:
#
A
66
t
has two leaves labelled by
and
t u:
u
t
P r = P1; : : : ; Pn, where each Pi is a constraint:
Pr
P1
...
Pn
In this case, we x an arbitrary ordering on the constraints
P1 ; : : : ; Pn ,
to
obtain a unique representation.
After transforming a problem into a graph as above, we identify the leaves that
are annotated with the same atom, constant or variable but not leaves that are
annotated by the same permutation.
Having a root node
Pr
for a problem is not essential, but it will be useful
when we dene garbage collection on graphs.
The graph representation of a nominal problem, as dened above, is a
nal graph (see Denition 41 below).
nomi-
Nominal graphs are directed acyclic graphs,
they can be seen as termgraphs [43], that is, trees where subterms may be shared.
We will also include ? nodes to represent unsolvable constraints, and nodes
to represent solutions (substitutions).
Remark 3
Denition 40 species a function that transforms a nominal problem
into a graph. This function is linear in the size of the problem.
Denition 41 (Nominal graph)
with constructors (i.e., term constructors, and
a set
arity
N of nodes labelled
, P r), together with
A nominal graph is a set
#, , ?,
E N N of edges, such that if n is a node decorated with a symbol
m, then there are m outcoming edges for n (i.e., it has m children).
67
of
If
e = (n1 ; n2 ) 2 E , written n1 ! n2 , we say that e is an incoming edge for n2 , and
outcoming for n1 .
The edges connected to each node are ordered, and we will call the points of
attachment
ports.
A root is a node without incoming edges.
Dierent nodes may have the same decoration, however, leaves with the same
label are identied (they are the same node) except for permutations.
Example 2
The nominal graph representing the term
[a](a b)X is:
[]
a
(a b)
X
and the nominal graph representing the problem
[a]a [b]b; (a b)X X is:
Pr
[]
[]
a
b
(a b)
X
Note that we have xed an arbitrary order for the constraints above. The
root of a graph representing a problem is labelled by
roots labelled by
P r,
and its children have
or #. We will say that the root of the i-th constraint is the
i-th root of the problem.
We presented here an encoding of compact nominal terms as directed acyclic
graphs. The same technique can be used to dene an encoding of standard,
reduced, head reduced, encoded and rst-order terms as directed acyclic graphs.
68
Chapter 6
Extending Paterson and Wegman's
First-Order Unication Algorithm
In this chapter we present an adaptation of Paterson and Wegman 's First-Order
Linear Unication Algorithm
(FLU)
[40] to nominal unication. We will show
that, the rst-order and nominal algorithms have the same structure. Because
any unication problem can be encoded in linear time and space to a single
constraint s t, in the rest of this chapter, we only consider unication problems
fs tg. The set of atoms in the problem, written A0 is dened
as A0 = A(s) [ A(t). In the rest of the chapter, we consider a nominal graph d
representing the problem s t.
of the form
Section 6.1 gives a theoretical characterization of nominal unication as a
relation on the nodes of d. It will be used to prove the correction of the algorithm
presented in section 6.2. A concrete implementation of the algorithm in Objective
CAML
[6] is in appendix B.1. Please refer to it for a complete description of the
actual implementation of the algorithm.
6.1 Nominal Unication as a Relation on Term
Nodes
The
FLU
algorithm computes the solution, if it exists, by computing a relation
on the nodes of the directed acyclic graph representing the problem. To prove the
69
6.1 Nominal Unication as a Relation on Term Nodes
correctness of their algorithm Paterson and Wegman developed in [40] the concept
of
valid
equivalence relation. To extend their algorithm to nominal terms, we
need to start by extending their notion of (valid) equivalence relation to nominal
directed acyclic graphs.
Denition 42
R is a pair of a 3-ary relation R? ? ; between
nodes and permutations, written s R ; t, and a binary relation F between atoms
? ?
and nodes, written a F s. Two nodes s and t are related by R, written s R t if
and only if it exists a permutation such that s R ; t.
? ?
A nominal relation
A nominal relation is a nominal equivalence relation if:
s R? ? ;id s for any s (Reexivity)
s R? ? ; t ) t R? ? ; 1 s (Symmetry)
u R? ? ;2 t ) s R? ? ;1 2 t (Transitivity)
s R? ? ;1 u
s R? ? ; s ) (a F s)a2supp()
(Disagreement set)
a F s s R? ? ; t ) 1 (a) F t (Congruence)
Denition 43 Let (R? ? ; ; F)
fc over nodes as
Proposition 12
Proof
If
be a nominal relation. Let us dene the function
fc(s) = fa j a F sg
(R??;; F) is a nominal equivalence relation then
s R? ? ; t ) fc(s) = (fc(t))
a be in fc(s), a F s and s R? ? ; t so (a) F t, 1 (a) 2 fc(t) and
s R? ? ; t ) t R? ? ; 1 s gives fc(t) 1 (fc(s))
Let
a 2 (fc(t)).
If two rst-order terms do not have, on their root node, the same function
symbol and the same arity, then they can not be unied. Paterson and Wegman's
algorithm uses it to detect when a problem does not have any solution. We extend
this notion of compatibility to nominal graphs:
Denition 44 (; F-compatibility)
Two nodes
s and t are said ; F-compatible
if and only if at least one of the following properties hold:
70
6.1 Nominal Unication as a Relation on Term Nodes
s = 0 s0
t = 0 t0
and
s0
and
t are (0 1 ); F-compatible
( 0); F-compatible
s and t are two atoms, s = t, s 62 fc(s) and t 62 fc(t)
s and t are two constants and s = t
and
s and t0
are
s and t are two pairs
s and t are two abstractions
s is a variable
t is a variable
When there is no ambiguity on
F, ; F-compatibility will be called -compatibility
Denition 45 Two nodes s and t, representing rst-order terms, are said compatible if and only if they are Id; ;-compatible.
Any directed acyclic graph can provide a partial ordering on the nodes of the
graph:
Denition 46 The partial order derived from the directed acyclic graph d is dened as: n1 >d n2 if and only if there exists a path from the node n1 to the node
n2 in d (n1 6= n2 ).
Now, we can extend the notion of
valid
equivalence relation to nominal rela-
tions. Paterson and Wegman dene a valid equivalence relation as:
An equivalence relation on the nodes of a dag is valid if it has the
following properties:
(i) if two function nodes are equivalent then their corresponding
sons are equivalent in pairs,
(ii) each equivalence class is homogeneous, that is it does not contain
two nodes with distinct function symbols,
(iii) the equivalence classes may be partially ordered by the partial
order on the dag.
71
6.1 Nominal Unication as a Relation on Term Nodes
We wanted our denition as close as possible to Paterson and Wegman's. The
rst rule is essentially a propagation rule, the second one a compatibility rule on
the nodes of a class and the third a rule to check there is no cycle in the graph.
So here is the denition of a valid nominal equivalence relation:
Denition 47 A nominal equivalence relation (R? ? ; ; F) on the nodes of a nominal dag is valid if:
Propagation:
(s1; s2) R??; (t1; t2) )
[a]s R??; [b]t
)
a F (s1 ; s2 )
a F [b]s
a F [a]s
Compatibility: each nominal class is
homogeneous,
s R? ? ; t, (s; t) is ; F-compatibles.
such that
)
)
s1 R? ? ; t1 s2 R? ? ; t2
s R? ? ;(a (b)) t
( (b) F s
if a 6= (b))
a F s1 a F s2
a F s (if a 6= b)
that is for any
s and t
Occurrence check: the partial order derived from the directed acyclic graph,
quotiented by the equivalence relation, is a partial order on the equivalence
classes.
The last remaining thing we need to extend is the notion of minimal relation
:
Denition 48
inal relations.
Let us extend the standard partial ordering over relations to nom-
(R??;1; F1) (R??;2; F2) if and only if
8s; t; s R??;1 t ) 90 s R??;20 t fa F1 tg(a)6=0(a) dom() dom(0)
and
8a; s a F1 s ) a F2 s
Now we can state the same property as in [40] between valid relations and
solutions of unication problems:
72
6.2 A Quadratic Nominal Unication Algorithm
Proposition 13
Two nominal terms
s
and
t
are uniable if and only if there
exist a valid nominal equivalence relation on the graph representing
that
s R? ? ;id t.
s t
such
If such a relation is minimal, then it denes a most general
unier.
s and t are uniable then let (; ) be a solution of the unication
problem. Let R? ? ; be the relation containing s R? ? ; t if and only if s t
under . Let F be the relation containing a F s if and only if a # s under .
If such a relation exists, can be derived from the dag by (X ) = t if
X R? ? ; t and t not a variable, or (X ) = X is there is no such t. And
= f a # X j a F X (X ) = X g
Proof
If
6.2 A Quadratic Nominal Unication Algorithm
The Paterson and Wegman First-Order Linear Unication Algorithm is as described by the algorithm 1 and 2. It is linear in time and space in the size of the
directed acyclic graph.
The nominal version of
FLU,
(FLU)
is described in algorithms 3 , 4 and 5. Like in
the algorithm proceeds by computing a valid relation. There are two kinds
of edges in the algorithm: the edges of
s
t (also s R??; t).
d and undirected edges s 1
$ t which
pointer() is a partially dened function
whose domain is empty at the beginning of the algorithm. pointer(s ) represents
the class of s with the corresponding permutation.
In the rest of this chapter, we will use the similarity between FLU and QNU
to prove the correctness and the complexity of QNU.
represent
Proposition 14
Applying the morphism
terms to every operation in
QNU
F
from compact terms to rst-order
gives exactly
FLU:
F (QNU) = FLU
Proposition 15
Proof
Like
QNU
is correct.
FLU, QNU
computes a valid nominal equivalence relation. So if
the algorithm terminates without raising an error, then the computed relation is
valid. Furthermore the relation is minimal.
73
6.2 A Quadratic Nominal Unication Algorithm
1
2
3
4
unify-flu(s,t ) = ;
create-edge-flu(s,t ) ;
while There is a non-variable node r do
finish-flu(r )
5
end
6
;
7
8
9
10
11
while There is a variable node r do
finish-flu(r )
end
;
print Unied;
13
create-edge-flu(s,t ) = ;
create the undirected edge
14
propagate-unification-edges-flu(r,s ) = ;
12
15
16
17
18
19
20
21
22
23
24
25
26
27
28
s $ t;
switch (r; s) do
case ((r1 ; r2 ); (s1 ; s2 ))
create-edge-flu(r1 ; s1 ) ;
create-edge-flu(r2 ; s2 )
case ([]r0 ; []s0 )
create-edge-flu(r0 ; s0 )
otherwise
do nothing
endsw
endsw
doFathers-flu(s ) = ;
while there is a father t of s do
finish-flu(t )
end
Algorithm 1: First-Order Linear Unication (FLU): Part 1
74
6.2 A Quadratic Nominal Unication Algorithm
1
2
3
4
5
6
7
8
9
10
11
12
finish-flu(r ) = ;
Create a new empty stack stack;
push(r ) on stack;
while stack is non empty do
s := pop(stack) ;
if pointer(s ) is dened then
r' := pointer(s ) ;
if r' 6= r then
error(FAIL-LOOP )
end
else
pointer(s ) := r
13
end
14
;
15
16
if
s are not compatible then
error(FAIL-CLASH )
r and
17
end
18
;
19
doFathers-flu(s );
if s is a variable node
(s ) := r
20
21
22
23
else
propagate-unification-edges-flu(r,s )
24
end
25
;
26
while there is a undirected edge s
push(t) on stack;
delete the edge s $ t
27
28
29
30
31
$ t do
end
if r and s are not the same node then
delete s
33
end
end
34
delete
32
then
r
;
Algorithm 2: First-Order Linear Unication (FLU): Part 2
75
6.2 A Quadratic Nominal Unication Algorithm
1
2
3
4
unify-qnu(s,t ) = ;
create-edge-qnu(s; Id; t) ;
while There is a non-variable and non-suspended node r do
finish-qnu(r )
5
end
6
;
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while There is a variable node r do
finish-qnu(r )
end
;
print Unied;
create-edge-qnu(s, ,t ) = ;
switch (s; t) do
case (0 s0 ; t)
create-edge-qnu(s, 0 1 ,t )
case (s; 0 t0 )
create-edge-qnu(s, 0 ,t )
otherwise
create the undirected edge
s
1
$ t
endsw
endsw
Algorithm 3: Quadratic Nominal Unication (QNU): Part 1
76
6.2 A Quadratic Nominal Unication Algorithm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
propagate-unification-edges-qnu(r, ,s ) = ;
switch (r; s) do
case ((r1 ; r2 ); (s1 ; s2 ))
create-edge-qnu(r1 ; ; s1 ) ;
create-edge-qnu(r2 ; ; s2 )
case ([a]r0 ; [b]s0 )
create-edge-qnu(r0 ; (a
if a 6= (b) then
add 1 (a) to fc(s0 )
end
otherwise
(b)) ; s0 ) ;
do nothing
endsw
endsw
doFathers-qnu(s ) = ;
17
while there is a father t of s do
if t is a suspended node then
18
doFathers-qnu(t )
16
19
20
21
22
else
finish-qnu(t )
end
end
Algorithm 4: Quadratic Nominal Unication (QNU): Part 2
77
6.2 A Quadratic Nominal Unication Algorithm
1
2
3
4
5
6
7
8
9
10
11
12
finish-qnu(r ) = ;
Create a new empty stack stack;
push((id,r) ) on stack;
while stack is non empty do
( ,s) := pop(stack) ;
if pointer(s ) is dened then
( 0 ,r') := pointer(s ) ;
if r' 6= r then
error(FAIL-LOOP )
end
else
pointer(s ) := ( 1 ,r)
13
end
14
;
15
16
if
s are not -compatible then
error(FAIL-CLASH )
r and
17
end
18
;
19
doFathers-qnu(s );
if s is a variable node
(s ) := 1 r
20
21
22
23
else
propagate-unification-edges-qnu(r, ,s )
24
end
25
;
26
27
28
29
30
31
32
33
34
35
36
37
38
then
while there is a nominal undirected edge s
push(( 00 ; t)) on stack;
delete the edge s 00
1
00
1
$00 t do
$00 t
end
if r and s are the same node then
add supp( ) to fc(r )
else
add 1 (fc(s)) to fc(r ) ;
delete s
end
end
propagate fc(r ) to its children ;
78
delete r ;
Algorithm 5: Quadratic Nominal Unication (QNU): Part 3
6.2 A Quadratic Nominal Unication Algorithm
Proposition 16 Let d be a nominal directed acyclic graph representing the problem s t. The complexity unify-qnu(s,t ) is at most CA (j F (d) j +n (d))
where j F (d) j is the size of the rst-order directed acyclic graph corresponding to
d and n (d) the number of permutation application () nodes in d.
Proof
Let
F (d) be the direct acyclic graph obtained by applying F
to every
d the same way it is done with terms. Let us compare the execution of
unify-flu(F (s); F (t)) and unify-qnu(s,t ). The only possible dierence in the
node in
execution of the two functions is line 14 of algorithm 2 and line 14 of algorithm 4
when checking ( )-compatibly. It may happen that two terms
-compatibly
but
F (r )
and
F (s)
on the error FAIL-CLASH while
are compatible. In that case,
FLU
r
and
QNU
s
are not
will halt
continues its execution. Otherwise, the
(s); F (t)) and unify-qnu(s,t ) pass through the same
steps. Most of the operation which take n time in FLU take CA n in QNU. The
only one which does not, is the edge creation. When creating an edge s !e t, s
execution of unify-flu(F
and
t may be suspended terms but we want s and t to be head reduced terms so
we reduce them before creating the edge. Fortunately, this extra cost is bounded
in the whole algorithm by CA j F
(d) j.
Implementation and complexity
Atoms, constants and variables are imple-
mented as integers. Permutations and sets are implemented as arrays indexed by
atoms. The actual implementation of permutations and sets is in appendix B.1.
Composing and inverting permutations and permuting and computing the union
of sets takes a linear time in the number of atoms in the problem. Accessing
the image of an atom by permutation, or performing a membership test takes a
constant time. So with this implementation of permutations and sets of atoms
CA =j A0 j where A0 is the set of atoms appearing in the problem.
Remark 4
When
j d j is close j A0 j (j F (d) j +n (d)) and with mutable arrays
as permutations and sets implementation, the unication algorithm becomes linear
in time and space.
79
Chapter 7
A Simple and Ecient Nominal
Unication Algorithm
QNU
is a good algorithm and provides a concrete proof that nominal unication
can be done in quadratic time. But
QNU
relies a lot on very ad-hoc imperative
techniques which make it dicult to extend and in practice manipulations on
directed acyclic graph are much harder than on trees. In practice it is often
better to have a simple good algorithm, easy to maintain and extend, than a
more ecient, but complex and hard to maintain and extend, one.
In this chapter we present a modular algorithm (AQNU), whose complexity is
almost quadratic in time, to solve nominal unication. It has been inspired by the
Martelli
and
Montanari
ecient rst-order unication algorithm [34]. Martelli
and Montanari use the good but not linear set union Tarjan's algorithm [50], more
well known as the Tarjan 's union-nd algorithm, to maintain equivalence classes.
To maintain nominal equivalence classes, we need to adapt
Tarjan 's
union-nd
algorithm to nominal equivalence relations.
Section 7.1 presents such a nominal union-nd algorithm and section 7.2 uses
it to build a simple and ecient nominal unication algorithm. Concrete implementations in
Haskell
of the nominal union-nd and the nominal unication
algorithm are respectively in appendix A.7 and A.8.
80
7.1 A Nominal Union-Find Algorithm
7.1 A Nominal Union-Find Algorithm
disjoint set union problem is to
S1 ; : : : ; Sn with two operations on them:
The
maintain a collection of disjoint sets
find(x ) to nd the set Si containing the element x.
union(Si ; Sj ) unite Si and Sj into a single set: The sets Si and
removed from the collection and replaced by the set Si [ Sj .
Denition 49
A
union-nd
Sj
are
algorithm is an algorithm implementing the col-
lection and the two functions of a disjoint set union problem.
Tarjan's union-nd algorithm 6 represents each set as a tree. The collection
is called a
y
forest, written F.
Edges are written
x !e y where x is the origin and
the destination of the edge. Each set is identied by the element at the root
node of its tree called the representative of the set. find(x ) returns the root
node of tree containing
x and union(x,y ) merges the trees whose root nodes are
find(x ) and find(y ). To be ecient, the merging strategy of union() is to
minimize the depth of the trees and every time find(x ) is computed, the path
from x to the root node is rewritten so that x points directly to it.
The collection of trees are implemented as an array arruf indexed by the
elements of [ni=1 Si so that :
arruf [x] =
where
r
(
Indirect y
Direct r
is an integer called the
where y is the father of
if x is a root node
rank of x.
x
The rank represents a bound of the
depth of the tree.
Tarjan's union-nd algorithm is well suited to maintain rst-order equivalence
classes. But in nominal unication, two variables x and y in the same equivalence
class do not directly represent -equivalent terms but there exists a permutation
such that x and y do.
We need to adapt Tarjan's algorithm to take account
of these permutations and the freshness constraints they generate. We annotate
every edge
x !e y by a permutation , written x !
e y , arruf
arruf [x] =
(
Indirect y
Direct r; d
( )
becomes:
where y is the father of
if x is a root node
81
x
7.1 A Nominal Union-Find Algorithm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
find(x ) = ;
if arruf [x] = Indirect y then
r = find(y ) ;
arruf [x] := Indirect r ;
return r
else
return x
end
union(x,y ) = ;
zx = find(x ) ;
zy = find(y ) ;
if zx 6= zy then
Direct rx = arruf [zx ] ;
Direct ry = arruf [zy ] ;
if rx = ry then
arruf [zy ] = Direct (ry + 1) ;
arruf [zx ] := Indirect zy
else
if rx < ry then
arruf [zx ] := Indirect zy
else
arruf [zy ] := Indirect zx
end
end
end
Algorithm 6: Tarjan's union-nd algorithm (TUF)
82
7.1 A Nominal Union-Find Algorithm
r
In the unication algorithm, arruf
[x] = Indirect y will mean that x R??; y.
is still the rank of the tree. The new value
d is a data attached to the root of
every tree.
Sdat is a tuple of (G; f; p) where G is a group
(its internal law is written +), f a group action from P(A) on G where P(A) =
fA j A Ag and p is a group action of on G such that
Denition 50
A nominal data set
8A Satms; 2 ; g 2 G p(; f (A; g)) = f ((A); p(; g))
For the sake of simplicity, in the rest of this section, given a nominal data set
Sdat , we use g 2 Sdat for g 2 G, A # g for f (A; g) and g for p(; g).
A suspended node is a pair of a permutation and a node v written v . The
structure of arruf denes a relation R over suspended nodes and nominal data.
Let R be the relation dened as:
ids R e , s !
ee2F
idr R id(data r) if r is a root node
and
R the reexive, transitive, symmetric and invariant closure of R, ie the
minimal relation satisfying:
s s R s s
V
s R 0 s ids R idd ) d = ds(; 0 ) # ds
s s R e e ) e e R s s
V
s s R e e e e R f f
) ss R f f
8 ss R ee ) ( s)s R ( e)e
where s, e, and
f are nodes or nominal data and d is a nominal data.
The Nominal Union-Find algorithms 7 and 8 provide four functions:
union, data
and
putData
nd,
such that
(
)
(r (root s)) where ss R r (root s)
union(s s1 ; s s2 ) does not return anything but merge the trees of s1 and
s2 such that s s1 R s s2
nd s s returns
1
2
1
2
83
7.1 A Nominal Union-Find Algorithm
(s) returns d such that s R d
putData( s; d) does not return anything puts stores d in such a way that
s R d
data
where s,
1
find(s s) = ;
if arruf [s ] = e e then
r r = find(e e) ;
arruf [s] := r r ;
return (s r )r
else
return s s
end
2
3
4
5
6
7
8
9
10
11
data(s s) = ;
z z = find(s s) ;
Direct (r; d) = arruf [z ];
return z d
12
13
14
15
16
s1 and s2 are nodes and d is a nominal data.
putData(s s,d ) = ;
z z = find(s s) ;
Direct (r; d0 ) = arruf [z ] ;
arruf [z ] := Direct (r,z 1 d)
Algorithm 7: Nominal Union-Find Algorithm (NUF): Part 1
Proposition 17 (Complexity) The complexity of find() (resp. union()) in
NUF is bounded by CA C where C is the complexity of find() (resp. union())
in
TUF.
ack
So the amortized complexity in time of
find()
and
union()
1 (j [n Si j) where ack 1 () is the inverse Ackermann function.
i=1
is
CA union() has the same structure and pass through the same steps in
and NUF. And any step in TUF that takes n time takes at most CA n
Proof
TUF
in
NUF
Now that we have an algorithm well suited to maintain nominal relation equivalence classes, we can use it to develop a nominal unication algorithm.
84
7.1 A Nominal Union-Find Algorithm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
union(1 s1 ; 2 v2 ) = ;
(z1 z1) = find(1s1) ;
(z2 z2) = find(2s2) ;
Direct (r1 ; d1 ) = arruf [z1 ] ;
Direct (r2 ; d2 ) = arruf [z2 ] ;
if z1 = z2 then
putData(idz2 , ds(z1 ; z2 ) # d1 )
else
if r1 = r2 then
arruf [z2 ] := Direct (r2 + 1; d2 ) ;
union(z1 z1 ; z2 z2 )
else
if r1 > r2 then
union(z2 z2 ; z1 z1 )
else
3 = z11 z2 ;
arruf [z1 ] := Indirect 3 z2 ;
putData(idz2 ,d2 + 3 1 d1 )
end
end
end
Algorithm 8: Nominal Union-Find Algorithm (NUF): Part 2
85
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
In this section we present a nominal unication algorithm inspired by Martelli and
Montanari's almost linear rst-order unication algorithm [34]. The algorithm
uses the same techniques of using multi-equations and maintaining, for each class
of variable, a counter of the number of occurrence of this class still in the problem.
Once again the nominal algorithm will have the same structure as the rst-order
algorithm.
Because any unication problem can be encoded in linear time and space to
s t, in the rest of this chapter, we consider that the input
tg. The set of atoms in the problem, written A0 is dened as
a single constraint
fs A0 = A(s) [ A(t).
problem is
When we encounter X
Y in the process of the algorithm, it means that if
the problem has a solution then there exists a valid nominal equivalence relation
(R??;; F) such that X R??; Y . X could be replaced by Y
so
X
X
and
by
Y
Y
and
Y
by
1 X ,
can be considered more or less as the same variable. Replacing
(or
Y
by
1 X )
would be too costly but maintaining the nominal
equivalence classes of variables using the above nominal unication algorithm is
cheap.
Denition 51 (Equations) Here equations are A # t1 ? ? : : : ? ? tn
is a set of atoms and t1 ? ? : : : ? ? tn a multiset of terms.
Denition 52
The unication nominal data attached to a variable
nominal union-nd algorithm are triples
X
A
in the
(o; A; lt) where o is an integer counting
the number of occurrences of variables in the class of
set of atoms and
where
X
lt is a list of terms [t1 ; : : : ; tn ] such that
in the problem,
A
is a
A # X ? ? t1 ? ? : : : ? ? tn
is an equation of the problem. The set of unication nominal data is called
Proposition 18 Sudat
with
86
Sudat
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
(o1; A1; lt1) + (o2; A2; lt2) = (o1 + o2; A1 [ A2; lt1 + lt2) where lt1 + lt2 is the
list forms of all the elements of
lt1
followed by all the elements of
lt2
(0; ;; []) as neutral element (where [] is the empty list)
form a group. With the two following actions:
8 2 (o; A; [t1; : : : ; tn]) = (o; (A); [t1; : : : ; tn])
8A 2 A A # (o; A0; lt) = (o; A0 [ A; lt)
it forms a nominal data set.
The algorithm uses a global stack to store the equations of a nominal unication problem. Let
stack be this stack and emptyStack be the empty stack.
The main function of the algorithm, unify(), is described in algorithm 9. It
initializes the nominal union-nd array, the global stack
stack while the stack is
not empty, pops an equation from it and treats it. When the stack is empty, it
performs an occurrence check.
The normalizing function, normalizeEquation(), makes sure all terms in the
equation are in head reduced normal form and that there is at most only one term
that can be a suspended variable.
Denition 53 At any time t in the execution of the problem, the problem P rt
at time t is formed of the equations in the global stack, the ones in the nominal
unication data and the constraints A # X and X 7! t (which represents X t)
in the output.
Decomposition function Let eq = A # t1 ? ? : : : ? ? tn be a normalized
equation. If eq does not contains head variables, then it has to match one of this
87
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
Input: Two terms s
and
t
to unify
1 ;
2
3
4
5
unify(s,t ) = ;
for X 2 Vars(s ) [ Vars(t ) do
putData(IdX; (oX ; ;; [])) ;
where oX is the number of occurrences of
end
6
X in
stack:= emptyStack ;
push(; # s ? ? t) on stack;
while stack is not empty do
eq = pop(stack) ;
eq0 = normalizeEquation(eq ) ;
7
8
9
10
11
if eq0
12
has a head variable
storeEquation(eq 0 )
13
then
else
14
treatDecompose(eq 0 )
15
end
end
16
17
occurCheck()
18
Input: an equation A # t1 ? ? : : : ? ? tn
19 ;
20
21
22
23
24
25
26
27
28
29
normalizeEquation(A # t1 ? ?
: : : ? ? tn ) = ;
for 0 ilen do
h
replace ti by ti
end
while there are ti = i Xi and tj = j Xj
union (i Xi , j Xj ) ;
remove tj from the equation ;
decrement the counter of occurrence of
with
Xj
i 6= j do
by one
end
return the remaining equation
Algorithm 9: Unication and normalizing functions
88
; # s ?? t
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
rules:
(AtomsRule)
(CstRule)
(P airRule)
(AbsRule)
A # a ? ? : : : ? ? a
) ;
a and atom and a 62 A
A # f ? ? : : : ? ? f
with
) ;
f a constant
A # (t11 ; t12 ) ? ? : : : ? ? (tn1 ; tn2 )
) fA # t11 ?? : : : ?? tn1 ;
A # t12 ? ? : : : ? ? tn2 g
A # [a1 ]t1 ? ? : : : ? ? [an ]tn
) f(A [ fa1 : : : ang) n fa1g # (a1 a1)t1 ?? : : : ?? (a1 an)tng
with
Then resulting equations are put in the stack.
If
eq does not match any of these rules then the problem has no solution and
the program raise an error.
The function taking
eq
or raising an error is called
as input, putting the resulting equation in the stack
decompose
If the normalized equation does contain a term X X , then
it can not be decomposed but it can be store in the unication nominal data
Storing equation
of
X.
The function which stores such equations is called storeEquation() and
described in algorithm 10.
OccurCheck The function occurCheck checks that the data attached to every
class is (0; ;; []). If not, it means that the problem does contains a loop and so has
no solution. Thus, if one of the data is not (0; ;; []) the function raise an error,
otherwise returns nothing.
Proposition 19 (Correctness)
does not have any solution.
If the problem raised an error, then the problem
Otherwise, the output of the algorithm is a most
general solution of the problem.
Proof
All the steps of the algorithm transform the problem into an equivalent
one. So the set of the solutions of the problem is preserved.
89
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Input: A normalized equation eq = A # X ? ? t1 ? ? : : : ? ? tn
storeEquation(A # X ? ? t1 ? ? : : : ? ? tn ) = ;
d = data(( X )) ;
(o; A0; [tn+1; : : : ; tm]) = d ;
which represents the equation A0 # X ? ? tn+1 ? ? : : : ? ? tm ;
the two equations can be combined into :;
A00 # t1 ? ? : : : ? ? tm with A00 = A [ A0 ;
if o = 1 then
this is the only occurrence of X in the problem;
then the class of X is a root class and ca be processed;
if n = 0 then
then equation is the freshness constraints
output the freshness constraint 1 (A00 ) # X
else
X 7! 1 t1 ;
push(A00 # t1 ? ? : : : ? ? tm ) on stack
end
putData( X; (0; ;; []))
else
putData( X; (o 1; A00 ; [t1 ; : : : ; tm ]))
end
Algorithm 10: Equation storing function
output the substitution
90
A00 # X ;
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
7.2.1 Complexity
Let
d be a nominal data (o; A; lt).
Representing
lt as a list is too costly because
of the number of times we need to apply a permutation to its elements and
l1 ; : : : ; l4 be 4 lists and let us consider
l1 + l2 + l3 + l4 . Doing it in one shot is linear in jl1 j + jl2 j + jl3 j + jl4 j but computing
l5 = l1 + l2 , then l6 = l3 + l4 and nally do l5 + l6 is not. So performing lazily
appending two lists. For example, let
these operation is more ecient. In this section we implement a better structure
to represent
lt with lazy permutations and lazy appending.
This structure is the algebraic datatype:
T ree a
=
j
j
j
Nil
Singleton a
Node (T ree a) (T ree a)
Remap (T ree a)
the empty tree
the leaf a
a node with two children
With this structure we can merge two trees
Node t1 t2 .
t1
and
t2
in constant time with
And we can lazily apply a permutation to all the elements of a tree in
constant time with Remap t. If we have to apply many permutations to a tree,
permutations will be composed together and only then the resulting permutation
will be applied on the elements of the tree, which means only one tree and term
traversal.
When we need to get the list of terms, we atten the tree, applying permutations and giving back the list of leaves.
Input: A nominal tree t
1
2
3
4
5
6
7
8
flatten(t ) = flattenAux(id , t );
where;
flattenAux(id , t ) = switch (; t) do
case (,Nil) ! return [] ;
case (,Singleton a) ! return [a] ;
case (,Node t1 t2 ) ! Node flattenAux(; t1 ) flattenAux(; t2 )
;
case (,Remap 0 t) ! flattenAux(( 0 ); t)
endsw
91
7.2 An Almost Quadratic Nominal Unication Algorithm (AQNU)
Proposition 20
ture is at worse
ack
The complexity of the unication algorithm with the lazy struc-
CA (j F (s) j +n (s)+ j F (t) j +n (t)) 1 () is the inverse Ackermann function.
Proof
ack
1 (j
X j) where
Once again, when we forget all nominal details in the nominal algo-
rithm, we get a rst-order unication algorithm. The complexity in time of this
(j F (s) j + j F (t) j) ack 1(j X j) and traversing a permutation
application node takes CA time. ack 1 (j X j) comes from the nominal union-nd
algorithm.
algorithm is
Remark 5 When j F (s) j + j F (t) j is close to j F (s) j +n (s)+ j F (t) j
+n (t), with mutable arrays as permutations ans sets implementation the nominal
algorithm becomes linear in time and space.
Unlike to
QNU, AQNU
is convenient to use in practice: its input is simply
two nominal terms, it is modular and quite functional. That is why it has been
implemented in the
Haskell
library whereas
isolated algorithm.
92
QNU
has been implemented as an
Chapter 8
Nominal Unication via Graph
Rewriting
In this chapter we present a polynomial nominal unication algorithm based on
graph rewriting. The algorithm takes a directed acyclic graph representing the
problem as input and performs a series of transformations on it until either raising
an error or reaching a solution.
The encoding of nominal terms is a bit dierent from what was presented in
chapter 5. The two dierences are in the syntax of functions and tuples:
Denition 54
f t:
f
t
(t1; : : : ; tn):
()
t1
...
tn
Denition 55 (Graph rewriting rule) A graph rewriting rule is a pair of nominal graphs, written l ) r , where leaves may be labelled by (graph) metavariables
93
s; t; : : :,
l has exactly one root. All
in l , and we will also assume
and where
must also occur
l
the metavariables occurring in
r
that each metavariable occurs at
most once in .
A graph rewriting rule may have several roots in the right-hand side; rules
with multiple roots in the right-hand side will be used in Section 8.1.1.
Example 3
The following is a graph rewriting rule, using a metavariable
Id
t
t.
) t
l ) r to a graph G, we rst create a copy of
r and add it to G, and then pointers towards the instance of l in G are redirected
towards the copy of r. Occurrences of metavariables in r are replaced by a pointer
to the corresponding subgraph in G (thus, they are shared). Other pointers are
To apply a graph rewriting rule
not aected. If a node is no longer needed (i.e. there is no path from the root
node) we garbage-collect it.
We now dene graph reduction more precisely:
Denition 56 (Graph reduction) The one-step reduction relation generated
0
by a graph rewriting rule l ) r is dened as a set of pairs G ) G generated as
follows:
When a subgraph
g
in
G
matches the left-hand side
can be obtained by replacing the metavariables in
redex), G0
l
l
of a rule (that is,
by graphs; we say that
g
g
is a
is obtained by:
G a copy of the right-hand side r, where the metavariables t in r
are replaced by pointers to the corresponding instance of t in g .
1. Adding to
g towards the root of r (the pointers
are duplicated if r has more than one root, or erased if r is empty); in the
special case in which r is just a metavariable t, then pointers to the root
of g are updated to point directly to the instance of t in g . The use of a
2. Redirecting all the pointers to the root of
rule with multiple roots in the right-hand side is only permitted if the parent
nodes of
g
have variable arity.
94
3. Nodes that have no incoming edges are erased (garbage collected), except for
the node
Pr
(the root of the problem). In particular, the root of
g
will be
garbage-collected since all pointers to it are redirected, but the other nodes
in
g
may still be of use.
Example 4
The rule in Example 3 can be applied to the graph of
we assume there are incoming edges
H
O):
and
Id
[a]X (below
H
O
[]
Id
a
X
since we can identify an instance of the left-hand side in this graph (replacing
by the graph representing
representation of
[a]X :
t
[a]X ). After one step of rewriting we obtain the graph
H
O
[]
a
where the nodes
and
Id
X
have been garbage collected, and the pointer
updated, so that it points to the instance of
t (O
H
has been
is not aected).
The following sections specify a correct and complete graph rewriting algo-
rithm to solve nominal problems. We rst consider constraints in Section 8.1.1,
and show how the graph representing a set of
constraints can be reduced to
? if there is no solution, or to a graph representing a substitution and set of
freshness constraints. Section 8.1.2 then deals with freshness constraints: we
specify an algorithm to transform a set of freshness constraints into an equivalent
freshness context. We will show that both algorithms are polynomial in time and
space.
95
8.1 A polynomial Algorithm via graph rewriting
8.1 A polynomial Algorithm via graph rewriting
8.1.1 A graph rewriting algorithm to solve constraints
-equality constraints using three kinds
of graph rewriting rules: normalisation rules to simplify the graph representing the problem (eliminating trivial constraints, identity permutations, etc), rules to solve equality constraints, and neutralisation of permutations to
We will present an algorithm to solve
propagate lazy permutations in case no
-rule can be applied.
In the sequel, we assume that we start with a graph representing a nominal
problem and we only consider graphs that are obtained by reduction of the initial
graph. We show below that all the graph rewriting rules we will dene transform
the graph into a graph representing an equivalent problem.
8.1.1.1 Normalisation of the graph
Denition 57
not
A skeleton node
is a node labelled by a term constructor that is
or a permutation. A value node
is either an atom, a set of atoms, or unit
(i.e., a 0-ary tuple).
Skeleton nodes are therefore value nodes, variables, abstractions, tuple constructors and function nodes. For example, in the graph representing the term
a, there is only one skeleton node, which is the one labelled by a.
The following function W will be used to distinguish suspended variables on
variable nodes from other suspended variables, ignoring permutations.
Denition 58 (Weight) The function W : N
1) to each node n. It is dened by cases on n:
! f0; 1g assigns a weight (0 or
If n is a variable, W (n) = 0.
If n is neither a variable nor an , W (n) = 1.
If n is and it is the root of a subgraph as shown below, then W (n) = W (t).
96
8.1 A polynomial Algorithm via graph rewriting
t
The computation of the weight of a node can be done in polynomial time in
the size of the graph, since in the worst case it requires the traversal of a path in
the graph (and the graph is acyclic).
To reduce the graph we apply a set of
normalisation rules,
which are parti-
tioned into three groups: simplication, ordering, and compacting.
Simplication rules
Rule
Id-Perm
eliminates identity permutations:
t
Id
) t
Note that when applying this rule, no node is created since
t was already
present in the left-hand side. According to Denition 56, all pointers to
the
node in the left are redirected towards the node t, and the node
is garbage collected. The node
Id
will be garbage collected if there are no
other pointers to it.
It is clear that this rule preserves all the solutions of the problem, but notice
that in the case in which t is a variable, we are replacing
Id
X by X , which
strictly speaking is not a term. We could impose a condition that the rule
applies only when
t is not a variable, but in practice it is better to use the
rule also with variables and take this fact into account when we read the
problem represented by the graph (see Section 8.1.1.2).
Rule
Apply-Perm
applies permutations on
value
nodes. We give below a
rule scheme which encompasses three kinds of rules (one for each kind of
value node, see Denition 57):
97
8.1 A polynomial Algorithm via graph rewriting
) t
t
t is a value node.
if
Note that, according to Denition 56, when we reduce a graph using Apply-Perm ,
all pointers to the
node are redirected towards the node t, and the node is garbage collected. The node
instead may have other pointers to
it (it will only be erased if there are no pointers to it).
This rule preserves the solutions of the problem, since it replaces a suspended permutation by its result.
Ordering rules
Rule
Order-Unif
puts the heaviest side of an equation on the right, using
the notion of weight dened above (see Denition 58):
s
if
t
)
t
s
W (s) > W (t)
Only the edges of the graph are aected by this rule, no nodes need to be
created or erased.
Clearly, solutions are preserved since
is commutative.
Rule Order-Perm moves permutations towards the right-hand side of equations:
98
8.1 A polynomial Algorithm via graph rewriting
) s
t
if
1
s
t
W (s) W (t) and s is a skeleton node
.
In other words, this rule transforms a constraint of the form
s 1 t, which is equivalent (see [20, 51]).
s t into
Compacting rules
Rule
Consec-Perm
composes consecutive permutations:
) 0
0
if
t
t is a skeleton node
t
.
If there are more than two consecutive permutations, the condition in this
rule ensures that they are composed bottom-up (because
t
cannot be an
application of permutation).
8.1.1.2 Properties
To obtain a graph representation of a nominal unication problem, in the sequel
we assume that the translation function described in Denition 40 is used, with
a further optimisation: suspended variables of the form
represented as a node labelled by
X.
Id
X
may simply be
Proposition 21 (Correctness of normalisation) The normalisation rules are
correct; more precisely: if a graph G representing a unication problem P r reduces
0
to a graph G , then
99
8.1 A polynomial Algorithm via graph rewriting
1.
G0
is the representation of a problem
represented simply by a node
2.
P r0
is equivalent to
P r0
(except that a term
X ); and
IdX
may be
P r.
Proof
1. We consider each normalisation rule in turn. Since the translation function
from problems to graphs is inductive, we obtain the problem P r0 associated
to G0 by replacing in P r the subproblem corresponding to the left-hand side
of the rule by the problem represented by the right-hand side. The only
special case is
Id-Perm ,
which may replace
Id
X by X .
2. The normalisation rules can be justied as follows:
Id-Perm
and Apply-Perm are correct by denition of permutation (see
Section 4.2).
is commutative.
1
Order-Perm is justied by the equivalence: s t , s t.
0
0
Consec-Perm is justied by the fact that ( t) and ( )t represent
Order-Unif
is correct because
the same nominal term.
To show that the normalisation rules are terminating (i.e., reduction sequences
are nite), we introduce the notion of position in the graph.
Denition 59 (Positions) A position in a graph G representing a unication
problem P r is a string of integers. We use to denote the empty string, which
will be associated to the root of the graph, and use the notation p:i to denote the
string containing all the elements of p followed by i. The node at position p in
G, written Gjp , is dened as follows:
Gj is the root of the problem, the only node labelled by P r.
Gji is the i-th root, that is, the root of the i-th constraint.
Gjp:i is the node attached to the i-th port of the node Gjp.
100
8.1 A polynomial Algorithm via graph rewriting
The weight of a position is the weight of the node at that position.
Due to sharing, the same node may be associated to several positions as the
following example illustrates.
Example 5
In the graph
G representing the problem
[a]a [b]b; (a b)X X
2 is labelled by (that is, Gj2 is the root
of the second constraint), and the node at position 2:2 (i.e., the second child
of the node at position 2) is labelled by the variable X . Since X is shared,
this node is also at position 2:1:2.
given in Example 2, the node at position
All the normalisation rules, except
Order-Unif
, preserve the weight of posi-
tions; more precisely:
G be a nominal graph, p a position in G, and G0 the graph
obtained by applying a normalisation rule l =) r dierent from Order-Unif on
G. If G0 jp exists, and p corresponds to a position that exists in both l and r, then
W (Gjp ) = W (G0 jp ).
Proposition 22
Proof
Let
By inspection of the rules. The weight of a node at a position
left-hand side is the same as the one in position
p in the
p in the right-hand side.
Intuitively, this property says that the graph positions that survive reduction,
that is, the positions that are in the left and right-hand sides of the rule, maintain
their weight, except when
Order-Unif
is used.
We will now show that the rewrite system dened by the simplication, ordering and compacting rules is terminating. Unfortunately we cannot use a simple
interpretation based on sizes, because the rule
Consec-Perm ,
although having a
left-hand side which is bigger than the right-hand side, may increase the size of
the graph. Indeed, this rule creates a node
0 , and the nodes in the left-hand
side may be shared, so cannot be erased. We will dene the following patterns,
which include the left-hand sides of the rules above and some additional graphs
that will be useful to dene a decreasing measure:
101
8.1 A polynomial Algorithm via graph rewriting
Pattern
Apply-Perm :
Pattern
t
Consec-Perm :
0
Pattern
Order-Unif
t
:
s
where
Pattern
t
W (s) > W (t)
Order-Perm-Left :
where
Pattern
t
s
W (s) W (t)
Order-Perm-Right :
s
102
t
8.1 A polynomial Algorithm via graph rewriting
where
W (s) > W (t)
We associate to each pattern a number of points:
Apply-Perm
Consec-Perm
Order-Unif
Order-Perm-Left
Order-Perm-Right
and to each node
n
:
:
:
:
:
1
1
1
3
3
point
point
point
points
points
in a graph the sum of the points of all the patterns that
match the subgraph rooted by n (a subgraph may match several patterns). More
precisely:
Denition 60 (Reduction Points) Let G be a graph and n a node in G. Let
RedP ts(G; n) be the sum of the points of patterns matched by the subgraph with
P RedP ts(G; n).
root n in G. RedP ts(G) =
n 2G
Proposition 23
Proof
Let
n
For any node
n in a graph G, RedP ts(G; n) 4.
node, it may only match
node it may match
Apply-Perm
Order-Perm-Left ,
4 points at most. Otherwise,
and
or
Consec-Perm :
Order-Unif
and
Let
2 points.
Let
n
is a
Order-Perm-Right ,
n matches no pattern so has zero points.
G be a graph and G0 be the graph
0
normalisation rule on G. RedP ts(G ) < RedP ts(G).
Proposition 24
Proof
If n is a be the root of a subgraph that matches a pattern. If
so
obtained by applying a
n be the root of a subgraph that matches a rule; we say that n is
the node on which the rule is applied. We consider each rule in turn:
n is deleted, and the value node t created by
Apply-Perm has zero points. RedP ts() may have changed for some nodes:
Let n0 be a node of G0 and G (so n0 6= n). Either n0 was not aected by the
Apply-Perm
and
Id-Perm :
rewrite step, or it is a node in a position belonging both to the left- and
righ-hand sides.
Apply-Perm
and
Id-Perm
preserve the weights of those
positions (Proposition 22), and pattern conditions depend only on weight
conditions so a condition is satised after the application of the rule if and
103
8.1 A polynomial Algorithm via graph rewriting
only if it was satised before. If a pattern is matched by n0 in
G0 so it was
in G. So RedP ts(G0 ; n) RedP ts(G; n), hence RedP ts(G0 ) < RedP ts(G)
(because n has points and n is not in G0 ).
Order-Unif
:
n in G0 cannot match the patterns Apply-Perm or Consec-Perm
), and Order-Perm-Right (because of weight conditions).
(because it is
It matches the pattern
Order-Perm-Right
RedP ts(G; n)
G0
in
Order-Perm-Left
if and only if it matched
in G and it matched Order-Unif in G. So RedP ts
1.
(G0; n) =
The rule neither deletes nor creates any node, the
subgraph of the other nodes is the same, and so are the points. Thus,
RedP ts(G0 ) = RedP ts(G)
1.
n cannot match any pattern in G0 , but in G it matched the
1 are created, with at most 2
Order-Perm-Left . Nodes and Order-Perm :
pattern
points. The subgraph of the other nodes remains unchanged, so do the
points. Hence,
RedP ts(G0 ) RedP ts(G)
Consec-Perm :
n matches only the pattern Apply-Perm in G0 (not Consec-Perm
because t is a skeleton
Consec-Perm
tions satised
is an
or
G.
in G0
in
node
) and it matched the patterns Apply-Perm and
The rule
Consec-Perm
are also satised in
node and matches patterns
Order-Perm-Right ,
Consec-Perm
3 + 2 = RedP ts(G) 1.
so it was in
G.
preserves weights, so condiLet
n0
Order-Unif
G.
If
n0
is a
be a parent of
and
n,
if
n
Order-Perm-Left ,
node, and matches
or Apply-Perm , so it was in G. Otherwise n matches nothing.
Other subgraphs are not modied. Hence
RedP ts(G0 ) = RedP ts(G)
1.
As a corollary, we obtain the termination property for reduction:
Corollary 1
The graph rewriting system dened by the simplication, ordering
and compacting rules is terminating. The normalisation process terminates in at
most
RedP ts(G) steps.
We can give an upper bound on
RedP ts(G), that is, a bound on the number
of reduction steps needed to compute a normal form (i.e., an irreducible graph):
104
8.1 A polynomial Algorithm via graph rewriting
Proposition 25 RedP ts(G) 2Nb(G; ) + 4Nb(G; ) where Nb(G; )
number of nodes in G and Nb(G; ) is the number of nodes in G.
is the
nodes have at most 2 points, nodes at most 4, other nodes have
no points.
Proof
Corollary 2
The number of normalisation steps for a graph
nominal problem is at most
2Nb(G; ) + 4Nb(G; ).
We will now compute a bound on
Nb(G; ).
G
representing a
This will be needed to evaluate
the cost of the unication algorithm. First we introduce some notation:
Denition 61 We denote by App(G) the set of nodes in the graph G (so
Nb(G; ) = jApp(G)j), P ortssk (G) (resp. P orts (G)) denotes the set of all the
ports of skeleton nodes (resp. nodes) in G, P ortssk; (G) denotes the set of all
the ports of skeleton and nodes in G, and P ortssk; (G) denotes the set of all
the ports of skeleton nodes and nodes in G that are attached to an node.
Proposition 26 Let G be a normal form of a graph representing a problem P r.
Then Nb(G; ) = jApp(G)j jP ortssk; (G)j.
: P ortssk; ! App(G) be the function that for each port in
P ortssk; (G) returns the node attached to it. We can show that f is surjective as
follows: Each node n has at least one parent (otherwise it is garbage collected);
let np be a parent of n, then np is not an node (otherwise we would have two
Proof
Let
f
consecutive applications of permutations, which contradicts the fact that
G is in
normal form). Thus, np is either a skeleton node or a node. Let p be the port
of np to which n is attached. Then p is in P ortssk; G and f p
n. Therefore
( )
jApp(G)j = jIm(f )j jP ortssk;(G)j P ortssk;(G)j.
( )=
Note that in the unication algorithm it is not necessary to reduce the full
graph to normal form. In fact, in our implementation we normalise locally, reducing only the subgraphs that are relevant for the application of an
rule.
The local normalisation process terminates too, and it has a smaller cost.
105
8.1 A polynomial Algorithm via graph rewriting
-rules
8.1.1.3
We will now solve -equality constraints using -rules which apply to normalised
graphs. Before giving the rules, it is useful to characterise graphs in normal form.
Proposition 27
The normal forms of graphs representing nominal terms are the
graph representation of terms generated by the following grammar:
Tnf
Tnf:np
Tnf:cons
Tnf:val
Tnf:var
Tnf:npvar
::= (Tnf:consjTnf:var ) j Tnf:np
::= Tnf:cons j Tnf:val j Tnf:var
::= f (Tnf ) j (Tnf1 ; : : : ; Tnfn ) j [a]Tnf
::= a j ()
::= X
::= Tnf:cons j Tnf:val
6= Id
n 6= 0
nf stands for normal form, np for non permutation, cons for term-constructor,
val for value, var for variable and npvar for not variable-or-permutation.
Here
Proof
A normal form is irreducible, so it cannot contain
Id
, consecutive
permutations, or permutations acting on value nodes.
Proposition 28
GR nf
Proof
-constraints are generated by:
::= (Tnf:var Tnf ) j (Tnf:npvar ( j )Tnf:npvar )
The normal forms of
By inspection of the rules: A constraint in normal form is irreducible,
so its left-hand side cannot be a permutation application. If we have a variable
right-hand side (possibly with one permutation) then we must have a variable on
the left-hand side. Note that we can have at most one application of a permutation
in the right-hand side (otherwise the rule
Denition 62
Two term nodes
unify a graph rooted by
n1
n1
and
Consec-Perm
n2
would apply).
are incompatible if it is impossible to
with a graph rooted by
n2 .
n1 , n2 are incompatible if they are labelled by two
dierent, non-variable, term constructors (e.g., dierent atoms a; b; an atom and
In other words, two nodes
an abstraction; two dierent function symbols; a function and an abstraction;
etc.).
We divide the rules into two groups: simplication and propagation rules.
Simplication rules have priority.
106
8.1 A polynomial Algorithm via graph rewriting
-simplication rules
Rule Id-Unif erases trivial equations:
) ;
t
Rule
?-Unif
detects failure due to incompatibility:
s
if
Rule
Same-Term
t
) ?
s and t are incompatible
solves equations where both sides involve the same term
s, with a suspended permutation on the right-hand side:
s
Note that
#
) supp() s
supp() may be empty; we deal with freshness constraints later.
-propagation rules
Each application of a propagation rule will be combined with the meta rule
Redirect :
107
8.1 A polynomial Algorithm via graph rewriting
P
s
t
P
)
s
t
t has s as a child.
Here P is any parent node of s.
if no node in
The aim of this rule is to move all the pointers to the left-hand side of the
equation (except the pointer from the root of the equation) towards the right-
s t, then we can replace any occurrence of s by
t. However, this operation should not create a cycle; in other words, if s and t
are -equivalent then s cannot be a subterm of t. We call the latter a generalised
hand side. The idea is that if
occur check.
If
Redirect
cannot be applied because a node in
equation has no solution, and we rewrite it to
s
if a node in
t
)
?:
t has s as a child,
then the
?
..
.
t has s as a child (generalised occur-check).
We will assume that each time an -propagation rule R has been selected to
be applied to an equation, the
that the
Redirect
Redirect
rule is applied
rule does not change the terms
redirected to an -equivalent term.
Rule
Subst :
X
t
)
108
X
applying
R.
Note
s and t, only pointers to s are
The propagation rules are:
before
t
8.1 A polynomial Algorithm via graph rewriting
There is no need to perform an occur check in the rule
done by
Subst ,
since it is
Redirect .
Recall that once this rule is selected to be applied, all pointers to
left are redirected to
t by the Redirect
rule (provided
X
X
in the
does not occur in
t), which corresponds to applying the substitution [X 7! t] in the graph.
Rule
Fct-Unif
:
Rule
f
f
s
t
)
s
t
Abs-Unif-Same :
[]
a
Rule
Abs-Unif-Di
)
[]
s
t
:
109
s
t
8.1 A polynomial Algorithm via graph rewriting
[]
)
[]
a
s
s
if
Rule
Tpl-Unif
t
b
#
(ab)
t
...
a
a 6= b
:
()
t1
()
t1
...
s1
)
sn
...
t1
s1
tn
sn
Here if the tuple has arity 0, the right-hand side is empty (i.e., the constraint
is eliminated).
Notice that the rules
Abs-Unif-Di
and
Tpl-Unif
rewrite a constraint into a
set of constraints. The graph in the right-hand side has two roots in the case
of
Abs-Unif-Di
, and may have zero or more roots in the case of
Tpl-Unif
.
According to the denition of graph rewriting, in a reduction step using one of
these rules, each pointer to the root of the redex is replaced by a set of pointers
to the roots of the right-hand side. Since the parent node of
is P r, which has
a variable arity, these rules are well-dened.
Before showing that the -simplication and -propagation rules are sound,
we introduce a rule to deal with suspended permutations that prevent the application of the rules above.
110
8.1 A polynomial Algorithm via graph rewriting
8.1.1.4 Computing permutations: Neutralisation
In order to be able to match the left-hand side of an
-rule, we may need to
(partially) compute a permutation in a normalised graph. We will do this in
a lazy way, moving the permutation down the term only the minimum that is
needed to match an
-rule.
However, due to sharing, such propagations have
to be computed carefully. Consider the following example:
M
u f
where
M
t
represents any node pointing towards . If we apply directly
on t, we
obtain:
f
u
which is incorrect (f
apply
on
t
t
becomes
M
t
f t).
and redirect parents of
( 1(t)) as follows:
However, since
t
= 1(t), we can
towards (t) and parents of t towards
u
f
1
111
M
t
8.1 A polynomial Algorithm via graph rewriting
This is the basis of the
neutralisation
Denition 63 (Neutralisation rule)
of n is dened by:
rule dened below.
n be a non leaf node.
Let
M
Neutralisation
N
N
) n
n1
The
...
M
1
n
nn
The neutralisation rule will only be applied to
...
n1
nn
t when it occurs immediately below
node, as shown in Figure 8.1 below. This rule is used only to trigger the
application of an -rule in a normalised graph, by bringing a term constructor
up to match the left-hand side of an -rule.
an
rule can be applied, the neutralIt is easy to see that after applying this rule, the node In the graph shown in Figure 8.1, if no
isation rule applies.
will point to
s and to the root of t.
Proposition 29 If G is a graph representing a nominal problem, and G nor0
0
malises to G , then either there are no nodes in G or an - rule can be
applied, possibly preceded by an application of Neutralisation.
s
t
Figure 8.1: Graphs where neutralisation may be applied
112
8.1 A polynomial Algorithm via graph rewriting
G0 contains an node. By Proposition 28, this
node is the root of a subgraph G00 representing a constraint s t, and we can
Suppose the graph
Proof
distinguish two cases:
If t is not of the form v , then using Propositions 27 and 28 we can check that
rule for each kind of normal form (i.e., G00 matches the left-hand
side of an -rule).
there is an
If G00 is a graph as depicted in Figure 8.1 (note that s and t must be dierent,
otherwise the graph would not be normalised since rule Same-Term applies) then
will point to s and to the root of t, and again
using Proposition 28 we can check that the graph can be reduced by an -rule.
after applying neutralisation,
rules) When applied to a normalised graph
representing a nominal problem, all the rules, as well as Neutralisation, pre-
Proposition 30 (Correctness of
serve the solutions.
Proof
First notice that the
-simplication rules deal with 'simple' equations
(constraints of the form t t, which are trivially solvable and can be eliminated;
equations between incompatible terms, which have no solution and thus are re-
?; and equations of the form t t, which have solutions only if
the atoms aected by are fresh in t). It is easy to see that the -propagation
placed by
rules follow the format of the rules given in Section 4. The only interesting case
is
Subst ,
by
which replaces the
node by a node representing a substitution, or
? (depending on the result of the occur check).
In the rst case, the meta-
rule Redirect performs the substitution by updating the relevant pointers. Rule
Redirect
generates
? only if the equation has no solution.
8.1.1.5 Putting all together
In the rst phase of the algorithm to solve nominal unication problems, we solve
constraints in the graph representation of the problem, using Normalisation
rules, -rules, and the Neutralisation rule. We deal with freshness constraints in
the second phase of the algorithm. More precisely, the rst phase of the algorithm
can be described as follows: we start by normalising the graph
113
G0
representing
8.1 A polynomial Algorithm via graph rewriting
the initial nominal problem and then we apply cycles of -rules followed by nor-
malisation, until no constraints are left (if necessary, we use the Neutralisation
rule before applying an
-rule). The algorithm is given below in pseudo-code:
algorithm:
Normalise
G
while G has at least one node do
if G is irreducible for -rules then
Apply
Neutralisation
end if
Apply an
Normalise
end while
on G
-rule on G
G
If at any point
? is generated, we raise an exception since this means that
the problem has no solution.
Below we show that the algorithm above terminates and all the
eventually eliminated, obtaining
nodes are
? or the graph representation of a substitution
and a set of freshness constraints. We analyse the cost of the algorithm in terms
of number of iterations, and reduction steps.
8.1.1.6 An upper bound on the number of iterations
We use a measure based on the arities of nodes (note that the number of nodes
does not necessarily decrease with each rule application, because of sharing and
because rule
Tpl-Unif
creates several
nodes).
Denition 64 Let G be a nominal graph and n a node in G.
P
ResSize(G) = n2G ResSize(n), where ResSize(n) is dened by:
8
>
>
<0
ResSize(n) = 2 arity(n)
>
>
:arity(n)
114
if n is a
if n is a
, , # node
tuple node
otherwise
8.1 A polynomial Algorithm via graph rewriting
For example,
ResSize( ) = 2, ResSize(?) = 0.
Remark 6 ResSize(n) depends only on n (not G).
Proposition 31 Let G be a graph and G0 be the graph
-rule on G. Then ResSize(G0) < ResSize(G).
Proof
Let
n
obtained by applying an
be the root of the subgraph on which the rule is applied, we
consider each rule:
Id-Unif
?-Unif , Subst , Same-Term delete n (that is, an -node of arity
2), and create only nodes of null ResSize(). Therefore ResSize(G0 ) ResSize(G) ResSize(n) = ResSize(G) 2.
Fct-Unif
,
, Abs-Unif-Same and Abs-Unif-Di only create nodes of null ResSize.
[]
They do not erase the two roots (f or , resp.) because there may be other
pointers to these nodes. Let
n.
Redirect ,
l
(resp. r) be the left (resp. right) child of
which is combined with each rule, transforms parents of l into
parents of r so that after applying the rule l becomes useless and is garbage-
(l) > 0, except if l is a 0-ary function, in which
case the -node n is erased. Hence ResSize(G0 ) < ResSize(G).
Tpl-Unif : Let m be the arity of the tuple. If m = 0 then the -node n is
erased. Otherwise, this rule creates m 1 -nodes but as in the previous
case the left tuple-node (l) becomes useless and so is erased. ResSize(G0 ) =
ResSize(G) + (m 1) ResSize(n) ResSize(l) = ResSize(G) + 2m
2 2m = ResSize(G) 2.
collected. Note that ResSize
G0 be the graph obtained by applying
0
normalisation or neutralisation rule on G. Then ResSize(G ) ResSize(G).
Proposition 32
Proof
Let
G
be a graph and
For each rule, only nodes of null
Proposition 33
There are at most
ResSize are created.
ResSize(G0 ) iterations of the while-loop.
115
a
8.1 A polynomial Algorithm via graph rewriting
Proof
ResSize(G), and neither does
Also, -rules decrease ResSize(G) by Propo-
Normalisation rules do not increase
Neutralisation ,
by Proposition 32.
sition 31, and by Proposition 29 one of these rules is applicable in each iteration
of the while-loop, hence
ResSize(G) decreases.
-rules terminate on their own (but in the algorithm we apply only one step of -reduction
As a corollary of Proposition 31 we can also deduce that the
in each iteration of the while-loop).
8.1.1.7 An upper bound on the number of normalisation steps
Using Proposition 2, we know that in each iteration of the while-loop there are
at most
2Nb(G; ) + 4Nb(G; ) normalisation steps.
upper bound which depends only on the initial graph
We will now compute an
G0 .
Denition 65 MaxArity(G) = maxfarity(n)jn 2 Gg
Proposition 34
Let
G be a nominal graph and G0
be the graph obtained by ap-
, or neutralisation rule on G. Then
MaxArity(G0 ) MaxArity(G), and
jP ortssk (G0)j jP ortssk (G)j.
plying a normalisation,
1.
2.
Proof
For the rst part: note that each node on the right-hand side of a
rule has an arity which is less than or equal to the arities of the nodes in the
left-hand side. The second part is a consequence of the fact that rules do not
create skeleton nodes.
Proposition 35
reduced version of
Let
G0
G0
be the initial graph representing a problem, and
G
a
at any moment in the algorithm.
Nb(G; ) Nb(G0 ; ) + Nb(G0 ; ()) MaxArity(G0 ) = K :
Proof
The only rule that adds nodes in the graph is Tpl-Unif . It consumes
()) and creates at most MaxArity(G0) -nodes. No
rule creates tuple nodes, hence at most Nb(G0 ; ()) MaxArity (G0 ) nodes
can be created.
a tuple node (denoted by
116
8.1 A polynomial Algorithm via graph rewriting
Proposition 36
Let
G be a normalised graph.
Nb(G; ) jP ortssk (G0 )j + 2K :
Proof
By Proposition 26, we know that in a normalised graph,
Nb(G; ) jP ortssk;(G)j = jP ortssk (G)j + jP orts(G)j. Now using Propositions 34 and 35
we deduce Nb(G; ) jP ortssk (G0 )j + 2K .
Proposition 37 Let G be a normalised graph. By applying an -rule and/or
Neutralisation, at most MaxArity (G0 ) + 1 -nodes are created.
Proof
By inspection of the rules we see that at most MaxArity (G) + 1
nodes are created, and using Proposition 34 we deduce that MaxArity (G) + 1 MaxArity(G0 ) + 1.
Proposition 38
1.
At any step in the iteration:
Nb(G; ) jP ortssk (G0 )j + 2K + MaxArity(G0 ) + 1 = K .
2. Let
jGj denote the size of the graph.
jGj K + 2K + ResSize(G0) + Nb(G0; sk) = Kjj
Proof
1. In each iteration, we start with a normalised graph
and apply at most
-rule (possibly with an application of Neutralisation ), before normalising again. Since G is normalised, by Proposition 38 Nb(G; ) jP ortssk (G0)j + 2K, and by applying a neutralisation and rule, at
most (MaxArity (G0 ) + 1) -nodes are created (by Proposition 37). There
are then at most jP ortssk (G0 )j + 2K + MaxArity (G0 ) + 1 -nodes.
We count the number of nodes of each kind: #-nodes and -nodes are
only created by -rules, one at a time, hence Nb(G; #) + Nb(G; ) ResSize(G0 ). Since every permutation is attached to a -node, Nb(G; ) Nb(G; ). Hence:
jGj = Nb(G; ) + (Nb(G; #) + Nb(G; )) + Nb(G; sk)+
Nb(G; ) + Nb(G; )
K + ResSize(G0) + Nb(G0; sk) + 2K
one
2.
G
117
8.1 A polynomial Algorithm via graph rewriting
The constant
K
N
dened in the proposition below is an upper bound on the
number of normalisation steps in each iteration of the algorithm.
Proposition 39
There are at most
2K +4K = KN normalisation steps in each
iteration of the while-loop.
Proof
By Proposition 2, when we normalise the graph in each iteration of
2Nb(G; )+4Nb(G; ) steps, hence (by Propositions 35 and 38) we obtain an upper bound: 2K + 4K = K .
the while-loop there are at most
N
We have therefore an upper bound for the number of iterations, and the num-
ber of normalisation steps, which shows that the algorithm is indeed terminating.
Below we will analyse the cost of each operation, in order to establish the complexity of the algorithm, but we can already show its correctness.
Proposition 40 (Soundness and completeness of -solving)
The algorithm
dened above is sound and complete.
Proof
Consequence of Propositions 21 and 30, together with Propositions 39 and
33, which entail the termination of the loop.
At the end of the rst phase of the unication algorithm no
-constraints
remain, and provided that ? has not been generated, the second phase can start.
At this point, the only children of the node
introduced by the
rules.
P r will be either # nodes or nodes
8.1.1.8 Cost of the rules
Let A0 be the set of atoms in the initial problem. A permutation is implemented
as an AVL tree of pairs
(a; (a)) for a 2 supp().
The algorithm does not introduce new atoms in the problem, so at each step
there are at most jA0 j atoms in G, and in particular in permutations in G. Thus,
(a) in 's AVL, which can be done in
O(log(jA0 j)) . Inversion and union of permutations are also O(jA0 j log(jA0 j)).
applying to a requires searching the value 1
Below we calculate the cost of applying each normalisation rule:
1 We
follow the notations of [49], to which we refer the reader for a denition of O.
118
8.1 A polynomial Algorithm via graph rewriting
Rule Apply-Perm : Since we use AVLs to represent permutations, computing
t for a value node t has a cost O(log(jA0 j)).
Rule
Id-Perm
eliminates identity permutations, its cost is constant:
Rule
Order-Unif
also has a constant cost:
O(1)
Rule Order-Perm involves the computation of the inverse of a permutation.
This can be done in
O(1)
Rule
Consec-Perm
O(jA0 j log(jA0 j)) with AVLs.
composes consecutive permutations. Again the cost is
O(jA0 j log(jA0 j)) with AVLs as permutations.
Hence, applying a normalisation rule is at most in
We now turn our attention to
rules.
O(jA0 j log(jA0 j)).
First note that some of these rules
require a redirection of all parents of a node to another node. This operation
is linear in the number of parents, so linear in
jGj.
The generalised occur check
(i.e., checking whether a node in t has s as a child, see below) is also linear in the
size of t, therefore linear in
jGj.
P
s
if
no node in
t
)
P
s
t
t has s as a child (generalised occur-check),
otherwise the right-hand side is
Cost:
Rule
Same-Term
Rule
Subst
Rule
Id-Unif
at most
?.
jGj
erases trivial equations, its cost is constant:
O(1)
Rule ?-Unif detects incompatibility failure, again its cost is constant:
O(1)
requires the computation of the support of . The cost is
O(jA0 j log(jA0 j)) with permutations as AVLs.
creates a substitution, its cost is constant:
119
O(1)
8.1 A polynomial Algorithm via graph rewriting
Rules
Fct-Unif
,
Abs-Unif-Same
operations, with constant cost
Rule
Tpl-Unif
tuple:
and
O(1).
Abs-Unif-Di
involve simple pointer
has a cost proportional to the number of elements in the
O(arity of the tuple), that is O(MaxArity(G0 )).
(max(jGj+MaxArity(G0); jA0j
log(jA0 j))) O(jGj + MaxArity(G0 ) + jA0 j log(jA0 j)).
Note that MaxArity(G0 ) Kjj and jGj is bounded by Kjj , therefore the cost
is at most O(jA0 j log (jA0 j) + Kjj ).
Hence, the cost of applying an rule is at most O
Finally, we consider an application of the Neutralisation rule:
M
N
N
) n
n1
...
M
1
n
nn
...
n1
nn
Cost: O(jGj + MaxArity(G0 ) + jA0 j log(jA0 j))
(jGj + MaxArity(G0)+
jA0j log(jA0j)), hence again it is O(jA0j log(jA0j) + Kjj).
Proposition 41 This phase is O(ResSize(G0 ) (Kjj + KN jA0 j log(jA0 j))).
The application of an rule or Neutralisation is in O
Proof
For each iteration, by Proposition 39, there are at most
K
N
steps of
(jA0jlog(jA0j)). In addition, one -rule is applied,
and at most one neutralisation step, each in O(jA0 j log (jA0 j) + Kjj ). The cost
of an iteration is then O(K jA0 j log (jA0 j) + 2(jA0 j log (jA0 j) + Kjj ), or,
simplifying, O(K jA0 j log (jA0 j) + Kjj ).
Because there are, by Proposition 33, at most ResSize(G0 ) iterations, the
loop is in O(ResSize(G0 ) (Kjj + K jA0 j log (jA0 j))). The normalisation
phase before entering the loop is also O(K jA0 j log (jA0 j)) and hence does
not change the complexity of the algorithm.
normalisation, each one in O
N
N
N
N
120
8.1 A polynomial Algorithm via graph rewriting
8.1.2 Freshness constraints
The algorithm to solve nominal problems has two phases, as mentioned above.
The rst phase deals with
constraints, as described in the previous section.
Assuming the problem has a solution, at the end of the rst phase we are left
#t (where A is a set of atoms, see
rule Same-Term in Section 8.1.1.3) and a substitution of the form [Xi 7! ti ]i2I .
with a set of freshness constraints of the form A
We describe in this section an algorithm to check that a set of atoms is fresh in
a term t. The idea is to traverse the graph representing the term, and annotate
term nodes with the set of atoms for which freshness has already been checked
(to avoid repeated computations).
Denition 66 (Annotated terms)
Given a graph representing a term
t,
we
annotate each term node by a set of atoms, and denote the set of atoms attached
to the node
n by (n).
Initially, all term nodes are annotated with the empty set
of atoms.
Denition 67
Let
G
be the graph representing a problem
subgraph representing a freshness constraint
t where n is its root node.
One step of freshness checking for
(fresh)
where the function
A#n
freshl
)
(
G0
A#t and G00
P r.
Let
G0
the graph representing
is dened by the following rule:
freshl (A n (n) # n)
with side eect (n) := (n) [ A
is dened as follows (we assume
freshl (; # n)
freshl (A # f (n))
freshl (A # (ni )i=1:::m )
freshl (A # [a]n)
freshl (A # n)
freshl (A # X )
=
=
=
=
=
=
freshl (A # a)
=
121
;
be the
A 6= ;):
A#n
(A # ni)i=1:::m
(A n a) # n
1 (A) # n
A
8 #! X
<; a 62 A
:? a 2 A
8.1 A polynomial Algorithm via graph rewriting
Note that when freshl is applied to A
# X we generate a new constraint A #! X
which will be part of the solution to the problem.
Denition 68 Let G be the graph representation of a nominal problem P r. As0
0
sume G is the graph obtained after running the rst phase of the algorithm and G
does not contain
the rule
?.
The algorithm to check freshness constraints simply applies
(fresh) dened above until the graph is irreducible.
Proposition 42 (Irreducible forms) Irreducible graphs do not contain # nodes.
Only freshness constraints of the form A #! X may remain.
Proof
If there is a
Proposition 43
Proof
# node, rule (fresh) can be applied.
The freshness check is correct.
(
)
The rule fresh memorises the freshness information that has already
been checked (or is being checked), to prevent checking multiple times that the
same atom is fresh in the same term. Formally, checking
A
# t is equivalent
(a # t)a2A. So checking A # t; B # t is equivalent to checking
(AnB ) # t; (A\B ) # t; (B nA) # t; (B \A) # t. Although we have the constraint
(B \ A) # t twice, we only need to check it once, so we do A # t; (B n A) # t.
to checking
freshl follows the inductive denition of the freshness predicate (see Section 4). When the propagation reaches a variable (A # X ) we save
this resolved constraint as A #! X so that (fresh) can no longer be applied.
The denition of
Proposition 44
The freshness check terminates in at most
2 jGj jA0j steps
of rewriting.
Proof
We compute the maximum number of steps by counting the number of
times that an atom is checked for freshness in a given term node in this phase.
A = ; or if we have already checked all atoms in A, i.e. A n (n) = ;,
constraint is simply removed. Let jGjtn be the number of term nodes in
Note that if
the
G,
term nodes are never created in this phase. Thanks to annotations there is
jA0j steps of (fresh) on n where A n (n) 6= ; so
jA0jjGjtn application of (fresh) where A n (n) 6= ;. An empty set can either be
created in the previous phase, or be generated by freshl (A # [a]n); hence there
for each term node
n
at most
122
8.1 A polynomial Algorithm via graph rewriting
Nb(G; #) + jA0 j jGjtn applications of (fresh) where A n (n) = ;.
Therefore, we have at most Nb(G; #) + 2 jA0 j jGjtn 2 jA0 j jGj steps of
are at most
rewriting.
Before considering the cost of the freshness check, we can state the following
property, which is a direct consequence of Propositions 40 and 43:
Proposition 45
The algorithm to solve nominal problems by
1. transforming the problem into a graph;
2. applying the rst phase to eliminate
constraints;
3. applying the second phase to eliminate freshness constraints;
is sound and complete.
8.1.3 Cost
We now analyse the complexity of the second phase in the algorithm (freshness
checking). We start by analysing the cost of
freshl
for each case:
freshl (; # n)
: O(1)
freshl (A # f (n))
: O(1)
freshl (A # (ni )i=1:::m ) : O(m)
freshl (A # [a]n)
: O(log(jA0j)) since sets are represented as AVL trees
freshl (A # n)
: O((jA0j log(jA0j))2); we discuss this case below
freshl (A # X )
: O(1)
freshl (A # a)
: O(log(jA0j)) due to the use of AVL trees
The cost of freshl (A # n) is due to the computation of 1 in O(jA0 j log(jA0 j)) and the application of 1 to A in O(jA0 j log(jA0 j)) too.
Proposition 46
Freshness checking is
(
O(jGj jA0 j3 log(jA0 j)2 ).
)
freshl and the update
of (n), which is a union of sets and can be done in O(jA0 j log (jA0 j)). We deduce
that each step of rewriting with these rules is at most in O((jA0 j log (jA0 j))2 ).
Since there are at most 2 jGj jA0 j steps of rewriting by Proposition 44, the
Proof
A rewrite step of fresh consists of one step of
result follows.
123
8.1 A polynomial Algorithm via graph rewriting
8.1.4 Total cost in time for the unication algorithm
An upper bound on the total cost of the unication algorithm can be obtained
by adding the cost of the phase of resolution of
constraints and the phase of
freshness checking.
Proposition 47
The following is an upper bound for the unication algorithm:
O(ResSize(G0 ) (Kjj + KN jA0 j log(jA0 j)) + Kjj jA0 j3 log(jA0 j)2 )
G for the freshness algorithm is the output of the resolution algorithm, hence jGj Kjj . The result is then obtained by adding the
Proof
The input graph
two bounds (see Propositions 41 and 46).
To simplify the formula above, we use the following properties:
Proposition 48
1.
ResSize(G0 ) = O(jG0 j MaxArity(G0 ))
2.
K = O(jG0 j MaxArity(G0 ))
3.
K = O(jG0 j MaxArity(G0 ))
4.
Kjj = O(jG0 j MaxArity(G0 ))
5.
KN = O(jG0 j MaxArity(G0 ))
Proof
1. For each node n in G0 , ResSize
2.
3.
4.
(n) 2MaxArity(G0) then ResSize(G0) 2jG0j MaxArity(G0).
K = Nb(G0 ; ) + Nb(G0 ; ()) MaxArity(G0 )
jG0j + jG0j MaxArity(G0) = O(jG0j MaxArity(G0)).
K = jP ortssk (G0 )j + 2K + MaxArity(G0 ) + 1.
jP ortssk (G0)j jG0j MaxArity(G0) because the arity of each node is less
than or equal to MaxArity (G0 ).
Kjj = K +2K +ResSize(G0 )+Nb(G0 ; sk) and each element in the addition
is bounded by O(2jG0 j MaxArity (G0 )).
124
8.1 A polynomial Algorithm via graph rewriting
5.
K = 2K + 4K and again
O(2jG0 j MaxArity(G0 )).
N
each element in the addition is bounded by
Proposition 49 (Cost in time for the unication algorithm)
The follow-
ing is also an upper bound for the unication algorithm:
O(K 4 log2 (K ))
where
K = jG0 j MaxArity(G0 )
Proof
jG0j K .
Consequence of Propositions 47 and 48, using the fact that
125
jA0j 8.1 A polynomial Algorithm via graph rewriting
3
We have presented algorithms to solve nominal unication.
QNU
is the most
ecient but as it has been seen, it is not easy to use in into a larger program.
Though being a bit less ecient that
QNU, AQNU
is simple and can easily be
part of a larger program, which is nice in practice. The graph rewriting algorithm
is the less ecient but shows an interesting way of solving nominal equivalence.
QNU
and
AQNU
extend the corresponding algorithms on rst-order terms
to nominal terms without modifying the structure of the algorithm but only
adding name management. This management adds an extra cost which depends
on the implementation of atoms, permutations and sets of atoms. Generally, the
complexity of the extended algorithm is the product of CA and the complexity of
the original algorithm.
Finally, we presented a concrete implementation of
126
QNU
and
AQNU.
Part III
-equivalence, Matching and
Rewriting
127
Let
P r = fs tg be a nominal -equivalence (resp matching) problem. P r
can be solved by considering it as a nominal unication problem whose solutions
(; ) have to satisfy that the domain of (dom() is empty (resp. dom() \
V(s) = ;). However, in some cases, some optimisations can lead to more ecient
nominal -equivalence and matching algorithms.
This part presents a very modular algorithm which adapts itself to the input
problem to reach better eciency. This algorithm is based on the one we presented in [10]. The implementation has been completely rethought and is now
using the ideas presented in the chapter 3 and streams. This implementation is
better than the previous one on many points:
it is as close as possible to the abstract description of the algorithm.
it completely abstracts the actual implementation of permutations and sets.
it is extremely modular.
it is much easier to read, understand and to use.
The implementation in
Haskell
is in appendices A.6, A.9 and A.10.
Chapter 9 introduces the notions of
environment and presents the algorithm
in an abstract way. Then chapter 10 describes the implementation.
128
Chapter 9
A modular algorithm to check
-equivalence and solve matching
constraints
Ecient implementations of nominal unication rely on the use of lazy permutations: permutations are only pushed down a term when this is needed to apply
a transformation rule, and then, they are only pushed one level down the term.
Since lazy permutations may grow (they accumulate), in order to obtain an ecient algorithm we will compose the swappings eagerly. The key idea is to work
with a single
current
permutation, represented by an
environment.
The algorithms to check -equivalence constraints and to solve matching prob-
lems (linear or non-linear) will be built in a modular way. The core module is
composed of four phases and is common to both algorithms; only the nal phase
will be specic to matching or -equivalence.
9.1 Environments
We begin by introducing the notion of an environment. Environments will be
associated to terms and used to store a permutation and a set of atoms.
Denition 69 Let s and t be terms, be a permutation, and A be a nite set
of atoms. An environment is a pair (; A). We denote by the permutation
129
9.1 Environments
A the set of atoms) of an environment. We write s t
s t; A # t and call s t an environment constraint.
(resp.
X b
A = fag represents the problem X (a b)b; a#b.
For example, the environment constraint
Denition 70
An
where to represent
= (a b) and
environment problem P r is either ? or has the form
s1 1 t1 ; : : : ; sn n tn
iti (1 i n) are environment constraints.
n
abbreviate it as (si i ti )1 .
where
si
Denition 71
We will sometimes
The problems dened in Section 4 will be called
standard to dis-
tinguish them from environment problems (standard problems have no environ-
standard form of an environment problem is obtained by applying
ments). The
the rule:
s t =) s t; A # t
as many times as possible. We denote by
ment problem
P r.
[ P r] the standard form of an environ-
Thus:
[ s1 1t1; : : : ; sn ntn] =
s1 (1 ) t1 ; (1 )A # t1 ; : : : ; sn (n ) tn ; (n )A # tn
This rule is terminating because it consumes a
each time, without creating
any. There are no superpositions, so the system is locally conuent and because
it terminates it is conuent [38]. Therefore the standard form of an environment
problem exists and is unique.
Denition 72
The
solutions of an environment problem are the solutions of its
standard form (see Section 4). A problem
problems are
equivalent
? has no solutions. Two environment
if their standard forms are equivalent, i.e., have the
same solutions.
As a consequence of Denition 72, two equivalent environment problems have
the same set of solutions.
130
9.2 Core algorithm
The set of reduction rules on standard problems given in Section 4 transform
a problem into an equivalent one (i.e., solutions are preserved, see [51]). Below we
will give a set of transformation rules for environment problems, and we will prove
that a step of rewriting P r
=) P r0 on an environment problem can be simulated
by some steps of reduction on its standard form using the rules in Section 4, that
is,
[ P r] =)
[ P r0] . Therefore P r and P r0 are equivalent.
It is easy to translate standard problems into environment problems, as shown
below.
Denition 73
The translation
Env
from standard problems into environment
problems is inductive, with base cases:
Env(s t) = s t where = (Id; ;) and
Env(A # t) = t t where = (Id; A).
This transformation is linear in time and in space. Therefore, from a (log-
)linear algorithm solving environment problems we can derive a (log-)linear algorithm to solve standard problems.
In the following sections, we restrict our attention to checking -equivalence
constraints and solving matching problems. In the latter case, in environment
s t, the term t will not be instantiated and variables in s and t
are disjoint. If right-hand sides t are ground terms, we will say that the problem
constraints
is
ground,
and
non-ground
otherwise.
9.2 Core algorithm
(si iti)n1 into
an equivalent standard problem of the form (Xi ti )i2I ; (Aj # Xj )j 2J . There
are four phases in the core algorithm. The rst one reduces constraints, by
The core of the algorithm transforms an environment problem
propagating i over ti . The second phase eliminates permutations on the lefthand side of constraints, and the third phase reduces freshness constraints, also
by propagating i over ti . Finally, the fourth phase computes the standard form
of the resulting problem.
Denition 74
P r be an environment problem.
of applying the core algorithm on P r .
Let
131
We denote by
Pr
c
the result
9.2 Core algorithm
Below we describe each phase of the core algorithm.
Phase 1
The input is an environment problem
P r = (si i ti )n1 , which we
will reduce by applying the following transformation rules.
(
if a = t and t 62 A
t =) ?P r otherwise
(
P r; (si ui )m
1 if t = (u1 ; : : : ; um )
P r; (s1 ; : : : ; sm ) t =)
otherwise
(?
P r; s u if t = f u
P r;
f s t =)
otherwise
(?
0
if t = [b]u
P r;
[a]s t =) ?P r; s u otherwise
P r;
where 0
a
= ((a b) ; (A [ f 1ag) n fbg) in the last rule, and a; b could be
the same atom.
We show below that the rules transform an environment problem into another
equivalent environment problem.
The environment problems that are irreducible for the rules above will be
called
phase 1 normal forms or ph1nf
for short.
Proposition 50 (Phase 1 normal forms) The normal forms for phase 1 rules
n
are either ? or (i Xi i si )1 where si are nominal terms and n 0.
Proof
If the left-hand side of an
constraint is an atom, function applica-
tion, tuple or abstraction, then the constraint can be reduced using the phase 1
rules. Note that if the problem is ground, the ph1nf is either
? or empty.
Proposition 51 (Correctness) Let P r be an environment problem and assume
P r =) P r0 using the phase 1 rules. Then P r and P r0 are equivalent.
Proof
To prove that
Pr
and
P r0
are equivalent we have to show that their
standard forms have the same solutions. We prove this by showing that if a
? then it has no solutions, and that if P r =) P r0 6= ?
then there is a problem Q such that [ P r] =) Q using standard reduction rules
problem reduces to
132
9.2 Core algorithm
(given in Section 4), and Q is equivalent to
[ P r0] . Since standard reduction steps
preserve solutions, this implies the equivalence of
Pr
and
P r0 .
We distinguish
cases according to the rule applied:
P r; a t =) P r0.
[ a t] = a t; A # t so if t is not
an atom or if t is an atom but a 6= t or t 2 A then there is no solution.
If t is an atom b, t = a and t 62 A , then P r0 = P r, and the reduction
Then
step can be simulated as follows:
[ P r] ; a t; A # t
[ P r] ; a a; (c # b)c2A
=)
[ P r] ; (c # b)c2A
=) [ P r]
P r; f s t =) P r0. If t is not a term of the form f u then there is no
solution. If t f u the step can be simulated by standard reduction steps
as follows:
[ P r] ; f s f u; A # f u
=) [ P r] ; s u; A # u
= [ P r; s u]
= [ P r0 ]
P r; (s1; : : : ; sn) t =) P r0. If t is not a tuple, there is no solution. If
t = (u1 ; : : : ; un ) the step can be simulated by standard steps as follows:
[ P r] ; (s1; : : : ; sn) (u1; : : : ; un); A # (u1; : : : ; un)
=) [ P r] ; (si ui)n1 ; (A # ui)n1 = [ P r; (si ui)n1 ] = [ P r0]
P r; [a]s t =) P r0. If t is not an abstraction, there is no solution. If
t [b]u, where a and b denote two atoms not necessarily dierent, then we
distinguish two cases:
= a, then
[ P r] ; [a]s [b]u ; A # [b]u
=) [ P r] ; s u; A n fbg # u
= [ P r] ; s ((a b) )u; (A [ f 1(a)g) n fbg # u
= [ P r; s 0u]
= [ P r0 ]
where 0 = ((a b) ); (A [ f 1 a)g n fbg).
If b
133
9.2 Core algorithm
=
If b 6 a, then, since freshness is preserved by permutation (i.e., it is an
equivariant relation, see [20, 51]), we obtain:
[ P r] ; [a]s [b]u ; A # [b]u
=) [ P r] ; s ((a b) )u; A n fbg # u ; a # ( u)
= [ P r] ; s ((a b) )u; (A [ f 1(a)g) n fbg # u
= [ P r; s 0u]
= [ P r0 ]
where 0 = ((a b) ); (A [ f 1 a)g n fbg).
Phase 2
This phase takes as input an environment problem in ph1nf, and
moves the permutations to the right-hand side. More precisely, given a problem
in ph1nf, we apply the rule:
X t =) X ( 1 )t
1 = ( 1 ; A ). Note that 1
X t represents X t; A #t.
where
( 6= )
Id
applies only to here, because
If the problem is irreducible (i.e., it is a normal form for the rule above), we
say it is a
phase 2 normal form, or ph2nf
Proposition 52 (Phase 2 normal forms)
for short.
Given a ph1nf problem, it has a
unique normal form for the rule above, and it is either
form
(Xi iti)1
Proof
n,
where the terms
ti
? or a problem of the
are standard nominal terms.
The rule is clearly terminating (each application consumes a permuta-
tion on the left) and it does not overlap with itself, therefore it is conuent [38].
This implies the unity of normal forms. Given a ph1nf problem, its normal form
cannot contain a suspended variable on the left-hand side.
Proposition 53 (Correctness) X t is equivalent to X ( 1 )t.
Proof
We need to show that the standard forms of both problems are equiv-
[ X t] = X t ; A # t, and [ X ( 1)t] =
X ( 1 )t ; A # t, the result follows directly from the preservation of
by permutations (see [20, 51]).
alent. Since
134
9.2 Core algorithm
Phase 3
In the phases 1 and 2 we deal with
constraints.
Phase 3 takes
a ph2nf and simplies freshness constraints, by propagating environments over
terms. Since the input is a problem in ph2nf, each constraint has the form
X t. We reduce it with the following rewrite rules, which propagate over
t and deal with problems containing ? (denoted P r[?]):
(
=) ? a aa 262 A
A
f t =) f ( t)
(t1 ; : : : ; tj ) =) ( ti )j1
[a]s =) [ a](( n fag)s)
(X ) =) ( )X
P r[?] =) ?
where n fag = ( ; A n fag) and = (( ); 1 (A )).
a
These rules move environments inside terms, so formally we need to extend the
denition of nominal term, to allow us to attach an environment at any position
inside the term. We omit the denition of terms with suspended environments,
and give just the grammar for the normal forms, which may have environments
suspended only on variable leaves:
Denition 75
The language of normal environment terms is dened by:
T = a j f T j (T ; : : : ; T ) j [a]T j X
If the problem is irreducible (i.e., it is a normal form for the rules above), we
say it is a
phase 3 normal form, or ph3nf
for short.
Proposition 54 (Phase 3 normal forms - ph3nf)
n
phase are either ? or (Xi ti )1 where ti 2 T .
Proof
The normal forms for this
By inspection of the rules, it is easy to see that environments move
down, and suspend on variables.
To give a semantics to the problems generated in phase 3, we extend the
denition of a standard form (see Denition 70) as follows:
135
9.2 Core algorithm
Denition 76 (Standard Form)
The standard form of a problem with sus-
pended environments is obtained by normalising with the rewriting rule
s C [ t] =) s C [ t]; A # t
where
s and t are nominal terms.
Remark 7
The rule in Denition 71 is a particular case of this one, taking an
empty context, that is,
C [ ] = [ ].
This rule is terminating and conuent, therefore normal forms are unique; we will
use the notation
[ P r] as before.
Proposition 55 (Correctness) Let P r be a problem in ph2nf, and P r =) P r0
0
using a phase 3 rule. Then P r and P r are equivalent.
Proof
As before, it is sucient to show that if ? is obtained then the problem
has no solutions, and otherwise the standard forms of
P r and P r0 are equivalent.
We show the three interesting cases (recall that standard reductions preserve
solutions).
Assume we have s a.
Notice that
C [ a].
Then its standard form is
[ s C [ a]]]; A #
? can only be obtained if a 2 A, and in this case the stan-
dard form has no solutions.
If
a 62 A then [ s C [ a]]]; A # a is equivalent to [ s C [ a]]].
Assume we have
s C [ [a]t].
Then its standard form is
[ s C [ [ a] t]]]; A # [a]t
which is equivalent to [ s C [ [ a] t]]]; A n fag # t, which is the standard form of s C [ [ a]( n fag)t].
Assume we have s C [ (X )]. Then its standard form is
[ s C [( )X ]]]; A # X
and using standard reduction rules we obtain [ s C [( )X ]]]; 1 (A ) #
X , equivalent to [ s C [( )X ]]].
136
9.2 Core algorithm
Phase 4
This phase computes the standard form of a ph3nf, using a particular
case of the rule given in Denition 76:
X C [ X 0 ] =) X C [ X 0 ] ; A # X 0
Denition 77
We dene the sets of variables on the left-hand (resp. right-hand)
-constraint as Vl (s t) = V(()s) (resp. Vr (s t) = V(t)). We
extend the notation to problems as follows: Vl=r (A # t) = ; and Vl=r (P r1 ; P r2 ) =
Vl=r (P r1 ) [ Vl=r (P r2 ).
side of an
Proposition 56 (Phase 4 normal forms - ph4nf) If we normalise a ph3nf
using the rule above, we obtain either ? or (Xi ui )i2I ; (Aj # Xj )j 2J where ui
are nominal terms and I; J may be empty.
Moreover, (Xi )i2I Vl (P r ) and (Xj )j 2J Vr (P r ). Thus, if the right-hand
sides of -constraints in P r are ground, there are no freshness constraints in
the ph4nf (because Vr (P r ) = ;).
Proof
By Proposition 54, environments can only be suspended on variables
in a ph3nf. So if the problem is irreducible by the rule above, it cannot contain
any environment.
The variable property follows from the fact that the rules never move subterms
-constraint into the right-hand side, and all the
freshness constraints generated involve subterms of the right-hand side.
of the left-hand side of an
Clearly, this phase preserves the set of solutions, it terminates and it does not
?, therefore if the resulting problem is ? so was the ph3nf.
Since all the reduction rules, except the rule dealing with ?, are local (i.e.
raise
only modify one constraint), the result of applying the core algorithm to a set
of constraints is the union of the results obtained for each individual constraint
(assuming
?; P r ?):
(si iti)n1 c = (si ti c)n1
Thus, without loss of generality we can consider the input of the core algorithm
to be one single constraint
s t .
137
9.3 Checking the validity of -equivalence constraints
9.3 Checking the validity of -equivalence constraints
P r of -equivalence constraints is valid, we rst run the core
c
algorithm on P r and then reduce the result P r by the following rule:
To check that a set
( )
where
(
P r ; X t =)
P r ; supp() # X
?
=
if t X
otherwise
supp() is the support of : supp() = fa j a 6= ag.
Since this rule is terminating (each application consumes one
-constraint)
and locally conuent, it is conuent [38], therefore normal forms exist and are
unique.
Denition 78
We will denote by
P r the normal form of
Pr
c
by the rule
above.
Proposition 57 (Normal forms P r ) P r is either
? or (Ai # Xi)n1 .
P r c is a ph4nf, so by Proposition 56 it is either ? or (Xi ti )n1 ; (Aj #
Xj )j 2J where ti are standard terms. While there are constraints Xi ti , the
problem is reducible by (), and each -constraint is replaced by a set of freshProof
ness constraints.
Proposition 58 (Correctness)
n
is (Ai # Xi )1 then P r
` P r.
Proof
If
P r is
? then P r is not valid.
If
P r The core algorithm preserves solutions, as a consequence of Proposi-
X t is valid (see Section 4) if and only if t is X
and supp( ) # X , because ds(; Id) = supp( ). Hence () is correct.
c
If the left-hand sides of -constraints in P r are ground, then P r = P r ;
rule () is not necessary in this case (by Prop 56). Formally:
tions 51, 53, 55. Moreover,
Proposition 59
P r = P r c .
Let
P r = (si i ti )n1
138
(si) = ; for 1 i n.
where V
Then
9.4 Solving Matching Problems
Remark 8
The rule
(), when it does not involve ?, is also local (i.e. it only
aects one constraint in the problem), so in practice we will implement two functions, one taking only
s
t as input and computing s t , and the
other taking an environment problem as input and applying the rst function on
each constraint:
(si iti)ni = (si iti )ni
In the case of ground terms, our algorithm to check -equivalence relies only
on the information stored in . Thus, it could be seen as an implementation
of the inductive denition of ground -equality used in [25] (and attributed to
Shankar [47]), which also relies on a list of pairs of names.
9.4 Solving Matching Problems
P r, we rst run the core algorithm on P r and then
c
if the problem is non-linear we normalise the result P r by the following rule.
To solve a matching problem
(?)
P r; X s ; X t =)
(
P r ; X s ; s t ?
if s t otherwise
This rule is terminating: each reduction consumes at least one
since
Pr
does not introduce
6= ?
-constraint
-constraints (by Proposition 57) and is also
terminating.
Denition 79
We denote by
P r ?
a normal form of
Pr
c
by the rule
(?).
Proposition 60 (Normal forms P r ? ) If we normalise P r c using the rule
n
m
above, we obtain either ? or (Xi ti )1 ; (Ai # Xi )1 where ti are standard terms,
n
all Xi in the equations (Xi ti )1 are dierent variables and 8i; j : Xi 62 V(tj ).
(?) does not produce -constraints. The
property then follows from Proposition 56, since V(()ti ) Vr (P r), Xi 2 Vl (P r),
and Vl (P r) \ Vr (P r) = ; in a matching problem.
n
A problem of the form (Xi ti )1 where all Xi are distinct variables and
Xi 62 V(tj ) is the coding of an idempotent substitution dened by
(
t if 9i X = Xi
(X ) = i
Proof
By Proposition 57, rule
X
otherwise.
139
9.4 Solving Matching Problems
(Ai # Xi)n1 is a freshness context . So the result of the algorithm is either
? or a pair (; ) of a substitution and a freshness context.
Proposition 61 (Correctness) P r ? is a most general solution of the matching problem P r .
Proof
Rule
(?) is justied by the equivalence:
X s; X t , X s; s t
? is raised, either:
P r c = ? and the problem has no solution by correctness of the core algoIf
rithm.
Rule
(?) raised ?, so s t = ?, i.e., s and t are not -equivalent,
and the problem has no solution.
Note that if variables occur linearly in patterns (i.e., we have a linear matching
problem), then the result of the core algorithm is already the solution of the
problem.
Formally:
Proposition 62 Let P r = (s t)
? = P r c.
once in s. Then P r
Proof
for each
X
If all variables
and rule
X
in
where for all
X
2 V(s), X
occurs only
s are linear, then P r c contains only one equation
(?) cannot be applied.
140
Chapter 10
Implementation
In this chapter we describe the implementation of the core algorithm, then the
implementation of the nal phases specic to -equivalence and matching respectively. In the sequel,
occurring in
P r0 .
P r0 will denote the input problem and A0 the set of atoms
The core algorithm is implemented using a monadic tower made of rst-class
monadic signatures as presented in chapter 3. The lower layer is a
agement layer. A
store
store
man-
is a structure associating a value to some variables.
Substitution, freshness context and
arruf
are stores. Because we want the de-
scription of the algorithm and the implementation to be as simple as possible, we
consider in the rest of the chapter that stores are arrays but it may sometimes be
easier to take another implementation of stores: for example as persistent data.
In the implementation, the actual representation of stores is abstracted and all
algorithms use the rst-class monadic signature StoreCall dened in A.4 to manage
stores.
The next layer is the exception handling monad transformer
SCont (DMonadError
error)
presented in chapter 3.5.2.
For the sake of simplicity the lifting operations required to call an operation
of a layer from an upper layer will not be shown. For a detailed implementation
of these algorithm, please refer to appendices A.4, A.6, A.9 and A.10.
141
10.1 The Freshness Layer
The complexity of the core() algorithm depends heavily on the implementation of sets and permutations. Unfortunately the appropriate implementation
choice depends on the input. So we add the two layers SetCall and PermCall dened in section 4.4. For now the tower has four layers, the lowest is StoreCall for
store management, the next one is DMonadError for exceptions, then SetCall set for
native sets and the top one is PermCall perm for native permutations. The Monad
is
1
2
3
4
SContT
( SContT
( SContT
( SCont
( PermCall
( SetCall
( DMonadError
( StoreCall
perm )
set )
)
s t o r e Var ) ) ) )
error
10.1 The Freshness Layer
In the process of the core() algorithm, freshness constraints
erated. For solving
A
#X
are gen-
-equivalence and matching problem they have to be added
to the solution if any but for rewriting they will have to be checked against the
freshness context of the rule. Once again we dene a new layer to the tower.
This layer is implemented in the
Haskell
module A.10 as the rst class monadic
signature data type FreshContextCall set where set is the native type of sets. It
provides the following calls:
FreshConstraint deals with a freshness constraint
FreshContextRule gives, for a variable
A#X
X , the set of atoms
A = f a j a # X 2 g
is the freshness context of the rewriting rule. When solving an
-equivalence or matching problem, is considered empty.
where
FreshToList returns the list corresponding to the freshness context
Let
arr an array of sets of atoms indexed by variables. arr represents the
freshness context
if and only if
arr [X ] = fa j a # X 2 g
142
10.2 Only one environment: The environment layer
When solving an
-equivalence
or matching problem, we start with an empty
context and ll it with constraints generated by the algorithm. The interpretation
of FreshContextCall for this chapter is described in the algorithm 11.
1
2
3
4
interpretFreshContextSolve(c ) = ;
switch c do
case FreshConstraint A # X
arr [X ] = arr [X ] [ A ;
k
k(())
5
case FreshContextRule X
6
k(;)
7
k
case FreshToList k
let l be the list corresponding to arr ;
k(l)
8
9
10
11
endsw
Algorithm 11: Interpretation of Freshness when Solving an -equivalence or
12
matching problem
We now have ve layers in the tower:
1
2
3
4
5
SContT
( SContT
( SContT
( SContT
( SCont
( FreshContextCall
( PermCall
( SetCall
( DMonadError
( StoreCall
set )
perm )
set )
)
s t o r e Var ) ) ) ) )
error
10.2 Only one environment: The environment layer
As it is presented above, the core algorithm uses many environments, created by
either copying (for example in the tuple case of phases 1 and 3) or modifying (for
example in abstraction case of phase 1 and 3) an existing one. Here we will use
only on environment. Modications will be done in place and copies will point
to the same environment.
143
10.2 Only one environment: The environment layer
In practice this single global environment is abstracted into the sixth monadic
layer implemented in the
Haskell
module A.9. The rst-class monadic signature
data type is EnvCall set perm where set (resp. perm) is the type of native set and
(resp. permutations) . The layer provides the following operations:
envImage of type
A ! A ! m A.
envImage a returns the image of
a by the
environment
envImageInv of type
envIsFresh of type
A ! m Bool.
envIsFresh a returns whether or not there is
a # in the environment
envSetFresh of type
the atom
envImage a returns the inverse image of
a by the environment
a constraint
A ! A ! m A.
A
! Bool ! m ().
envSetFresh a b add (resp. remove)
a from the freshness constraint set if b is true (resp.
envComposeRight of type > m ().
false).
pi perform in place
> m (). envComposeLeft pi perform in place
envLocal of type F ORALLaDOT m a ! m a. envLocal c performs the comenvComposeLeft of type
envComposeLeft
putation c locally which means that even if c modies the environment, it
will be the same as before after the call.
envPerm of type
envSet of type
m returns the permutation of mS
returns the freshness set of
A environment layer is not a simple state monad because envLocal would require to save the whole environment before computing c, which would be done
in linear time in the size of the environment but we need constant time. Instead
the monad maintains a stack of the modications done and undoes the operations on the stack when exiting envLocal. Storing these modication is cheap:
storing envComposeLeft pi (resp. envComposeRight) will only require to store a data
(ComposeLeft,
pi) what is linear in the size of pi.
envComposeLeft (resp. envComposeRight) with
To undo it we only need to call
1.
easy, we have only to remember whether or not
Storing envSetFresh a b is also
a
is in the freshness set and,
when exiting envLocal, adding (resp. removing) it if it was (resp. was not) in it.
144
10.2 Only one environment: The environment layer
Phase2 and Phase4
Phase 2 is:
X t =) X ( 1 )t
( 6= )
Id
and Phase 4:
X C [ X 0 ] =) X C [ X 0 ] ; A # X 0
.
The two functions are described in algorithm 12. Actually Phase 2 has been a
bit modied to be able to perform nominal rewriting. Because freshContextRule()
returns always
; when solving a matching problem, these modication aect nei-
ther the correctness nor the complexity of Phase 2.
Input: A suspended variable X
and a term
t
1 ;
2
3
4
5
phase2( X; t) = ;
envComposeLeft( 1 ) ;
A = freshContextRule(X );
0 = envPerm;
for a 2 (0 1 (A) do
6
envSetFresh(a,>)
7
8
end
9
;
return (X; t);
10
Input: A variable X
11 ;
12
13
14
15
16
phase4(X ) = ;
A = envSet();
= envPerm();
freshConstaint(A; # X );
return X ;
Algorithm 12: Phase 2 and 4
145
10.3 Combining the phases: Streams
10.3 Combining the phases: Streams
Modifying the environment in place requires running the phases in a specic
order. For example, let us consider the reduction of a
phase3, it is reduced into
( [ a]( n fag)s; u.
(nomAbsat; u)
by the
We can keep virtually two
dierent environment thanks to envLocal, but switching from one environment to
another would require to undo all the modication made on the rst, to apply
those of the second. It would require too many operations. It is more ecient
to reduce completely one branch, for example
the environment to reduce completely
reduction strategy.
u.
( n fag)s and only then restore
Such a reduction is called a
local
The problem is running phases one after another is not a local reduction
strategy. The idea is to interleave the phases. It could be done simply by calling
phase 2 in phase 1, phase 3 in phase 2 and phase 4 on phase 3 but this solution is
neither exible nor aesthetic. Instead we transform phase 1 and 3 into a stream.
Phase 2 and 4 do not need be transformed as they do not fork the computation.
Phase 1 and 3 become (To simplify the code, lifting operations have been hidden)
as described in algorithms 13 and 14.
streamStep is the only call of the seventh layer of the tower. The signature data
type of the layer is
1
data
StreamCall arg r e t n r = StreamStep arg ( r e t
> r)
streamStep give a value to the interpretation function and returns what the inter-
pretation send back. This can be seen as stream for which it is possible to act on
the ow. It is implemented in the
Haskell
module A.10.
Combining the phases is done by instantiating the stream layer for each function. core() is then as described in algorithm 15.
core() is also a stream because of streamStep((X; s0 )) in interpretPhase1().
The interpretation of this stream will give either the -equivalence or the matching algorithm.
146
10.3 Combining the phases: Streams
Input: Two nominal terms t
1 ;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
and
u
switch (t,u) do
case ([a]t; [b]u)
a0 = envImageInv(a );
b0 = envImage(b );
phase1(t,u ) =
envLocal(;
envSetFresh(a0 ; >) ;
envSetFresh(b; ?) ;
envComposeLeft((a b0 )) ;
phase1(t , u );
);
case ((t1 ; : : : ; tn ); (u1 ; : : : ; un ))
phase1(t1 ; u1 ) ;
: : : phase1(tn ; un )
case ((f t') , (f u')) where f is a function symbol
phase1(t0 ; u0 )
case (a; b) where a and b are two atoms
if envIsFresh(b ) then
dthrowError(freshness constraints not satisfied)
end
if a = envImage(b ) then
return ()
else
dthrowError(atoms not equal)
end
case (X; u)
streamStep(( X; u))
otherwise
dthrowError(t and
endsw
endsw
u
do not match)
Algorithm 13: Streamed version of Phase 1
147
10.3 Combining the phases: Streams
Input: A nominal term t
1 ;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
phase3(t ) = switch t do
case a where a is an atom
if envIsFresh(a) then
dthrowError(freshness constraints not satisfied)
else
envImage(a)
end
case fu ! phase3(u );
case (u1 ; : : : ; un )
s1 = phase3(u1 );
: : :;
sn = phase3(un );
return (s1 ; : : : ; sn )
case [a]u
s = envLocal(;
envSetFresh(a; ?);
phase3(u );
);
= envImage(a );
17
18
19
20
21
22
23
24
25
26
b
return [b]s
case X
envlocal(;
envComposeRight( );
streamStep(X );
);
27
28
endsw
Algorithm 14: Streamed version of Phase 3
148
10.4 -equivalence and Matching
Input: Two nominal terms t
and
u
1 ;
2
3
4
core(t,u ) = instantiate(interpretPhase1() , phase1(t,u )) ;
where;
interpretPhase3(StreamStep X k ) = k(phase4(X ));
5
;
6
phase3and4(t ) = instantiate(interpretPhase3() , phase3(t ));
7
;
8
9
10
11
12
interpretPhase1(StreamStep ( X; u0 ) k ) = ;
(X; s) = phase2(X; u0);
s0 = phase3and4(s) ;
streamStep((X; s0 ));
k(());
Algorithm 15: The core algorithm
10.4 -equivalence and Matching
Interpretation as -equivalence Solving the -equivalence problem s t
is interpreting the stream of (X; s) that core(s,t ) returns with the rule () 9.3.
The rule () is implemented as the interpretAlpha() interpretation function in
algorithm 16.
Interpretation as Matching Solving the matching problem s t is interpreting the stream of (X; s) that core(s,t ) returns with the rule (? ) 9.4. The
rule
(?) is implemented as the InterpretMatch() interpretation function in
algorithm 17. This function uses a global array representing the current substitution. Such an array is dened by:
Denition 80 arr
is an array representing the substitution
8
<Nothing
arr [X ] =
:Just t
(X ) = X
if (X ) = t and t 6= X
if
149
if and only if
10.4 -equivalence and Matching
Input: two nominal terms t and u
1 ;
2
3
4
5
alpha(t,u ) = ;
instantiate(interpretAlpha(), core(t,u )) freshContextToList()
where;
interpretAlpha((X; s)) = Switch(X; s) case (X; X )
freshConstaint(supp( ) # X )
otherwise
6
dthrowError(Substitutions
7
not allowed )
endsw
Algorithm 16: -equivalence Interpretation of core
8
Input: two nominal terms t and u
1 ;
2
3
4
5
7
8
9
10
11
12
match(t,u ) = ;
ll arr with Nothing;
instantiate(InterpretMatch, core(t,u ));
return arr ;
6
where InterpretMatch((X; s); k) = ;
if arr [X ] = Nothing then
arr [X ] = Just s
else
(X; t0) = arr [X ] ;
if j s j<j t0 j then
arr [X ] = Just s
13
end
14
;
15
alpha(s,t0 )
16
end
17
k(())
Algorithm 17: Matching Interpretation of core
150
10.5 Complexity
Choosing the implementation of Permutations and Sets
We will use
either interpret permutations and sets of atoms as either as mutable arrays or as
persistent data depending on the kind of problem to be solved:
Case
Ground
Linear
Non-linear
Alpha-equivalence
mutable arrays
persistent data
persistent data
Matching
mutable arrays
persistent data
mutable arrays
Note that when the problem is ground, the streams of all the phases are empty,
so no interpretation of StreamCall is required. Since in this case we only need to
access and update the environment, arrays are more ecient. With linear, nonground problems, we need to interpret StreamCall, and the cost is quadratic using
arrays, but log-linear using functional maps. We will discuss the non-linear case
in Section 10.5.
10.5 Complexity
In this section we analyse the complexity of the core algorithm and the nal
-equivalence and matching phases.
Let P r0 = s t be the input problem; s and t are coded as trees and
is coded as a pair of a permutation and a set of atoms (permutations being
a tuple of the actual permutation, its inverse, and its support), as discussed in
Section 10. Atoms are coded as integers, as explained above. Let MA0 be the
maximum atom in A0 (the set of atoms occurring in P r0 ). Let jtjn be the number
of nodes in the tree representing t. Let
P
be the multiset of permutations in s,
t, and jj be the size of the array representing 2 P
(or the size of the map if
(t) be the multiset of
we are dealing with non-ground problems). Finally, let MV
the occurrences of variables in t.
Core algorithm
Below we analyse the cost of the traversal of the data struc-
ture, and the cost of the operations involved in the rules.
Proposition 63
The core algorithm is linear in the size of the problem
the ground case, using mutable arrays.
using functional maps and
P r0
in
In the non-ground case it is log-linear
#(js tj + jMA0 j jtjn ) using mutable arrays.
151
10.5 Complexity
The idea of the proof is that the core algorithm is essentially a traversal of
the data structure representing the problem. Phases 1 to 3 are trivially linear
with arrays and log-linear with functional maps. Phase 4, with functional maps
is done in
Proof
#(jMA0 j) time and #(jMA0 j jtjn ) with arrays.
There are three kinds of rules in the core algorithm:
propagation rules, such as the rules in phase 1 and phase 3, except the rule
rules dealing with input permutations (i.e., permutations that occur in the
(X ) =) ( )X ;
initial problem
)X ;
P r0 ), such as phase 2 rules and the rule (X ) =) ( rules dealing with
X
to compute standard form.
The propagation rules visit each node in the tree representation of
t at most
once. They may apply at most one swapping (resp. adding/removing an atom) to
modify the environment and the same again (resp. removing/adding this atom)
to restore it. All these operations are done a constant number of times and in
constant (resp. logarithmic) time with arrays (resp. maps). Hence, they take
#(jtjn ) time with arrays and #(log(jtjn ) jtjn ) with maps.
(X ) =)
Rules dealing with permutations, i.e., phase 2 rules and the rule ( )X , compose an input permutation and the environment. This takes
#(jj) (resp. #(log(jj) jj)) time per input permutation with arrays (resp.
maps), as described in Section 4.4. They are applied at most once per input
P jj in total with arrays and P log(jj) jj with
permutation, so take
maps.
2P
2P
If the right-hand side terms are ground, only these two kind of rules can be
applied, so the algorithm is linear in the size of the problem. Otherwise, there's
also the rule dealing with
X
in phase 4 to compute standard form (i.e., the
output of the algorithm). It is applied at most once per occurrence of a variable
in t, and is done in constant time with functional maps and
space with arrays.
#(jMA0 j) time and
If the problem consists of more than one matching or -equivalence constraint,
they can be solved independently. Thus, we run the core algorithm on each of
152
10.5 Complexity
them and concatenate the results, obtaining again an algorithm of the same
complexity.
Remark 9
Our algorithm checks
-constraints and the freshness constraints
that are generated, all in one traversal of the data structure representing the
problem (relying on the environment to provide information about the freshness
constraints generated).
We have chosen to develop the algorithm in this way
because it is simpler, but it is possible to solve the
-constraints and the generated
freshness constraints separately, while keeping the same complexity.
Instead of
using an environment with a permutation and a set of atoms, we can split the
environment into two parts, one storing just a permutation and the other just
a set of atoms. In phase 1, it is sucient to reduce all
propagating any freshness constraints.
-constraints without
That is, when we reach an abstraction
[a]s [b]t, instead of adding a to the set of atoms in the environment and
propagating it, we just put a tag a # on the current node of t. At the end of the
-reduction phase, an environment (consisting just of a set of atoms) will be
t and some nodes will be tagged with freshness constraints.
It remains to propagate the environment with the set of atoms over t, adding a
to the set each time we reach a node tagged by a.
attached to the root of
To obtain an ecient algorithm it is important to avoid doing several freshness
checks of the form
(a # b)a2A,
which would require time proportional to
Instead, we should do just one check
jAj.
b 2? A, which can be done in constant time
using an array.
-constraint, after running
the core algorithm we have to normalise the problem using the rule (), as deAlpha-equivalence
To check the validity of an
scribed in Section 9.3.
If the right-hand sides of
-constraints are ground,
the core algorithm is
sucient and it is linear (ground case). Otherwise, each application of the rule
() requires to know the support of a permutation, which we do because supports
are always created with permutations and maintained when they are updated.
Thanks to the use of functional maps, the support is copied in constant time
when the permutation is copied, therefore the algorithm is also log-linear in the
size of the problem in the non-ground case.
153
10.5 Complexity
Matching
The algorithm to solve matching constraints consists of the core
algorithm, followed by a normalisation phase in which the result of the core
algorithm is reduced using a rule that deals with variables occurring multiple
times in the pattern (called
? in Section 9.4). In the case of linear matching
this rule is not needed the core algorithm is sucient.
In Section 10 we discussed the implementation of the rule ? using an array S
indexed by variables and a rule which we called Rl-Check-Subst. The construction
of
S requires the traversal of the term s and every term in the output of the core
algorithm. This is done in time proportional to the size of the output of the core
algorithm. At worst, the size is jMA0 j MV
(t)+ js tj because phase 4 can add
a suspended permutation and freshness constraints on every variable occurring
in t. Therefore the output can be quadratic in the size of the input.
Then Rl-Check-Subst will compute S
[Xi] ti for each constraint Xi ti
in the result of the core algorithm. Phase 1 to 3 are linear in its size and phase 4
has a complexity
#(jMA0 j MV(ti )), hence at worst quadratic in time in the size
of the input problem. The worst case complexity arises when phase 4 suspends
permutations on all variables, making the output terms bigger than the input
ones. Since permutations are bounded in size, in the worst case (an input problem
with no permutations but many variables and abstractions), the output of phase 4
may be quadratic in the size of the input problem. On the other hand, if the input
problem already has in each variable a permutation of size jMA0 j (i.e. variables
are 'saturated' with permutations), then, since permutations cannot grow, the
-equivalence and matching algorithms are linear even using arrays.
Note that the representation of a matching problem or an -equivalence prob-
lem using higher-order abstract syntax does saturate the variables (they have to
be applied to the set of atoms they can capture). Thus, our algorithms (whether
for ground or non-ground, linear or non-linear problems) are all linear in time
with respect to the size of the problem represented in higher-order abstract syntax. The table below summarises the results:
Case
Ground
Non-ground and linear
Non-ground and non-linear
Alpha-equivalence
linear
log-linear
log-linear
154
Matching
linear
log-linear
quadratic
10.6 Benchmarks
333
3
3
3
0.45
+33333
0.4
3333333333333
3
3
3
33333 33333
0.35
3
3333333333333
3
3
0.3
3
3333333333333
3
3
3
Time 0.25
33333333333333 + + ++ +
3
3
3
0.2
++++++++ ++ ++
3333333333
3
+
+
3
3
+
3
0.15
333333 +++++++++++++++++++
3
3
0.1
33333333++++++++++++++++++ +
3
3
3
3
0.05
3++3+3+3+3+++++++++ +
3
3
3
+
3++3+3+3+3+++++
0+
0.5
alpha
match
0
100002000030000400005000060000700008000090000100000
Size
Figure 10.1: Benchmarks
10.6 Benchmarks
The algorithms described above to check
-equivalence
and to solve ground
matching problems have also been implemented in OCAML [6], using arrays.
In Figure 10.1, we show benchmarks generated by building random solvable
ground problems (i.e., problems that do not lead to
taken by the
as
?) and measuring the time
-equivalence and matching algorithms to give the result (marked
3 and + in the graph) .
1
The benchmarks suggest that for problems of similar size, the
-equivalence
algorithm takes more time than the matching algorithm. This may be explained
by the fact that to check
-equivalence we need to traverse the whole problem,
whereas a full traversal is not always needed to solve a matching problem. Note
that, unlike rst-order matching, nominal matching might produce freshness constraints that have to be checked by traversing the term.
1 The
program is available from: www.dcs.kcl.ac.uk/staff/maribel/CANS
155
Chapter 11
A Simple Nominal Rewriting
Framework
In this chapter, we recall the denition of nominal rewriting systems [20] and we
show that by only changing the interpretation of the freshness context layer, the
nominal matching algorithm can become a nominal rewriting algorithm.
11.1 Nominal rewriting
nominal rewriting rule is a tuple (rl ; l; r), written rl ` l ! r, where rl
is a freshness context and l,r are nominal terms such that V (r; rl ) V (l).
A
Intuitively, in a rewriting rule, variables represent unknown terms and the
rewrite relation is generated by instantiating the variables in the rules using
substitutions. For example, the two rules
a#X ` ([a]X )Y
!X
and
a #Y
` ([a]Y )X ! Y
have dierent syntax but should generate the same rewrite relation. Atoms are
R(a b) for that rule obtained
by swapping a and b in the rule R throughout. For example, if R a#X `
[a]X ! X then R(a b) b#X ` [b]X ! X . In general, we write R for that rule
obtained by applying the permutation to the atoms in R.
A set of rewrite rules is equivariant when it is closed under ( )(a b) for all
atoms a and b.
not substituted, but they can be swapped: We write
156
11.1 Nominal rewriting
Denition 81
A
nominal rewriting system R is an equivariant set of nom-
inal rewriting rules.
Nominal rewriting rules generate a rewrite relation on nominal terms in context. We denote a term
t
with a freshness context
t by t ` t.
In order to
apply a rewrite rule at the root position in t, we use nominal matching. Not only
l has to match t, but also the constraints in rl have to be satised by t . For
example, consider the rule a # X ` f X ! X and let f a be the term to rewrite.
In this case, even if f X and f a match (with the substitution = [X 7! a]) the
rule cannot be applied because a is not fresh in X .
Formally, given a nominal rewrite rule rl ` l ! r and a term t with freshness
context t , we say that t ` t matches the left-hand side l of the rule under the
constraints rl if and only if the matching problem l t has a solution (; )
and [ Env ( (rl ))
t, where (rl ) = fa#X j a#X 2 rl g. The
result of applying this rewrite rule to t ` t is then the term t ` r .
Note that since nominal rewriting systems are equivariant, in order to decide
whether there exists a rule in the system that matches a term at a given position
we need equivariant nominal matching (i.e., nominal matching with respect to
variants of the rules obtained by changing the names of the atoms). However,
nominal matching is sucient if the rules are
closed.
Closed nominal rules are
equivalent to the rules used in standard notions of higher-order rewriting (for
instance, any Combinatory Reduction System [31] can be dened as a closed
nominal rewriting system, see [21]). Equivariant nominal matching, which is an
NP-complete problem in general [12], can be avoided for closed rules, simply by
using a copy of the rewrite rule where all the variables and atoms are chosen to
be dierent from the ones in the term to be rewritten (this is always possible
because nominal rewriting systems are equivariant). We refer the reader to [20]
for the formal denition and properties of closed nominal rewriting.
For example, the -rule in the
rule scheme
We can specify
-calculus
x:Mx ! M
if
is usually written informally as a
x 62 F V (M )
using a nominal rewriting system dened by the (closed) rule
a#X ` ([a]Ap(X; a)) ! X
157
11.2 A nominal rewriting algorithm: two improvements on the
matching algorithm
and then use this system to rewrite the term
context as follows:
([b]Ap(a; b)) in an empty freshness
1. We choose a fresh copy of the rewrite rule that does not use any variables
or atoms occurring in the term to be rewritten. For instance, we take
c#X ` ([c]Ap(X; c)) ! X
2. We solve the matching problem:
([c]Ap(X; c)) ([b]Ap(a; b))
which has solution
([X 7! a]; ;).
the term a, as expected.
Since
c #a
is valid, our term rewrites to
11.2 A nominal rewriting algorithm: two improvements on the matching algorithm
t ` t matches rl ` l ! r eciently, we will use a modied
version of our matching algorithm. Since checking [ Env ( (rl ))
t
To check whether
can be expensive because of the set unions and -equivalence problems it involves,
we will do these operations on-the-y, directly in the matching algorithm.
For this, the rst modication consists of computing
traverse the term, so that it will directly be in
X
Below we use the notation
in a freshness context
.
Env((rl ))
as we
X for the set of atoms in constraints relating to
, more precisely:
X = fa j a#X 2 g
Proposition 64 If for each equation X t at the end of phase 2, we
1
1
the set (rlX ) = f a j a # X 2 rl g to the current freshness set A ,
is equivalent to computing
Env((rl ))
[ .
In fact, it is sucient to do this once for each distinct variable
158
X.
add
this
11.2 A nominal rewriting algorithm: two improvements on the
matching algorithm
Proof
By Proposition 52, at the end of phase 2, the problem, if it has a solu-
(Xi iti)n1 . Let Xj j tj be one of these constraints.
Notice that for each i such that Xi = Xj , i ti and j tj must be -equivalent
for the problem to have a solution . In addition to satisfying Xj (j tj ),
must also satisfy rlXj # Xj .
Since rlXj # Xj is equivalent to rlXj # j tj , which is in turn equivalent
to j 1 (rlXj ) # tj , any solution of the original problem is also a solution of:
tion, is of the form
Xj (j ; j A [ j 1 (rlXj ))tj
Furthermore, since j tj
i ti when Xi = Xj , we also have
rlXj # j tj , rlXj # i ti
which means we only need to do it once for each distinct variable.
The second improvement consists of computing
t directly without
. For this, in phase 4, when reaching X , instead of adding
A # X to we directly check if A tX .
computing
Proposition 65
In phase 4, when reaching
equivalent to adding
to
and then checking t.
is
#
by generating constraints A
X for each
in the problem. Therefore, checking t is equivalent to checking
Proof
X
A # X
X , checking whether A tX
Phase 4 computes
#
whether every A X t , which is equivalent to A tX .
These two modications can actually be made without changing the phases.
All we need to do it to interpret FreshnessContextCall dierently. The interpretation
of FreshnessContextCall to match terms in context is described in the algorithm 18.
Because we no longer need to copy freshness sets, we can always use mutable
arrays to represent A . Unfortunately we still need to copy permutations ,
so in the following we will use mutable arrays for sets, and persistent maps for
permutations. We will represent
rl using an array indexed by variables, such
that the element X in the array will contain the list of atoms that should be fresh
for
X
according to
rl .
159
11.2 A nominal rewriting algorithm: two improvements on the
matching algorithm
1
2
3
4
5
interpretFreshContextCheck(c ) = ;
switch c do
case FreshConstraint A # X
if A 6 tX then
dthrowError(Constraints
6
end
7
;
8
k(())
9
10
11
12
13
14
k
case FreshContextRule X
S = rlX ;
remove
k(S )
not satised )
k
rlX from rl ;
case FreshToList k
k([])
15
endsw
Algorithm 18: Interpretation of Freshness for matching terms in context
16
160
11.3 Complexity of nominal rewriting
11.3 Complexity of nominal rewriting
We start by dening the size of a matching problem for terms in context.
Denition 82
The size of a matching problem Pr
= rl ` l t ` t is dened
as
jPrj = jrl j + jlj + jtj + jtj
where the sizes of rl and t are the size of their representations.
Proposition 66
The total cost of the algorithm to solve the matching problem
for terms in context:
is at most
rl ` l t ` t
#((jrl j + jlj + jtj) log jAj + m jMV ar(t)j)
A is the set of atoms occurring in the problem, m
MV ar(t) is the multiset of variable occurrences in t.
where
= maxX (jtX j) and
F reshContextRule adds
1 (rlX ) to the current freshness set, once for each variable X . Freshness sets
Proof
With this interpretation of FreshContextCall,
being coded as mutable arrays and permutations as functional maps, it takes
#(log jAj) time to access each atom a in rlX (coded as a list of atoms), compute
its image by 1 and add it to A . The cost of Phase 2 is therefore #(jrl jlog jAj).
Phase 4, when reaching X , rewrites it into X and checks whether A tX . The former is done in constant time thanks to the use of functional maps.
The latter, if done in a naive way, would take time proportional to jAj since A
is represented as an array of size jAj. However, checking A tX is equivalent
to checking jtX \ A j = jfa j a 2 A gj. We call this number sA . We can, as we
did for permutations, compute this number when A is created and then update
it in constant time on every update of A . In this way, we only need to compute
tX that are also in A and compare it with sA , which
can be done in time proportional to tX (because tX is a list of atoms and A
the number of atoms in
X , we can bound
the total cost is m MV ar(t).
an array). Thus, for any
161
the cost by
m,
so in the worst case
11.4 The Zipper Layer
Summarising:
1
2
2
3
4
Phase
Phase
Phase 0
Phase
Phase 0
: #(jlj log jAj)
: #(jlj log jAj)
: #(jrl j log jAj)
: #(jtj log jAj)
: #(m jMV ar(t)j)
11.3.0.1 Special cases
First-order matching is a particular case of nominal matching, and it is interesting
to see that the rewriting algorithm specied above behaves as expected when the
terms involved are ground or rst-order.
Proposition 67 If t is ground or if t is empty, the complexity of the algorithm
is at most #((jrl j + jl j + jtj) log jAj).
Proof
If
t is ground, jMV ar(t)j = 0 and if t is empty, m = 0.
Proposition 68
For rst-order terms, where
t is ground, the algorithm is linear
in time.
Proof
(jAj
= 0).
In this case there are neither variables (jMV ar
(t)j = 0) nor atoms
11.4 The Zipper Layer
A zipper is a data structure that has been introduced by Huet [26] to represent a
pointer within a term in a purely functional way. There are many implementations
of zippers. The
Haskell
module Zipper in appendix A.11 presents a very simple
implementation of a generic zipper on algebraic datatypes. This section presents
the zipper on compact nominal terms using the implementation in appendix A.11.
Let t be the compact nominal term
navigate withing
[a]((a; b); [b]X ). We want a simple way to
t in a purely functional way.
162
11.4 The Zipper Layer
Denition 83
Let
H
be a new variable symbol (
H
62 X ).
Until now we have
X . We will keep
calling compact nominal terms such terms. Compact nominal terms c over (X [
fH g) with exactly one occurrence of X are called compact nominal context or
only considered compact nominal terms whose variables where in
context for short. H
For example,
is called a
hole.
[a](X; H ) is a context but [a](X; X ) and [a](H; H ) are not. The
following equalities are valid:
t = H [X 7! t]
[a]H [H 7! ((a; b); [b]X )]
([a](H; [b]X ))[H 7! (a; b)]
[a]((a; b); [b]H )[H 7! X ]
u can be represented as a pair
(c; u0) where c is a context and u0 a subterm of u and u = c[H 7! u0]. Furthermore
let c1 and c2 two contexts, c1 [H 7! c2 ] is also a context.
As we can see any can see any compact term
Denition 84 A zipper z on a nominal term is a pair (t; [c1 ; : : : ; cn ]) where
t is a nominal term and [c1 ; : : : ; cn ] a list of contexts. z represents the term
cn [H 7! cn 1 [H 7! : : : c1 [H 7! t]]]. t is called the subterm of the zipper
z = ([a]u; [c1 ; : : : ; cn ]) be a zipper. We can move a step downward by the
new zipper z 0 = (u; [[a]H; c1 ; : : : ; cn ]). Let z = ((u1 ; u2 ); [c1 ; : : : ; cn ]), there are two
downward move possible: moving to u1 by z 0 = (u1 ; [(H; u2 ); c1 ; : : : ; cn ]) or to u2
by z 0 = (u2 ; [(u1 ; H ); c1 ; : : : ; cn ]). Going a step upward from z = (u; [c1 ; : : : ; cn ])
is as simple as z 0 = (c1 [H 7! u]; [c2 ; : : : ; cn ]) if n > 0.
It also possible to update the subterm of a zipper. Let z = (u; [c1 ; : : : ; cn ]).
We can replace u by t by z 0 = (t; [c1 ; : : : ; cn ]).
Let
The Haskel module Zipper in appendix A.11 implements a state monad whose
state is a zipper. Along with the rewrite function it makes a very simple but
complete rewriting framework. Any stategy can be written in
Haskell,
using the
zipper to navigate within the term and the rewrite function to rewrite at the
desired position.
163
Part IV
Conclusions
164
Related Works
On Nominal Algorithms
Gabbay, Pitts
and Urban [51] were the rst to prove
that nominal unication is decidable. They presented a simple algorithm based
on problem rewriting. Unfortunately, because subterms were not shared, the
algorithm is exponential in time and space.
Cheney
proved that
equivariant unication
[11] (deciding whether two
terms involving swapping can be made equal up to a permutation) is NPcomplete.
Higher-Order Patterns Higher-Order Patterns syntax is another syntax,
introduced by
Miller
[35] to represent systems with binders. Higher-Order Pat-
-terms in -long forms, in which free variables F only occur in the
form F (xk ) with xk is a sequence of k (k > 0) distinct bound variables.
For example, x:y:z:F (y; x) and x:y:y (z:F (z; y ); F (x; y ); G(y )) are higherorder patterns, provided that they are -long forms. But x:F (c; x), x:y:F (x; x)
and z:F (G(x)) are not.
terns are
Higher-order patterns and nominal unication are strongly related. Cheney [13]
showed that higher-order pattern unication can be reduced to nominal unication. Latter Levy and Villaret [33] proved that nominal unication can be reduced to higher-order pattern unication. Their reduction proves that nominal
unication can be decided in quadratic deterministic time. Their proof is based
on Qian's linear higher-order pattern algorithm [45]. This algorithm is intricate
and, to our knowledge, has never been implemented (certainly because of its intricacy), whereas QNU (presented in chapter 6) is simple, based on a well known
rst-order algorithm and has been implemented.
165
Qian's algorithm is linear in time and space on the size of the input problem
whereas the nominal unication algorithm is quadratic in time and space in the
size of the input problem. This does not mean that Qian's algorithm is faster
because the syntax is dierent. Nominal syntax can be much more concise than
higher-order patterns. The reason of that lies with the variables. In nominal
syntax a variable is just X and there is no restriction on the possible free variable
of
X.
This is not the case with higher-order patterns where a variable
X
is
always followed by the list of its possible free atoms (called bound variables in
A0 is the set of atoms in the
pattern syntax can be j A0 j times
higher-order pattern terminology). It means that if
problem, the same problem in higher-order
bigger than expressed in nominal syntax. Furthermore, as seen in chapter 6, when
all variables in a nominal problem are suspended, then unication become also
linear.
First-class monadic signature
The idea of using continuations to stop the
computation to return value and a computation is not new. It has been used in
many situations:
Kiselyov, Shan
Claessen
and
Sabry
used them to implement a concurrency monad [14],
showed they can be used in operating systems to im-
plement system calls [30], Filinski used them to represent monads and monadic
towers [22, 23], they have been used to implements streams, . . .
To our knowledge it has never been used to implement monadic classes.
lyov, Shan
Kise-
and Sabry 's approach is the closest to ours as system calls and monadic
operations are related (the operation system can be seen as a monad). However,
unlike their, our concern is not only to implement system calls but to have a
correct and complete way to implement monadic classes. Furthermore their calls
are only monomorphic and do not have arguments of the same monad (the example of section 3.3) whereas we presented how to implement monomorphic and
polymorphic calls even in that case. Their approach works ne for one layer but
problems arise with more: our approach explicits the number of layers and the
type of each layer. With more typing information, programs are easier to read,
to develop and overall safer. The
prompt not dened error
that can occur at run
time with their approach is impossible with ours. Every level error is detected
by the type checker at compile time. Furthermore our approach is based on the
166
CPS Hierarchy, which is simple and well known [9, 18] whereas they rely on a
complex monadic framework [19].
167
Future Works
The main problem when implementing an algorithm in a syntax involving binders
is the amount of technical details needed everywhere like freshness check or applying the right permutation at the right place at the right time. Our modular
approach has two main benets:
most of these details are handled automatically by the corresponding layer
instead of interacting in a chaotic way, nominal aspects (naming, handling of
the context, actual implementation, combing the phases, . . . ) are separated
so that it is easier to check each of them is OK.
The idea of
environment
shows an important aspect of syntax involving as-
pect. On the contrary to rst-order syntax where a term comes alone, nominal
terms are the combination of a term and a position map. This position map
called environment is the link between names and positions. For example: let
us consider two nominal terms
abstraction in
[a]a and [b]b.
a (resp. b) is just the name of the
[a]a (resp. [b]b). The two abstractions have the very same position,
only their name is dierent. All nominal terms come with such a map and the
complexity of nominal algorithms comes from translating position names from
terms to terms.
The reason why the nominal
-equivalence and matching algorithms can be
more ecient than unication whereas in rst-order syntax they have all the same
complexity is that for
-equivalence
and matching algorithms, the closer two
nodes are in the directed acyclic graph the closer are their environments. That
is not true for unication. The key to a faster than
set of atoms in the problem
P r0 )
j A0 j P r0 (where A0 is the
complexity algorithm for nominal unication
168
is either to be able to compute the suspended permutation and set of freshness
constraints at any node in less time than
j A0 j or to prove that knowing it for
a smaller subset of nodes is sucient. Unfortunately the dependency relation
between permutations and freshness set in a unication problem is chaotic and
depend only on the problem.
A possibility could be to have a less chaotic dependency. Let us consider the
[a](X; [a]X ). This term means explicitly that a free occurrence of a in X in
bound by two dierent abstractions. Having [a](a; [a]a) is not annoying because
the two a are not the same occurrence and [a](a; [a]a) is equivalent to [a](a; [b]b)
but in the [a](X; [a]X ), the two occurrences of X represent the very same term.
term
Do we need in practice to have many dierent environment in a term? Maybe
not.
t = [a](X; ((f; X ); [b]y)). If c is an free atom in X all the occurrences of
c in t[X 7! c] will be either all bound and bound by the same abstraction or all
free. Let u = (X; [a]X ) and X = a, a in at the same time free and bound in
u[X 7! a]. More generally, when in a term s there is a variable X such that a
free atom a in X can be bound by dierent abstractions or at the same time free
and bound, then u is said to be schizophrenic.
Let
anti-schizophrenic restriction is: let t be a nominal term
For any atom a, if two occurrences of a (from X ) in t[X 7! a]
are under two dierent abstractions, or one free in t[X 7! a] and not the other,
then a has to be fresh in X .
Denition 85
and X 2 V(t).
The
This restriction ensures that all occurrences of
X
in t have the same environ-
X s 0 X t
s t. If managing the
ment. Because of this, when reducing a nominal problem
we know that
ds(; 0 )
# X so we can directly write
set of freshness constraints for every variable can be done eciently, then we
conjecture a better than quadratic unication algorithm can be found under this
restriction
169
Conclusion
We showed in this thesis that even if nominal algorithms may seem simple at rst
look, developing and implementing ecient nominal algorithms requires powerful
theoretical and practical tools. Their complexity depends a lot on the implementation of permutations and sets and on the strategy used. Furthermore there
are not (yet) a unique implementation and strategy that are the best for all the
situations. The algorithms need to adapt themselves to the input problem to
perform specic optimisations.
This complexity is not specic to nominal algorithm. Even if a linear algorithm to solve Higher-Order Pattern Unication exist since 1993 [44], to our
knowledge it has never been implemented in practice. Practical higher-order
pattern unication implementations, like
Teyjus
[7, 37], seem to prefer using a
simpler, but less ecient, algorithm. The unication algorithms presented in
part II are simple and ecient.
The -equivalence and matching algorithms presented in part III are the most
ecient known algorithm for these problems. On linear matching problems without permutations, the nominal algorithm complexity is log-linear, but the size
of these problems in higher-order pattern syntax can be
j A0 j (where A0 is the
set set of atoms in the problem) times bigger. So even if higher-order pattern
matching is linear, on these problems, it is less ecient than nominal matching.
The techniques developed in the thesis are interesting results by themselves.
The nominal union-nd algorithm and the theory behind can be useful in any
situation where a union-nd algorithm able to handle names is required. The
permutations, sets of atoms, freshness contexts and environment monadic layers
can be used as-is in any program manipulating nominal terms thus enabling it to
170
use several actual implementations of permutations and sets of atoms, dierent
interpretations of freshness context calls and one single environment.
First-class monadic signatures can be partially implemented in other languages than
Haskell
thus bringing monadic classes to theses languages. For
languages with native rst-class continuations (Scheme,
(bytecode),
Ruby, Objective CAML
. . . ) it can even be done transparently using Filinski's implementation
of shift /reset in terms of callcc [22].
171
Bibliography
[1] Brics website:. http://www.brics.dk/. 11
[2] Ghc documentation:. http://www.haskell.org/ghc/docs/latest/html/.
15
[3] Haskell 98 revised report:. http://www.haskell.org/onlinereport/. 15
[4] Haskell website:. http://haskell.org/. 15
[5] Hnt website:. http://www.brics.dk/. 13
[6] Objective caml website:. http://www.dcs.kcl.ac.uk/pg/calves/hnt/. 69,
155
[7] Teyjus website:. http://teyjus.cs.umn.edu/. 170
[8]
S. Adams.
Functional Pearls Ecient sets—a balancing act.
Functional Programming,
[9]
3(04):553561, 2008.
59
M. Biernacka, D. Biernacki, and O. Danvy. An operational founda-
tion for delimited continuations in the CPS hierarchy.
[10]
C. Calvès and M. Fernändez.
BRICS. 32, 167
Nominal matching and alpha-equivalence.
Logic, Language, Information and Computation. LNCS,
[11]
J. Cheney.
J. Cheney.
5110:111122.
The complexity of equivariant unication.
computer science,
[12]
Journal of
Lecture notes in
pages 332344, 2004. 165
Equivariant unication.
3467:7489, 2005.
128
157
172
Lecture Notes in Computer Science,
BIBLIOGRAPHY
[13]
J. Cheney.
Relating nominal and higher-order pattern unication. In
Pro-
ceedings of the 19th International Workshop on Unication (UNIF 2005),
pages 104119. Citeseer, 2005. 165
[14]
K. Claessen.
9(03):313323, 1999.
Programming,
[15]
A poor man's concurrency monad.
O. Danvy and A. Filinski.
Journal of Functional
166
Abstracting control. In
Proceedings of the
1990 ACM conference on LISP and functional programming,
pages 151160.
ACM New York, NY, USA, 1990. 20
[16]
O. Danvy and K. Malmkjær.
formation.
On the Idempotence of the CPS Trans-
BRICS, Computer Science Department, University of Aarhus.
20
[17]
O. Danvy and K. Millikin.
Computer Programming,
[18]
[19]
74(8):534549, 2009.
O. Danvy and Z. Yang.
chy.
Refunctionalization at work.
21
An operational investigation of the CPS hierar-
Lecture notes in computer science,
pages 224242, 1999. 32, 167
R.K. Dyvbig, S.P. Jones, and A. Sabry.
delimited continuations.
Science of
A monadic framework for
Journal of Functional Programming,
17(06):687
730, 2007. 167
[20]
M. Fernandez and M.J. Gabbay.
Computation,
[21]
205(6):917965, 2007.
Information and
99, 134, 156, 157
M. Fernández, M.J. Gabbay, and I. Mackie.
tems. In
Nominal rewriting sys-
ACM Symposium on Principles and Practice of Declarative Pro-
gramming (PPDP’04), ACM Press,
[22]
Nominal rewriting.
A. Filinski.
Representing monads.
2004. 157
In
Proceedings of the 21st ACM
SIGPLAN-SIGACT symposium on Principles of programming languages,
pages 446457. ACM New York, NY, USA, 1994. 166, 171
173
BIBLIOGRAPHY
[23]
A. Filinski.
Representing layered monads. In Proceedings
of the 26th ACM
SIGPLAN-SIGACT symposium on Principles of programming languages,
pages 175188. ACM New York, NY, USA, 1999. 33, 166
[24]
M.J. Gabbay and A.M. Pitts.
variable binding.
[25]
P. Homeier.
Formal aspects of computing,
G. Huet.
13(3):341363, 2002.
10
A proof of the Church-Rosser theorem for the lambda calculus
in higher order logic. In
[26]
A new approach to abstract syntax with
The zipper.
TPHOLs,
pages 207222, 2001. 139
Journal of Functional Programming,
7(05):549554,
1997. 162
[27]
M. Jaskelioff.
Monatron: An Extensible Monad Transformer Library. 33
[28]
M. Jaskelioff.
Modular Monad Transformers. In
Proceedings of the 18th
European Symposium on Programming Languages and Systems:
Held as
Part of the Joint European Conferences on Theory and Practice of Software,
ETAPS 2009,
[29]
pages 6479. Springer, 2009. 25, 33
M.P. Jones.
Functional programming with overloading and higher-order
polymorphism.
[30]
Lecture Notes in Computer Science,
O. Kiselyov, C. Shan, and A. Sabry.
Proceedings of the 2006 ICFP conference,
925:97136, 1995.
30
Delimited dynamic binding. In
41, pages 2637. ACM New York,
NY, USA, 2006. 166
[31]
J.W. Klop, V. Oostrom, and F. Raamsdonk.
systems: introduction and survey.
[32]
P.J. Landin.
Research,
[33]
TCS,
Getting rid of labels.
Combinatory reduction
121(1&2):279308, 1993.
157
Report, UNIVAC Systems Programming
1965. 19
Jordi Levy and Mateu Villaret.
order perspective. 2008. 165
174
Nominal unication from a higher-
BIBLIOGRAPHY
[34]
A. Martelli and U. Montanari.
An ecient unication algorithm.
ACM Transactions on Programming Languages and Systems (TOPLAS),
4(2):258282, 1982.
[35]
D. Miller.
80, 86
A logic programming language with lambda-abstraction, func-
tion variables, and simple unication.
1(4):497536, 1991.
[36]
E. Moggi.
Journal of Logic and Computation,
165
Notions of computation and monads. INF. COMPUT., 93(1):55
92, 1991. 19
[37]
G. Nadathur and D.J. Mitchell.
System description: Teyjus-a compiler
and abstract machine based implementation of lambdaprolog.
in computer science,
[38]
MHA Newman.
lence".
[39]
On theories with a combinatorial denition of" equivapages 223243, 1942. 130, 134, 138
J. Nievergelt and EM Reingold.
Binary search trees of bounded bal-
Proceedings of the fourth annual ACM symposium on Theory of
computing,
[40]
pages 287291, 1999. 170
Annals of Mathematics,
ance. In
Lecture notes
pages 137142. ACM New York, NY, USA, 1972. 59
MS Paterson and MN Wegman.
Linear unication. In
the eighth annual ACM symposium on Theory of computing,
Proceedings of
pages 181186.
ACM New York, NY, USA, 1976. 69, 70, 72
[41]
S.L. Peyton Jones. The Implementation of Functional Programming Lan-
guages (Prentice-Hall International Series in Computer Science).
Prentice-
Hall, Inc. Upper Saddle River, NJ, USA, 1987. 10
[42]
A.M. Pitts.
Nominal logic, a rst order theory of names and binding.
Information and computation,
[43]
D. Plump.
186(2):165193, 2003.
Term graph rewriting.
10
Handbook of Graph Grammars and Com-
puting by Graph Transformation, Applications, Languages and Tools,
67
175
2:361.
BIBLIOGRAPHY
[44]
Z. Qian.
Linear unication of higher-order patterns.
IN COMPUTER SCIENCE,
[45]
Z. Qian.
pages 391391, 1993. 170
Unication of higher-order patterns in linear time and space.
Journal of Logic and Computation,
[46]
LECTURE NOTES
John C. Reynolds.
languages. In
6(3):315341, 1996.
165
Denitional interpreters for higher-order programming
Proceedings of the ACM annual conference (ACM'72),
pages
717740, 1972. 20, 21
[47]
N. Shankar. Metamathematics, machines and Gödel's proof.
Cambridge
Univ Pr, 1997. 139
[48]
M.R. Shinwell, A.M. Pitts, and M.J. Gabbay.
FreshML: Program-
ming with binders made simple. In Proceedings of the eighth ACM SIGPLAN
international conference on Functional programming,
pages 263274. ACM
New York, NY, USA, 2003. 10
[49]
J. Stern.
national,
[50]
Fondements mathématiques de l'informatique.
Ediscience Inter-
1990. 118
R.E. Tarjan.
On the eciency of a good but not linear set union algorithm.
1972. 80
[51]
C. Urban, A.M. Pitts, and M.J. Gabbay.
retical Computer Science,
Nominal unication.
Theo-
323(1):473498, 2004. 50, 51, 53, 54, 99, 131, 134,
165
176
Appendix A
Haskell Code
A.1 Base denitions
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
ExistentialQuantification ,
7
GeneralizedNewtypeDeriving # }
8
9
10
11
12
13
14
module
Base
where
import
import
Control .
Control .
Monad
Monad . S t a t e
15
16
17
18
Monad m) => m a
liftStateT : : (
liftStateT = l i f t
> StateT s m a
19
20
21
mkFst : : ( a > c ) > ( a , b )
mkFst f ( a , b ) = ( f a , b )
> (c , b)
mkSnd : : ( b
> (a , d)
22
23
24
> d)
> (a , b)
177
A.1 Base denitions
25
mkSnd f ( a , b ) = ( a , f b )
26
27
28
29
30
31
32
33
34
Linear
Continuation
newtype
LContT m a = LContT { unLContT : :
forall r . (a > m r ) > m r }
instance (Monad m) => Monad ( LContT m) where
return a
= LContT $ \k > k a
( LContT m) >>= f
35
36
37
= LContT $ \k
Monad
> m (\ x
=>
return
runLContT : : (
m)
LContT m a
runLContT ( LContT m) = m
> unLContT ( f x ) k )
>m a
38
39
40
41
42
43
44
Monad
=>
liftLContT : : (
m)
m a > LContT m a
l i f t L C o n t T m = LContT $ \k > m >>= k
instance
MonadTrans LContT
l i f t = liftLContT
where
45
46
47
48
49
50
51
52
53
54
55
56
Er ro rT
newtype
of
the
mtl
error
EitherT
m a =
EitherT { unEitherT : :m (
Either error
a) }
instance (Monad m) => Monad ( EitherT error m) where
return
= EitherT . return . Right
( EitherT m) >>= f = EitherT $ do v < m
case v of
Left e > return $ Left
Right a > unEitherT $ f
57
58
59
60
61
62
63
l i f t E i t h e r T m = EitherT $ m >>=
instance
MonadTrans ( EitherT
l i f t = liftEitherT
64
65
throwEitherT : : (
return
.
Right
error ) where
Monad m) => error
> EitherT
178
error
m a
e
a
A.1 Base denitions
66
throwEitherT
error
67
68
69
70
71
72
73
74
75
76
= EitherT $
Monad
error
return
=>
catchEitherT : : (
m)
EitherT
m a
> (
> EitherT
m a)
> EitherT
m a
catchEitherT ( EitherT m) h = EitherT $
v < m
v
e > unEitherT $ h e
a >
$
a
error
do
error
case of
Left
Right
error
return
Right
77
78
79
80
81
82
83
Usefull
printing
instance Show ( a > b ) where
show _ = "<fun>"
179
$
Left error
A.2 Fist-Class Monadic Signatures
A.2 Fist-Class Monadic Signatures
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
GeneralizedNewtypeDeriving ,
6
KindSignatures ,
7
ExistentialQuantification
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module
Call
( call ,
callT ,
instanciateStream ,
instanciateStreamT ,
instanciate ,
instanciateT ,
instanciateNoT ,
IdentityT ( . . ) ,
liftIdentityT ,
liftSContT ,
SCall ,
SCont ,
SCallT ,
SContT ,
stream ,
streamT ,
clv ' p1 ,
clv ' p2 ,
clv ' p3 ,
clv ' p4 ,
clv ' p5 ,
clv ' p6 ,
clv ' p7 ,
clv ' p8
)
where
36
37
38
39
import
import
Control .
Control .
Monad
Monad . Trans
180
# }
A.2 Fist-Class Monadic Signatures
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
data
SCall t h e c l a s s f i n a l v a l u e =
End f i n a l v a l u e
| C a l l ( t h e c l a s s ( SCont t h e c l a s s ) ( SCall t h e c l a s s f i n a l v a l u e ) )
newtype
SCont t h e c l a s s a = SCont { unSCont : :
f o r a l l e . ( a > SCall t h e c l a s s e ) > SCall t h e c l a s s e }
instance Monad
return a
( SCont t h e c l a s s )
= SCont $ \k
( SCont m) >>= f = SCont $ \k
return
m >>=
(m >>=
a >>=
f
return
≡
f
≡
m
f ) >>= g
≡
where
> k a
> m (\ x
> unSCont ( f x ) k )
a
m >>= ( \ x
>
f
x >>= g )
56
57
58
59
call ::
( f o r a l l r . ( a > r ) > t h e c a l l ( SCont t h e c a l l ) r )
> SCont t h e c a l l a
c a l l a = SCont $ \k > C a l l ( a k )
60
61
62
63
stream : : SCont t h e c l a s s a
stream m = unSCont m End
> SCall t h e c l a s s a
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
Monad
=>
instanciateStream : : (
m)
( t h e c l a s s ( SCont t h e c l a s s ) ( SCall t h e c l a s s f i n a l v a l u e )
> m ( SCall t h e c l a s s f i n a l v a l u e ) )
> SCall t h e c l a s s f i n a l v a l u e
> m finalvalue
i n s t a n c i a t e S t r e a m i n t e r p r e t = aux
aux (End
r) =
r
aux ( C a l l c ) = i n t e r p r e t c >>= aux
where
return
Monad
=>
instanciate : : (
m)
( t h e c l a s s ( SCont t h e c l a s s ) ( SCall t h e c l a s s a )
> m ( S Call t h e c l a s s a ) )
> SCont t h e c l a s s a
>m a
i n s t a n c i a t e i n t e r p r e t t = i n s t a n c i a t e S t r e a m i n t e r p r e t $ stream t
181
A.2 Fist-Class Monadic Signatures
81
82
83
84
85
86
TRANSFORMERS
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
data
SCallT t h e c l a s s m e =
EndT
e
| CallT ( t h e c l a s s
( SContT t h e c l a s s m)
(m ( SCallT t h e c l a s s m e ) )
)
newtype
SContT t h e c l a s s m a = SContT { unSContT : :
f o r a l l e . ( a > m ( SCallT t h e c l a s s m e ) )
> m ( SCallT t h e c l a s s m e ) }
instance (Monad m) => Monad ( SContT t h e c l a s s
return x
= SContT $ \k > k x
( SContT m) >>= f = SContT $ \k
> m (\ x
m)
where
> unSContT ( f x ) k )
102
103
104
105
106
l i f t S C o n t T m = SContT $ \k
> m >>= k
instance
MonadTrans ( SContT t h e c a l l )
l i f t = liftSContT
where
107
108
109
110
callT : : (
Monad m) =>
( forall r .
111
112
113
114
(a > r )
> t h e c a l l ( SContT t h e c a l l m) r
)
> SContT t h e c a l l m a
c a l l T a = SContT (\ k >
$ CallT ( a k ) )
return
115
116
117
streamT : : (
Monad m) =>
118
119
streamT m = unSContT m (
SContT t h e c l a s s m a
> m ( SCallT t h e c l a s s m a )
. EndT)
return
120
121
182
A.2 Fist-Class Monadic Signatures
122
123
124
125
126
127
128
129
Monad
=>
instanciateStreamT : : (
m)
( t h e c l a s s ( SContT t h e c l a s s t ) ( t ( SCallT t h e c l a s s t a ) )
> m ( SCallT t h e c l a s s t a ) )
> SCallT t h e c l a s s t a
>m a
i n s t a n c i a t e S t r e a m T i n t e r p r e t = aux
aux (EndT
r) =
r
aux ( CallT c ) = i n t e r p r e t c >>= aux
where
return
130
131
132
133
134
135
136
137
138
139
Monad
Monad
=>
instanciateT : : (
( t m) , MonadTrans t ,
m)
( t h e c l a s s ( SContT t h e c l a s s m) (m ( SCallT t h e c l a s s m a ) )
> t m ( SCallT t h e c l a s s m a ) )
> SContT t h e c l a s s m a > t m a
instanciateT interpret t =
c < l i f t $ streamT t
instanciateStreamT i n t e r p r e t c
do
140
141
142
143
144
145
146
147
148
149
clv
clv
clv
clv
clv
clv
clv
clv
' p1
' p2
' p3
' p4
' p5
' p6
' p7
' p8
=
=
=
=
=
=
=
=
liftSContT
clv ' p1 . clv
clv ' p1 . clv
clv ' p1 . clv
clv ' p1 . clv
clv ' p1 . clv
clv ' p1 . clv
clv ' p1 . clv
' p1
' p2
' p3
' p4
' p5
' p6
' p7
150
151
152
153
154
Identity
Transformer
newtype I d e n t i t y T
deriving (Monad)
m a = IdentityT { runIdentityT : : m a }
155
156
157
158
159
160
l i f t I d e n t i t y T : : m a > IdentityT m a
l i f t I d e n t i t y T = IdentityT
instance
MonadTrans I d e n t i t y T
l i f t = liftIdentityT
where
161
162
183
A.2 Fist-Class Monadic Signatures
163
164
165
166
167
Monad
=>
instanciateNoT : : (
m)
( t h e c l a s s ( SContT t h e c l a s s m) (m ( SCallT t h e c l a s s m a ) )
> m ( SCallT t h e c l a s s m a ) )
> SContT t h e c l a s s m a > m a
i n s t a n c i a t e N o T i n t e r p m = streamT m >>= i n s t a n c i a t e S t r e a m T i n t e r p
184
A.3 Error Handling Layer
A.3 Error Handling Layer
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
ExistentialQuantification ,
7
GeneralizedNewtypeDeriving # }
8
9
10
11
12
13
14
15
16
17
module
Error
where
import
import
Base
Call
import
import
import
Control .
Control .
Control .
Monad
Monad . I d e n t i t y
Monad . Trans
18
19
Error
20
21
22
data
23
24
E r r o r C a l l e (m : : > ) r =
f o r a l l a . RaiseError e
| f o r a l l a . Try
(m a ) ( e
25
26
27
28
29
Monad
try
:: (
> r)
> r)
=>
Monad m) =>
SContT ( E r r o r C a l l e ) m a
> ( e > SContT ( E r r o r C a l l e ) m a )
> SContT ( E r r o r C a l l e ) m a
m h = c a l l T $ Try m h
31
33
(a
> m a) (a
raiseError : : (
m)
e > SContT ( E r r o r C a l l e ) m a
raiseError e = callT $ RaiseError e
30
32
Class
try
34
35
36
37
38
try ' l v
try
Monad
Monad
=>
:: (
m,
m1)
( SContT ( E r r o r C a l l e ) m1 b > m (m a ) )
' lv lv m h =
$ lv $
(
m) (
join
try return
39
185
> b
return
> (e
. h)
> b)
>m a
A.3 Error Handling Layer
40
41
42
43
44
45
Monad
Error
newtype
48
49
50
51
error
54
55
Monad
58
61
62
63
64
=>
instance
MonadTrans ( ErrorT
l i f t = liftErrorT
runErrorT : : (
error
m a
error ) where
Monad m) =>
error
Either error
ErrorT
m a
>m (
a)
runErrorT = unEitherT . runLContT . unErrorT
Monad m) => error
error = ErrorT $ l i f t
raiseErrorT : : (
raiseErrorT
59
60
m) a}
liftErrorT : : (
m)
m a > ErrorT
l i f t E r r o r T = ErrorT . l i f t . l i f t
56
57
error
deriving Monad
52
53
Wright !
ErrorT
m a = ErrorT {
unErrorT : : LContT ( EitherT
(
)
46
47
Done
tryErrorT : : (
Monad m) =>
error
> ErrorT
$ throwEitherT
error
m a
error
ErrorT
m a
> (
> ErrorT
m a)
> ErrorT
m a
tryErrorT ( ErrorT m) h = ErrorT $ LContT $ \k >
catchEitherT ( unLContT m k ) (\ e > unLContT ( unErrorT $ h e ) k )
error
error
error
65
66
67
68
Interpretation
69
70
71
72
73
74
75
76
77
78
79
of
Call
Monad
as
Er ro rT
=>
interpretErrorT : : (
m)
E r r o r C a l l e ( SContT ( E r r o r C a l l e ) m)
(m ( SCallT ( E r r o r C a l l e ) m a ) )
> ErrorT e m ( SCallT ( E r r o r C a l l e ) m a )
interpretErrorT ( RaiseError e
k) =
r a i s e E r r o r T e >>= l i f t E r r o r T . k
i n t e r p r e t E r r o r T ( Try
m h k) =
r < tryErrorT ( i n s t a n c i a t e T i n t e r p r e t E r r o r T
m)
(( instanciateT interpretErrorT ) . h)
liftErrorT $ k r
do
80
186
A.3 Error Handling Layer
81
82
83
Monad
=>
runErrorCallErrorT : : (
m)
SContT ( E r r o r C a l l e ) m a > m (
e a)
runErrorCallErrorT = runErrorT . ( i n s t a n c i a t e T i n t e r p r e t E r r o r T )
187
Either
A.4 Store Layer
A.4 Store Layer
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
GeneralizedNewtypeDeriving ,
7
ExistentialQuantification
8
9
10
11
12
13
14
15
16
17
18
module
Store
import
import
Base
Call
import
import
import
Control .
Control .
Control .
# }
where
Monad
Monad . S t a t e
Monad . Trans
import qualified
Data . Map as Map
19
20
21
22
Implements
store
calls
23
24
25
26
data
elem
StoreCall store
(m : : >
f o r a l l dat . StoreGet
( store
elem
27
( dat
28
elem
| f o r a l l dat . StorePut
31
32
33
( store
elem
dat )
dat
( store
elem
dat
elem
34
35
36
r =
dat )
> r)
29
30
)
| f o r a l l dat . StoreToFun ( s t o r e
((
elem
> r)
elem
dat )
> dat )
> r)
37
38
39
| f o r a l l dat . S t o r e I n i t
dat
( store
elem
188
dat
> r)
A.4 Store Layer
40
41
42
43
44
elem
, dat )
elem
dat )
| f o r a l l dat a n . StoreFoldM ( a > (
a
( store
(a > r )
> m a)
45
46
47
48
49
storeFoldM
storeInit
storeToFun
f a store
dat
store
storeGet
storePut
store
store
elem
elem
= c a l l T $ StoreGet s t o r e
dat = c a l l T $ StorePut s t o r e
storeGets
storeGetsM
store
store
elem
elem
f
f
50
51
52
53
54
55
56
57
58
61
64
elem f
elem >>=
=
f >>= ( s t o r e P u t s t o r e
elem
> SContT ( S t o r e C a l l s t o r e
65
66
elem )
Monad m) =>
storeToList : : (
store
dat
s t o r e T o L i s t s t o r e = storeFoldM (\ l d
>
return
f a store
dat
store
elem
elem
dat
elem >>= return
elem >>= f
=
( storePut store
storeModifyM s t o r e
storeGet store
62
63
= storeGet store
= storeGet store
elem f
elem >>=
storeModify
store
storeGet store
59
60
= c a l l T $ StoreFoldM
= callT $ StoreInit
= c a l l T $ StoreToFun
. f
. f
elem )
elem )
m [(
elem ,
dat ) ]
$ d : l ) [ ] store
67
68
69
70
71
72
Monad
=>
storeFromList : : (
m)
dat
> [(
, dat ) ]
> SContT ( S t o r e C a l l s t o r e
elem
elem )
m ( store
elem
dat )
73
74
75
76
storeFromList d e f l t [ ]
= storeInit deflt
storeFromList d e f l t (( e , v) : l ) =
store < storeFromList d e f l t l
storePut store e v
do
77
78
79
80
Interpretation
of
Store
as
Data . Map
189
A.4 Store Layer
81
82
83
data
StoreP
elem
84
85
86
dat = StoreP { storeP '
storeP '
}
,
,
)
init
map
: : dat ,
: : Map . Map
deriving ( Eq , Show Read Ord
87
88
89
90
Monad
Ord elem ) =>
elem ) m a
runStoreP : : (
m,
SContT ( S t o r e C a l l StoreP
>m a
91
92
93
runStoreP = i n s t a n c i a t e N o T i n t e r p r e t S t o r e P
where
i n t e r p r e t S t o r e P ( StoreGet
k (
d
$ Map .
94
maybe
95
id
lookup
( StoreP d m) e
e m)
k) =
96
i n t e r p r e t S t o r e P ( StoreToFun ( StoreP d m)
k (\ e >
d
$ Map .
e m)
97
maybe
98
id
k) =
lookup
99
i n t e r p r e t S t o r e P ( StorePut
( StoreP d m) e v k ) =
k ( StoreP d (Map .
e v m) )
100
insert
101
102
interpretStoreP ( StoreInit
k ( StoreP d Map . empty )
103
104
d
k) =
105
i n t e r p r e t S t o r e P ( StoreFoldM f a ( StoreP d m) k ) =
r < runStoreP $
f a (Map . t o L i s t m)
k r
106
do
107
108
foldM
109
110
111
112
113
114
115
Implements
a
state
data
S t a t e C a l l s t a t e (m : : > ) r =
StateGet
( state > r )
| StatePut s t a t e ( ( )
> r)
116
117
118
119
120
121
stateGet
= callT
StateGet
s t a t e P u t s = c a l l T $ StatePut s
return
s t a t e G e t s f = s t a t e G e t >>=
stateGetsM f = s t a t e G e t >>= f
. f
190
elem
dat
A.4 Store Layer
122
123
124
s t a t e M o d i f y f = s t a t e G e t >>= s t a t e P u t . f
stateModifyM f = s t a t e G e t >>= f >>= s t a t e P u t
125
126
127
128
129
130
Monad
interpretStateT : : (
m)
S t a t e C a l l s t (m a )
> StateT s m a
=>
131
132
133
i n t e r p r e t S t a t e T ( StateGet
k) = get
>>= l i f t S t a t e T . k
i n t e r p r e t S t a t e T ( StatePut s k ) = put s >>= l i f t S t a t e T . k
134
135
136
137
138
139
Monad
=>
runStateCall : : (
m)
s > SContT ( S t a t e C a l l s ) m a
runStateCall s m =
r < runStateT ( i n s t a n c i a t e T i n t e r p r e t S t a t e T m) s
$
r
do
return
fst
140
141
142
Implements
a
global
stack
143
144
145
146
147
148
149
data
S t a c k C a l l s t a c k element (m : :
StackEmpty
| StackPush
element
| StackPop
>
)
Bool
(
(()
(
Maybe
r =
element
150
151
152
153
154
stackEmpty
= c a l l T $ StackEmpty
stackPush e = c a l l T $ StackPush e
stackPop
= c a l l T $ StackPop
155
156
157
158
159
160
161
162
Monad
=>
interpretStackStateT : : (
m)
S t a c k C a l l t a t1 (m b ) > StateT [ a ] m b
i n t e r p r e t S t a c k S t a t e T ( StackEmpty
k) =
gets
(
.
) >>= l i f t S t a t e T . k
i n t e r p r e t S t a c k S t a t e T ( StackPush e k ) =
not
null
191
> r)
> r)
> r)
>m a
A.4 Store Layer
163
164
165
166
167
168
169
modify ( e : )
>>= l i f t S t a t e T . k
i n t e r p r e t S t a c k S t a t e T ( StackPop
k) =
s < get
r <
s
[]
>
x : l > put l >>
(
liftStateT $ k r
do
case
of
return Nothing
return Just
x)
170
171
172
173
174
175
176
Monad
=>
runStackStateT : : (
m)
SContT ( S t a c k C a l l t a ) m a1 > m a1
runStackStateT m =
r < runStateT ( i n s t a n c i a t e T i n t e r p r e t S t a c k S t a t e T m) [ ]
$
r
do
return
fst
177
178
179
180
181
Implements
data
Output
OutputCall out (m : :
>
)
r = OutputCall out ( ( )
> r)
182
183
output out = c a l l T $ OutputCall out
184
185
interpretOutputP ( OutputCall out k ) = modify ( out : ) >>= l i f t S t a t e T . k
186
187
188
runOutputP : : (
Monad m) => SContT
( OutputCall out ) m a
> m ( a , [ out ] )
189
190
runOutputP m = runStateT ( i n s t a n c i a t e T interpretOutputP m) [ ]
192
A.5 Nominal Terms
A.5 Nominal Terms
1
2
3
4
5
6
7
module
Term
where
import
import
Naming
Call
import
import
Control .
Control .
Monad
Monad . I d e n t i t y
8
9
10
11
12
13
Atoms
type
type
data
14
15
16
defined
Cst =
Var =
in
Integer
Integer
Naming
Leaf = Atm Atm
| Cst Cst
| Var Var
( ,
,
deriving Eq Ord Show , Read )
17
18
19
20
21
data
Nominal s e t perm = Abs
Atm
| NPerm ( Perm s e t perm )
( ,
,
,
)
deriving Eq Ord Show Read
22
23
24
25
26
27
28
29
|
data
N om in al
Terms
Data
Structure
Term s e t perm =
Leaf
Leaf
| Pair
(Term
s e t perm ) (Term s e t perm )
| Nominal ( Nominal s e t perm ) (Term s e t perm )
( ,
,
,
)
deriving Eq Ord Show Read
30
31
32
Term
Construction
33
34
35
atm : : Atm > Term s e t perm
atm = Leaf . Atm
36
37
38
c s t : : Cst > Term s e t perm
c s t = Leaf . Cst
39
193
A.5 Nominal Terms
40
41
var : : Var > Term s e t perm
var = Leaf . Var
42
43
44
45
perm : : Perm s e t perm > Term s e t perm
perm PermId t = t
perm p
t = Nominal (NPerm p ) t
> Term s e t perm
46
47
48
swap : : Atm > Atm > Term s e t perm
swap a b = perm ( permSwapping a b )
> Term s e t perm
49
50
51
nomAbs : : Atm > Term s e t perm
nomAbs a = Nominal $ Abs a
> Term s e t perm
52
53
54
p a i r : : Term s e t perm
p a i r = Pair
> Term s e t perm
> Term s e t perm
55
56
57
58
59
60
termSize
termSize
termSize
termSize
Int
: : Term s e t perm >
( Leaf
_ ) = 1
( Nominal _ u ) = 2 + ( t e r m S i z e u )
( Pair t1 t2 ) = 1 + ( t e r m S i z e t1 ) + ( t e r m S i z e t2 )
61
62
63
64
65
66
67
68
69
Monad
=>
Maybe
s ub st it ut e M : : (
m)
( Var > m (
(Term s e t perm ) ) )
> Term s e t perm
> m (Term s e t perm )
s ub st it ut e M f = substM '
f ' v = f v >>=
. (
( var v )
where
return
maybe
id )
70
substM ' ( Leaf
( Var v ) ) = f ' v
substM ' ( Nominal m t
) =
x <
71
do
72
73
substM ' ( Pair
74
s t
) =
75
76
substM '
77
t
=
do
substM ' t
$ Nominal m x
s ' < substM ' s
t ' < substM ' t
$ Pair s ' t '
t
return
return
return
78
79
80
|
"substitute
f
t"
substitute
any
194
variable
v
by
(f
v)
A.5 Nominal Terms
in
81
82
83
84
85
t .
f
is
a
pure
substitute : :
function
Maybe
( Var >
(Term s e t perm ) )
> Term s e t perm
> Term s e t perm
s u b s t i t u t e f = r u n I d e n t i t y . ( su bs ti t ut eM $ I d e n t i t y . f )
86
87
88
89
90
91
92
93
94
95
Lazy
data
Term
Multi
Set
LazyTermSet s e t perm =
LtsNil
| L t s S i n g l e (Term
s e t perm )
| LtsNode
( LazyTermSet s e t perm ) ( LazyTermSet s e t perm )
| LtsRemap ( Perm
s e t perm ) ( LazyTermSet s e t perm )
( ,
,
)
deriving Eq Show Read
96
97
98
99
100
101
102
103
ltsMerge : :
LazyTermSet s e t perm
> LazyTermSet s e t perm
> LazyTermSet s e t perm
ltsMerge LtsNil t = t
ltsMerge t LtsNil = t
l t s M e r g e t1
t2 = LtsNode t1 t2
104
105
106
107
108
109
110
Monad
=>
flattenLTS : : (
m)
LazyTermSet s e t perm
> SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) [ Term s e t perm ]
111
112
113
114
f l a t t e n L T S l t s = aux PermId [ ] l t s
where
aux p l
LtsNil
=
return
l
115
116
117
118
aux p l ( LtsNode
n1 n2 ) =
l ' < aux p l n1
aux p l ' n2
do
119
120
121
aux p l ( LtsRemap p2 n ) =
p ' < permCompose MutateNone p p2
do
195
A.5 Nominal Terms
aux p ' l n
122
123
aux p l ( L t s S i n g l e
$ ( perm p
124
return
125
t ) =
t) : l
126
127
128
129
130
toLTS : : [ Term s e t perm ] > LazyTermSet s e t perm
toLTS [ ]
= LtsNil
toLTS ( t : l ) = LtsNode ( L t s S i n g l e t ) ( toLTS l )
131
132
133
134
135
Head
data
136
137
138
139
}
Permutation
Susp s e t perm element = Susp {
suspPerm : : Perm s e t perm ,
suspElem : : element
deriving ( Eq , Ord , Show , Read )
140
141
142
143
144
145
146
Monad
=>
termHeadPerm : : (
m)
Term s e t perm
> SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m)
( Susp s e t perm (Term s e t perm ) )
147
148
149
150
151
152
153
termHeadPerm ( Nominal (NPerm p ) t ) =
Susp p2 u < termHeadPerm t
p3 < permCompose MutateNone p p2
$ Susp p3 u
termHeadPerm t =
$ Susp PermId t
do
return
return
154
155
156
157
158
159
160
161
162
Head
Reduction
Function
Monad
=>
permStepDown : : (
m)
Term s e t perm
> SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) (Term s e t perm )
196
A.5 Nominal Terms
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
permStepDown ( Nominal (NPerm p ) ( Leaf (Atm a ) )
) =
b < permImage p a
$ Leaf (Atm b )
permStepDown ( Nominal (NPerm _) ( Leaf ( Cst c ) )
) =
$ Leaf ( Cst c )
permStepDown ( Nominal (NPerm p1 ) ( Nominal (NPerm p2 ) u ) ) =
p3 < permCompose MutateNone p1 p2
permStepDown ( perm p3 u )
permStepDown ( Nominal (NPerm p ) ( Nominal ( Abs a )
u)) =
b < permImage p a
$ nomAbs b ( perm p u )
permStepDown ( Nominal (NPerm p ) ( Pair t1 t2 )
) =
$ Pair ( perm p t1 ) ( perm p t2 )
permStepDown t
=
t
do
return
return
do
do
return
return
return
178
179
180
181
182
183
184
Directions
for
Zipper
on
No mi na l
data TermDir = P a i r L e f t | PairRight
deriving ( Eq , Ord , Read , Show)
Terms
| NominalDown
185
186
187
188
189
190
termDirs ( Leaf _
) = []
termDirs ( Nominal n t ) = [ ( NominalDown , ( t , (\ x
termDirs ( Pair t1 t2 ) = [ ( P a i r L e f t
, ( t1 , (\ x
( PairRight
, ( t2 , (\ x
]
197
> Nominal n x ) ) ) ]
> Pair
x t2 ) ) ) ,
> Pair
t1 x ) ) )
A.6 Sets and Permutation Layer
A.6 Sets and Permutation Layer
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures # }
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module
Naming
import
Call
where
import qualified
import qualified
import
type
Control .
Atm =
Data . Set as Set
Data . Map as Map
Monad
Integer
data MutateSide = MutateLeft
deriving ( Eq , Ord , Read , Show)
| MutateRight | MutateNone
21
22
23
24
25
msFlip
msFlip
msFlip
msFlip
: : MutateSide
MutateNone =
MutateLeft =
MutateRight =
> MutateSide
MutateNone
MutateRight
MutateLeft
26
27
28
29
30
31
32
33
data
S e t C a l l s e t (m
SetIsIn
set
| SetIsSubset set
| SetSize
set
| SetSet
Bool
True
34
35
36
37
38
|
|
|
|
:
:: Atm
set
)
Bool
Atm
Mutate
>
,
False
r =
Bool
Bool
Int
(
(
(
set ( set
:
>
>
>
>
r)
r)
r)
r)
Do NOT M u t a t e
SetEmptyNative
SetToList
set
SetUnion
MutateSide s e t s e t
SetInter
MutateSide s e t s e t
( set
( [ Atm ]
( set
( set
39
198
>
>
>
>
r)
r)
r)
r)
A.6 Sets and Permutation Layer
40
41
42
43
data
Set s e t = SetEmpty
| SetNative set
( ,
,
,
)
deriving Eq Ord Show Read
44
45
setIsIn : : (
Monad m) =>
46
47
Set s e t
> Atm
> SContT ( S e t C a l l s e t ) m
48
49
50
setIsIn
setIsIn
Bool
return False
SetEmpty
_=
( SetNative set ) a = callT $ SetIsIn set a
51
52
53
54
setSize : : (
Monad m) =>
Set t
55
56
57
return
setIsSubSet : : (
Monad m) =>
60
61
Set t
> Set t
> SContT ( S e t C a l l t ) m
62
63
64
setIsSubSet
SetEmpty
_
return True
=
65
66
67
68
s e t I s S u b S e t ( S e t N a t i v e s e t ) SetEmpty
n < callT $ SetSize set
( n == 0)
do
=
return
69
70
71
setIsSubSet ( SetNative set1 ) ( SetNative set2 ) =
callT $ SetIsSubset set1 set2
72
73
74
75
76
77
Monad
80
=>
setNativeEmpty : : (
m)
SContT ( S e t C a l l s e t ) m ( Set s e t )
78
79
Int
s e t S i z e SetEmpty
=
0
s e t S i z e ( SetNative set ) = callT $ SetSize set
58
59
> SContT ( S e t C a l l t ) m
setNativeEmpty =
do
s <
c a l l T SetEmptyNative
$ SetNative s
return
199
Bool
A.6 Sets and Permutation Layer
81
82
setSet : : (
Monad m) =>
83
Bool
> Atm
>
> Set s e t
> SContT ( S e t C a l l s e t ) m ( Set s e t )
Bool
84
85
86
87
88
89
90
s e t S e t mutate a v SetEmpty
s < setNativeEmpty
s e t S e t mutate a v s
=
do
91
92
93
94
s e t S e t mutate a v ( S e t N a t i v e s e t ) =
s < c a l l T $ S e t S e t mutate a v s e t
$ SetNative s
do
return
95
96
97
setToList : : (
Monad m) =>
Set s e t
> SContT ( S e t C a l l s e t ) m [ Atm ]
98
99
100
101
return
s e t T o L i s t SetEmpty
=
[]
s e t T o L i s t ( S e t N a t i v e s e t ) = c a l l T $ SetToList s e t
102
103
setFromList : : (
Monad m) =>
[ Atm ]
> SContT ( S e t C a l l s e t ) m ( Set s e t )
104
105
106
107
setFromList
[]
=
setFromList ( a : l ) =
return
do s <
SetEmpty
setFromList l
setSet
a
s
108
109
110
111
112
Monad
True True
=>
setUnshare : : (
m)
Set s e t > SContT ( S e t C a l l s e t ) m ( Set s e t )
setUnshare s e t = s e t T o L i s t s e t >>= setFromList
113
114
115
setSwap : : (
Monad m) =>
116
117
118
119
120
121
setSwap mutate s e t a b =
>
>
>
>
do
Bool
Set s e t
Atm
Atm
SContT ( S e t C a l l s e t ) m ( Set s e t )
va <
setIsIn set a
200
A.6 Sets and Permutation Layer
vb < s e t I s I n s e t b
s1 < s e t S e t mutate a vb s e t
s e t S e t mutate b va s1
122
123
124
125
126
setUnion : : (
Monad m) =>
127
128
129
MutateSide
> Set s e t
> Set s e t
> SContT ( S e t C a l l s e t ) m ( Set s e t )
130
131
setUnion _
SetEmpty
set2
=
return
set2
=
return
set1
132
133
setUnion _
set1
SetEmpty
134
135
136
137
setUnion ms ( S e t N a t i v e s e t 1 ) ( S e t N a t i v e s e t 2 ) =
s < c a l l T $ SetUnion ms s e t 1 s e t 2
$ SetNative s
do
return
138
139
140
setInter : : (
Monad m) =>
MutateSide
> Set s e t
> Set s e t
> SContT ( S e t C a l l s e t ) m ( Set s e t )
141
142
143
144
145
setInter _
SetEmpty
set2
=
return
set2
=
return
set1
146
147
setInter _
set1
SetEmpty
148
149
150
151
s e t I n t e r ms ( S e t N a t i v e s e t 1 ) ( S e t N a t i v e s e t 2 ) =
s < c a l l T $ S e t I n t e r ms s e t 1 s e t 2
$ SetNative s
do
return
152
153
{
Permutations
154
155
}
156
157
158
159
160
161
162
data
PermCall perm (m : :
PermImage
| PermSwapRight
Bool
True
:
| PermCompose
Mutate ,
>
)
r =
perm Atm
perm Atm
False
:
Do
Not
MutateSide perm perm
201
(Atm
Atm ( perm
> r)
> r)
Mutate
( perm
> r)
A.6 Sets and Permutation Layer
| PermSupp
| PermIdNative
| PermUnshare
163
164
165
perm
( [ Atm ]
( perm
( perm
perm
> r)
> r)
> r)
166
167
168
169
170
171
172
173
174
175
data
Perm s e t perm = PermId
| PermSwap
Atm Atm
| PermNative perm perm ( Set s e t )
( ,
,
,
)
deriving Eq Ord Show Read
176
177
178
179
Monad m) =>
permSupp : : (
Perm s e t t
> SContT ( S e t C a l l s e t ) m ( Set s e t )
180
181
182
permSupp PermId
permSupp ( PermSwap a b )
=
=
183
184
185
permSupp ( PermNative _ _ s ) =
return SetEmpty
if a == b
then return SetEmpty
else setFromList [ a , b ]
return s
186
187
188
189
190
permInverse : : Perm s e t perm > Perm s e t perm
permInverse ( PermNative p i s ) = PermNative i p s
permInverse p
= p
191
192
permImage : : (
Monad m) =>
193
194
Perm s e t perm
> Atm
> SContT ( PermCall perm ) m Atm
195
196
permImage
PermId
c =
197
198
199
200
201
202
permImage
( PermSwap a b )
c =
return
c
if c == a
then return b
else if c == b
then return
else return
203
202
a
c
A.6 Sets and Permutation Layer
204
permImage
( PermNative p _ _) c = c a l l T $ PermImage p c
205
206
207
208
209
210
permImageInv : : (
Monad m) =>
Perm s e t perm
> Atm
> SContT ( PermCall perm ) m Atm
permImageInv = permImage . permInverse
211
212
213
214
215
216
217
permSwapping : : Atm > Atm > Perm s e t perm
permSwapping a b =
a == b
PermId
PermSwap a b
if
then
else
218
219
220
221
222
223
224
Monad
=>
permCompose : : (
m)
MutateSide
> Perm s e t perm
> Perm s e t perm
> SContT
( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ( Perm s e t perm )
225
226
permCompose _
PermId p =
return
p
permCompose _
p PermId =
return
p
227
228
229
230
231
232
233
permCompose ms ( PermSwap a
i a < permImageInv p
i b < permImageInv p
permSwapRight (ms ==
do
b) p =
a
b
MutateRight ) p i a i b
234
235
236
permCompose ms p ( PermSwap a b ) =
permSwapRight (ms == MutateLeft ) p a b
237
238
239
240
241
242
243
permCompose ms ( PermNative p1 i 1 _) ( PermNative p2 i 2 _) =
p3 < c a l l T $ PermCompose
ms p1 p2
i 3 < c a l l T $ PermCompose ( msFlip ms) i 2 i 1
l < c a l l T $ PermSupp p3
s3 < clv ' p1 $ setFromList l
$ PermNative p3 i 3 s3
do
return
244
203
A.6 Sets and Permutation Layer
245
246
247
248
249
Monad
250
251
=
return
permFromSwappings [ ( a , b ) ] =
return
permFromSwappings [ ]
252
253
=>
permFromSwappings : : (
m)
[ ( Atm, Atm ) ]
> SContT
( PermCall a )
( SContT ( S e t C a l l s e t ) m) ( Perm s e t a )
PermId
$ permSwapping a b
254
255
256
257
258
permFromSwappings l
where
return
= aux PermId l
aux p [ ] =
p
aux p ( ( a , b ) : l ) =
do
259
p2 < permSwapRight
aux p2 l
True
p a b
260
261
262
263
permSwapRight : : (
264
265
266
267
268
269
>
>
>
>
Bool
Monad m) =>
Perm s e t a
Atm
Atm
SContT
( PermCall a )
( SContT ( S e t C a l l s e t ) m) ( Perm s e t a )
270
271
272
273
274
275
276
permSwapRight mutate perm a b =
a == b
perm
perm
PermId
>
$ PermSwap a b
if
then return
else case
of
return
277
278
279
280
281
282
283
284
PermSwap c d
>
sameSwap ( a , b ) ( c , d )
PermId
p < permFromSwappingsNative [ ( c , d ) , ( a , b ) ]
i < permFromSwappingsNative [ ( a , b ) , ( c , d ) ]
s < partialSupp p [ a , b , c , d ]
$ PermNative p i s
if
then return
else do
return
285
204
A.6 Sets and Permutation Layer
PermNative p
pa <
pb <
p2 <
i2 <
s' <
286
do
287
288
289
290
291
i s >
c a l l T $ PermImage p a
c a l l T $ PermImage p b
c a l l T $ PermSwapRight mutate p a b
c a l l T $ PermSwapRight mutate i pa pb
clv ' p1 $ s e t S e t mutate a ( a /= pb ) s
p
292
s2 <
293
294
return
295
296
297
298
299
300
b = p
(a
b)
a = p2
a
clv ' p1 $ s e t S e t mutate b ( b /= pa ) s
p
a = p
(a
b)
b = p2
b
$ PermNative p2 i 2 s2
where
Bool
sameSwap : : (Atm, Atm) > (Atm, Atm) >
sameSwap ( a , b ) ( c , d ) =
( ( a == c ) && ( b == d ) )
| | ( ( a == d ) && ( b == c ) )
301
302
303
304
permFromSwappingsNative l =
pid < c a l l T $ PermIdNative
auxFSN pid l
do
305
306
307
308
309
return
auxFSN p [ ]
=
p
auxFSN p ( ( a , b ) : l ) =
p2 < c a l l T $ PermSwapRight
auxFSN p2 l
do
True
p a b
310
311
312
partialSupp p l =
sl <
do
mapM (\ x
>
313
)
l
314
315
316
foldM
317
318
(\ s e t ( a , v )
SetEmpty
sl
do
px <
return
> clv ' p1 $ s e t S e t
319
320
321
322
323
324
325
326
Monad
c a l l T $ PermImage p x
( x , ( x /= px ) )
=>
permDS : : (
m)
Perm s e t perm
> Perm s e t perm
> SContT
( PermCall perm )
205
True
a v set )
A.6 Sets and Permutation Layer
( SContT ( S e t C a l l s e t ) m) ( Set s e t )
327
328
329
permDS p1 p2 =
do
setPermute : : (
Monad m) =>
330
331
332
p3 <
permCompose MutateNone
( permInverse p1 )
p2
clv ' p1 $ permSupp p3
333
334
335
Bool
336
337
338
339
340
> Perm s e t perm
> Set s e t
> SContT
( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ( Set s e t )
341
342
343
setPermute mutate
PermId
set =
return
set
344
345
346
setPermute mutate ( PermSwap a b ) s e t =
clv ' p1 $ setSwap mutate s e t a b
347
348
349
350
351
setPermute mutate p
set =
l < clv ' p1 $ s e t T o L i s t s e t
l' <
( permImage p ) l
clv ' p1 $ setFromList l '
do
mapM
352
353
354
355
356
357
358
Monad
=>
permUnshare : : (
m)
Perm s e t perm
> SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m)
( Perm s e t perm )
359
360
permUnshare p@PermId
=
return
p
permUnshare p@( PermSwap _ _)
=
return
p
361
362
363
364
permUnshare
( PermNative p i s ) =
do
365
366
p' <
i' <
s' <
return
367
206
c a l l T $ PermUnshare
c a l l T $ PermUnshare
clv ' p1 $ setUnshare
$ PermNative p ' i '
p
i
s
s'
A.6 Sets and Permutation Layer
368
369
370
371
372
{
Interpretation
373
374
of
Sets
and
Perms
as
Data . Map
}
375
376
i n t e r p r e t S e t P e r s i s t e n t : : S e t C a l l ( Set . Set Atm) m r
> r
377
378
379
interpretSetPersistent ( SetIsIn
k ( Set . member
a set )
set a
k) =
interpretSetPersistent ( SetSize
k ( Set . s i z e
set )
set
k) =
interpretSetPersistent ( SetIsSubset
k ( Set . i s S u b s e t O f s1 s2 )
s1 s2
k) =
380
381
382
383
384
385
386
387
388
389
390
391
392
393
i n t e r p r e t S e t P e r s i s t e n t ( SetSet
_ a v s k) =
k (
v
Set .
a s
Set .
a s
)
i n t e r p r e t S e t P e r s i s t e n t ( SetEmptyNative
k) =
k Set . empty
if
then
else
insert
delete
394
395
396
i n t e r p r e t S e t P e r s i s t e n t ( SetToList
k ( Set . t o L i s t s e t )
set
k) =
i n t e r p r e t S e t P e r s i s t e n t ( SetUnion
k ( Set .
s1 s2 )
interpretSetPersistent ( SetInter
k ( Set . i n t e r s e c t i o n s1 s2 )
_ s1 s2 k ) =
397
398
399
400
401
union
_ s1 s2 k ) =
402
403
404
405
406
407
Monad
=>
runSetPersistent : : (
m)
SContT ( S e t C a l l ( Set . Set Atm) ) m a > m a
runSetPersistent = instanciateNoT i n t e r p r e t S e t P e r s i s t e n t
408
207
A.6 Sets and Permutation Layer
409
410
411
mapImage : : (
mapImage
p
Ord
412
413
mapCompose : : (
a)
a =
=> Map . Map a a > a >
maybe a id $ Map . lookup
Ord
a)
414
415
=>
a
a p
Map . Map a a
> Map . Map a a
> Map . Map a a
416
417
418
mapCompose p1 p2 = Map . foldWithKey f p1 p2
f atm image p = Map .
atm ( mapImage p1 image ) p
where
insert
419
420
421
422
Ord
mapSupp : : (
mapSupp
p
423
424
b)
=
=> Map . Map b b > [ b ]
foldl (\ l a > if a == mapImage
then l
else a : l
p a
) [ ] (Map . keys p )
425
426
427
428
429
i n t e r p r e t P e r m P e r s i s t e n t : : PermCall (Map . Map Atm Atm) m r
430
431
432
i n t e r p r e t P e r m P e r s i s t e n t ( PermImage
k ( mapImage p a )
p a
k) =
433
434
435
436
437
438
i n t e r p r e t P e r m P e r s i s t e n t ( PermSwapRight _ p a b k ) =
k (
pa = mapImage p a
pb = mapImage p b
Map .
a pb (Map .
b pa p )
)
let
in
insert
insert
439
440
441
i n t e r p r e t P e r m P e r s i s t e n t ( PermCompose _
k $ mapCompose p1 p2
p1 p2 k ) =
i n t e r p r e t P e r m P e r s i s t e n t ( PermSupp
k $ mapSupp p
p
442
443
444
k) =
445
446
447
i n t e r p r e t P e r m P e r s i s t e n t ( PermIdNative
k
Map . empty
k) =
448
449
i n t e r p r e t P e r m P e r s i s t e n t ( PermUnshare
208
p
k) =
> r
A.6 Sets and Permutation Layer
450
k
p
451
452
453
454
455
456
Monad
=>
runPermPersistent : : (
m)
SContT ( PermCall (Map . Map Atm Atm) ) m a
>m a
457
458
runPermPersistent = i n s t a n c i a t e N o T i n t e r p r e t P e r m P e r s i s t e n t
459
460
461
462
463
464
465
466
467
Monad
=>
runNamingPersistent : : (
m)
SContT ( PermCall (Map . Map Atm Atm) )
( SContT ( S e t C a l l ( Set . Set Atm) ) m) a
>m a
runNamingPersistent = r u n S e t P e r s i s t e n t . runPermPersistent
209
A.7 Nominal Union-Find Algorithm
A.7 Nominal Union-Find Algorithm
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
GeneralizedNewtypeDeriving # }
7
8
9
10
11
12
13
module
UnionFind
import
import
import
import
Call
Naming
Term
Store
import
import
Control .
Control .
where
14
15
16
17
Monad
Monad . Trans
18
19
Abstraction
20
21
22
data
23
24
25
|
|
of
No mi na l
Datas
NominalDataCall s e t perm dat (m : : > ) r
NominalDataFresh
( Set s e t )
dat ( dat
NominalDataPermute ( Perm s e t perm ) dat ( dat
NominalDataMerge
dat dat
( dat
=
> r)
> r)
> r)
26
27
28
29
nominalDataFresh
s e t dat = c a l l T $ NominalDataFresh
s e t dat
nominalDataPermute perm dat = c a l l T $ NominalDataPermute perm dat
nominalDataMerge
dat1 dat2 = c a l l T $ NominalDataMerge
dat1 dat2
30
31
32
33
data
34
35
36
Integer
Ranked dat = Ranked { rank ' rank : :
rank '
: : dat
}
( ,
,
)
data
,
deriving Eq Show Read
37
38
39
data
UFCell s e t perm
elem
dat = I n d i r e c t ( Susp s e t perm
210
elem )
A.7 Nominal Union-Find Algorithm
40
41
| Direct
deriving ( Eq , Show , Read )
dat
42
43
44
45
unRankUFCell ( D i r e c t
( Ranked r d ) ) = D i r e c t d
unRankUFCell ( I n d i r e c t
s ) = Indirect s
46
47
48
49
50
51
52
Calls
type
for
the
arr_ { u f }
store
elem
elem
elem
elem
UFStore s e t perm s t o r e
dat m =
SContT ( S t a t e C a l l ( s t o r e
( UFCell s e t perm
( Ranked dat ) ) ) )
( SContT ( S t o r e C a l l s t o r e
) m)
53
54
55
56
elem
u f D a t a I n i t : : dat > UFCell s e t perm
u f D a t a I n i t dat = D i r e c t ( Ranked 0 dat )
( Ranked dat )
57
58
59
60
61
62
Monad
=>
ufStoreInit : : (
m)
dat
> UFStore s e t perm s t o r e
elem
dat m ( )
63
64
u f S t o r e I n i t dat = ( clv ' p1 $ s t o r e I n i t $ u f D a t a I n i t dat ) >>= s t a t e P u t
65
66
67
68
69
70
ufStoreGet : : (
elem
> UFStore s e t perm s t o r e
( UFCell s e t perm
71
72
73
elem
ufStoreGet
stateGetsM (\ s t o r e
74
75
76
77
78
ufStorePut : : (
elem
elem
elem
dat m
( Ranked dat ) )
=
> clv ' p1 $ s t o r e G e t s t o r e
Monad m) =>
elem
> UFCell s e t perm
( Ranked dat )
> UFStore s e t perm s t o r e
dat m ( )
79
80
Monad m) =>
ufStorePut
elem
elem
dat =
211
elem )
A.7 Nominal Union-Find Algorithm
81
stateModifyM (\ s t o r e
> clv ' p1 $ s t o r e P u t s t o r e
elem
82
83
84
85
86
87
88
89
ufStoreGets : : (
elem
elem
> ( UFCell s e t perm
( Ranked dat ) > a )
> UFStore s e t perm s t o r e
dat m a
90
91
Monad m) =>
ufStoreGets
elem
f
elem
= ufStoreGet
elem >>= return
92
93
94
95
96
97
98
99
100
ufStoreGetsM : : (
elem
elem
> ( UFCell s e t perm
( Ranked dat )
> UFStore s e t perm s t o r e
> UFStore s e t perm s t o r e
dat m a
elem
elem
101
102
Monad m) =>
ufStoreGetsM
elem
f
= ufStoreGet
dat m a )
elem >>=
f
103
104
105
106
107
108
109
110
111
ufStoreModify : : (
elem
114
elem
> ( UFCell s e t perm
( Ranked dat )
> UFCell s e t perm
( Ranked dat ) )
> UFStore s e t perm s t o r e
dat m ( )
112
113
Monad m) =>
ufStoreModify
ufStoreGet
elem f
elem >>=
elem
elem
=
( ufStorePut
elem )
. f
115
116
117
118
119
120
121
ufStoreModifyM : : (
elem
Monad m) =>
elem
> ( UFCell s e t perm
( Ranked dat ) >
UFStore s e t perm s t o r e
dat m
elem
212
. f
dat )
A.7 Nominal Union-Find Algorithm
elem ( Ranked
elem dat m ( )
( UFCell s e t perm
> UFStore s e t perm s t o r e
122
123
124
125
126
elem f
elem >>=
ufStoreModifyM
ufStoreGet
=
f >>= ( u f S t o r e P u t
dat ) ) )
elem )
127
128
129
130
131
132
133
134
Monad
elem
=>
ufStoreFoldM : : (
m)
(a > (
, UFCell s e t perm
dat )
> SContT ( S t o r e C a l l s t o r e
) m a)
> a
> UFStore s e t perm s t o r e
dat m a
elem
elem
elem
135
136
137
138
139
ufStoreFoldM f
store <
f' a
clv ' p1 $
do
let
a =
stateGet
( e , v ) = f a ( e , unRankUFCell v )
storeFoldM f ' a s t o r e
140
141
142
143
144
145
146
147
148
Monad
=>
elem
findRanked : : (
m)
Susp s e t perm
> UFStore s e t perm
( SContT ( PermCall
( SContT ( S e t C a l l
( Susp s e t perm
elem
store
dat
perm )
s e t ) m) )
(
, Ranked dat ) )
elem
149
150
151
152
153
154
findRanked ( Susp perm var ) =
vc < u f S t o r e G e t var
vc
Direct d
>
$ Susp perm ( var , d )
do
case
of
return
155
156
157
158
159
160
161
Indirect s
>
do
Susp p ( r , d ) < findRanked s
u f S t o r e P u t var $ I n d i r e c t ( Susp p r )
p2 < clv ' p2 $ permCompose MutateNone
perm
p
$ Susp p2 ( r , d )
return
162
213
A.7 Nominal Union-Find Algorithm
163
164
165
166
find
167
168
169
170
171
172
173
find
Monad
=>
:: (
m)
Susp s e t perm
> UFStore s e t perm
( SContT ( PermCall
( SContT ( S e t C a l l
( Susp s e t perm
s =
do
174
elem
elem
store
dat
perm )
s e t ) m) )
(
, dat ) )
elem
Susp p ( v , ( Ranked r d ) ) <
$ Susp p ( v , d )
return
findRanked s
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
Monad
=>
ufDataModify : : (
m)
( dat
> SContT ( NominalDataCall s e t perm dat )
( UFStore s e t perm s t o r e
dat
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) )
dat )
> Susp s e t perm
> SContT ( NominalDataCall s e t perm dat )
( UFStore s e t perm s t o r e
dat
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) )
()
elem
elem
elem
190
191
192
193
194
195
196
ufDataModify f s =
Susp p ( r , Ranked rk d )
dp
d2
d3
clv ' p1 $ u f S t o r e P u t r $
do
< clv ' p1 $ findRanked s
< nominalDataPermute p d
< f d
< nominalDataPermute ( permInverse p ) d2
D i r e c t ( Ranked rk d3 )
197
198
199
200
{
pr1
.
r1 = pr2
.
r2
201
202
203
A1 # r 1 = n1
=>
p3 ^
.
r2
1(A1 ) # r 2 = p3^
1
n1
,
214
p3 = p r 1 ^
1
.
pr2
A.7 Nominal Union-Find Algorithm
204
}
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
union
Monad
Eq elem =>
elem
elem
:: (
m,
)
Susp s e t perm
> Susp s e t perm
> SContT ( NominalDataCall s e t perm dat )
( UFStore s e t perm s t o r e
dat
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) )
()
elem
union s1 s2 =
do Susp p1 ( r1
, Ranked rk1 d1 ) < clv ' p1 $ findRanked s1
Susp p2 ( r2 , Ranked rk2 d2 ) < clv ' p1 $ findRanked s2
r1 == r2
ds < clv ' p3 $ permDS p1 p2
d3 < nominalDataFresh ds d1
clv ' p1 $ u f S t o r e P u t r1 ( D i r e c t $ Ranked rk1 d3 )
rk1 rk2
>
clv ' p1 $ u f S t o r e P u t r2
( D i r e c t $ Ranked ( rk2 + 1) d2 )
( Susp p1 r1 ) ( Susp p2 r2 )
>
( Susp p2 r2 ) ( Susp p1 r1 )
>
p3 < clv ' p3 $ permCompose MutateNone
( permInverse p1 )
p2
clv ' p1 $ u f S t o r e P u t r1 $ I n d i r e c t ( Susp p3 r2 )
ufDataModify (\ d > nominalDataMerge d d1 )
( Susp PermId r1 )
if
then do
else case compare
EQ do
GT
LT
of
union
union
do
234
235
236
237
238
239
240
241
242
243
Monad
Eq elem =>
unions : : (
m,
)
[ Susp s e t perm
> SContT
( NominalDataCall s e t perm dat )
( UFStore s e t perm s t o r e
dat
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) )
( Susp s e t perm (
, dat ) )
elem
elem
244
215
elem ]
A.7 Nominal Union-Find Algorithm
245
246
247
248
error
unions [ ]
=
"Empty l i s t o f union f i n d s "
unions [ x ]
= clv ' p1 $
x
unions ( x : ( y : l ) ) =
x y
unions ( y : l )
do union
find
249
250
251
252
253
254
255
256
257
Monad
Ord elem =>
runUFStoreP : : (
m,
)
dat
> SContT ( S t a t e C a l l ( StoreP
( UFCell s e t perm elem1 ( Ranked dat ) ) ) )
( SContT ( S t o r e C a l l StoreP
) m) a
>m a
elem
elem
258
259
260
261
runUFStoreP dat m =
runStoreP $
s t o r e < s t o r e I n i t $ u f D a t a I n i t dat
runStateCall store m
do
216
A.8 Unication
A.8 Unication
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
GeneralizedNewtypeDeriving ,
7
ExistentialQuantification
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module
where
Unification
import
import
import
import
import
import
Call
Naming
Term
UnionFind
Store
Error
import
import
Control .
Control .
# }
Monad
Monad . I d e n t i t y
import qualified
import qualified
Data . Set as Set
Data . Map as Map
24
25
26
SPECIALIZED UNION FIND TO UNIFICATION
27
28
29
data
30
31
32
33
34
NominalDataUnif s e t perm = NominalDataUnif
{ ndu ' o c c u r s : :
,
ndu ' s e t
: : Set s e t ,
ndu ' terms : : LazyTermSet s e t perm
}
( ,
,
)
Int
deriving Eq Show Read
35
36
37
38
type
UFStoreUnif s e t perm s t o r e m =
UFStore s e t perm s t o r e Var ( NominalDataUnif s e t perm ) m
39
217
A.8 Unication
40
41
42
43
44
Monad
=>
nominalDataUnifFresh : : (
m)
Set s e t
> NominalDataUnif s e t perm
> SContT ( S e t C a l l s e t ) m ( NominalDataUnif s e t perm )
45
46
47
48
nominalDataUnifFresh s ( NominalDataUnif o c c u r s s e t terms ) =
s2 < setUnion MutateNone s s e t
$ NominalDataUnif o c c u r s s2 terms
do
return
49
50
51
52
53
54
55
56
Monad
=>
nominalDataUnifPermute : : (
m)
Perm s e t perm
> NominalDataUnif s e t perm
> SContT
( PermCall perm )
( SContT ( S e t C a l l s e t ) m)
( NominalDataUnif s e t perm )
57
58
59
60
nominalDataUnifPermute perm ( NominalDataUnif o c c u r s s e t terms ) =
s2 < setPermute
perm s e t
$ NominalDataUnif o c c u r s s2 ( LtsRemap perm terms )
do
False
return
61
62
63
64
65
66
nominalDataUnifMerge : :
NominalDataUnif
> NominalDataUnif
> SContT ( S e t C a l l
Monad
=>
(
m)
s e t perm
s e t perm
s e t ) m ( NominalDataUnif s e t perm )
67
68
69
70
71
72
73
nominalDataUnifMerge
do
s3 <
return
( NominalDataUnif o c c u r s 1 s e t 1 terms1 )
( NominalDataUnif o c c u r s 2 s e t 2 terms2 ) =
setUnion MutateNone s e t 1 s e t 2
$ NominalDataUnif ( o c c u r s 1 + o c c u r s 2 )
s3
( l t s M e r g e terms1 terms2 )
74
75
76
77
78
79
80
Monad
Monad
=>
interpretNominalDataUnif : : (
m1,
m)
( SContT ( PermCall perm )
( SContT ( S e t C a l l t1 ) m1) ( NominalDataUnif t1 perm )
> m ( NominalDataUnif t1 perm ) )
218
A.8 Unication
81
82
> NominalDataCall t1 perm ( NominalDataUnif t1 perm ) t (m b )
>m b
83
84
85
i n t e r p r e t N o m i n a l D a t a U n i f l v ( NominalDataFresh
s e t dat k ) =
( l v $ clv ' p1 $ nominalDataUnifFresh
s e t dat ) >>= k
86
87
88
i n t e r p r e t N o m i n a l D a t a U n i f l v ( NominalDataPermute perm dat k ) =
( lv $
nominalDataUnifPermute perm dat ) >>= k
89
90
91
i n t e r p r e t N o m i n a l D a t a U n i f l v ( NominalDataMerge dat1 dat2 k ) =
( l v $ clv ' p1 $ nominalDataUnifMerge
dat1 dat2 ) >>= k
92
93
94
findUnif =
find
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
Monad
=>
ufDataModifyUnif : : (
m1)
( NominalDataUnif s e t perm
>
SContT
( NominalDataCall s e t
perm
( NominalDataUnif s e t perm )
)
( UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m1 ) ) )
( NominalDataUnif s e t perm )
)
> Susp s e t perm Var
> UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m1) )
()
114
115
116
117
ufDataModifyUnif f s =
i n s t a n c i a t e N o T ( i n t e r p r e t N o m i n a l D a t a U n i f clv ' p2 )
( ufDataModify f s )
118
119
120
121
Monad
=>
unionUnif : : (
m)
Susp s e t perm Var
219
A.8 Unication
122
123
124
125
> Susp s e t perm Var
> UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) ( )
126
127
128
129
unionUnif s1 s2 =
i n s t a n c i a t e N o T ( i n t e r p r e t N o m i n a l D a t a U n i f clv ' p2 )
(
s1 s2 )
union
130
131
132
133
nominalDataUnifNeutral : : NominalDataUnif s e t perm
nominalDataUnifNeutral = NominalDataUnif 0 SetEmpty L t s N i l
134
135
136
137
138
139
140
141
Monad
=>
unionsUnif : : (
m)
[ Susp s e t perm Var ]
> UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) )
( Susp s e t perm ( Var , NominalDataUnif s e t perm ) )
142
143
unionsUnif l
144
= i n s t a n c i a t e N o T ( i n t e r p r e t N o m i n a l D a t a U n i f clv ' p2 )
( unions l )
145
146
147
148
149
150
151
Monad
=>
nominalDataUnifOccurAdd : : (
m)
Var
>
> UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) ( )
Int
152
153
154
155
nominalDataUnifOccurAdd v n = ufDataModifyUnif f ( Susp PermId v )
where
f d = nominalDataMerge d ( NominalDataUnif n SetEmpty L t s N i l )
156
157
158
159
Monad
=>
ufStoreInitUnif : : (
m)
UFStoreUnif s e t perm s t o r e m ( )
160
161
u f S t o r e I n i t U n i f = u f S t o r e I n i t nominalDataUnifNeutral
162
220
A.8 Unication
163
164
165
166
167
168
169
170
Monad
Ord elem =>
elem
runUFStoreUnifP : : (
m,
)
SContT ( S t a t e C a l l ( StoreP
( UFCell s e t 1 perm1 elem1
( Ranked ( NominalDataUnif s e t perm ) ) ) ) )
( SContT ( S t o r e C a l l StoreP
) m)
a
>m a
elem
171
172
runUFStoreUnifP = runUFStoreP nominalDataUnifNeutral
173
174
THE UNIFICATION ALGORITHM
175
176
177
data
178
179
180
181
Equation s e t
{ eqSet
::
eqTerms : :
}
( ,
perm = Equation
Set s e t ,
[ Term s e t perm ]
deriving Eq Show , Ord , Read )
182
183
184
185
186
187
data
OutputSolution s e t perm =
OutputSubst Var (Term s e t perm )
| OutputFresh Var ( Set s e t )
(
,
, ,
)
deriving Show Read Eq Ord
188
189
190
191
192
Monad
iterM : : (
m)
iterM f [ ]
=
iterM f ( x : l ) =
193
=> ( t
return
do f x
> m a)
()
> [t]
> m ()
iterM f l
194
195
196
197
198
199
200
Monad
201
202
203
=>
splitEq : : (
m)
[ Term s e t perm ]
> SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m)
( [ Susp s e t perm Var ] , [ Term s e t perm ] )
splitEq [ ]
=
splitEq ( t : l ) =
return ( [ ] , [ ] )
do Susp p u <
termHeadPerm t
221
A.8 Unication
( vs , t s ) < s p l i t E q l
u
Leaf ( Var v ) >
_
>
204
case
205
206
207
of
return
return
( ( ( Susp p v ) : vs ) , t s )
( vs , ( perm p u ) : t s )
208
209
210
211
212
213
214
215
216
217
Monad
=>
decompose : : (
m)
Equation s e t perm
> SContT
( S t a c k C a l l s t a c k ( Equation s e t perm ) )
( SContT
( OutputCall ( OutputSolution s e t perm ) )
( UFStoreUnif s e t perm s t o r e
( SContT
( PermCall perm )
( SContT
( SetCall set )
( SContT
( ErrorCall [
] ) m) ) ) ) ) ( )
Char
218
219
decompose ( Equation f s [ ]
) =
return
()
220
221
222
223
224
225
226
decompose ( Equation f s ( t : q ) ) =
u < clv ' p4 $ permStepDown t
l <
( clv ' p4 . permStepDown ) q
u
Leaf ( Var _)
>
clv ' p6 $ r a i s e E r r o r " Error : head v a r i a b l e s "
do
mapM
case of
227
228
229
230
Leaf (Atm a )
val <
do
>
clv ' p5 $ s e t I s I n f s a
v a l ( clv ' p6 $ r a i s e E r r o r " Error : F r e s h n e s s Cst " )
iterM ( clv ' p6 . ( checkLeaf $ Atm a ) ) l
when
231
232
233
234
Leaf ( Cst c )
>
iterM ( clv ' p6 . ( checkLeaf $ Cst c ) ) l
235
236
237
Nominal (NPerm _) _ >
clv ' p6 $ r a i s e E r r o r "permStepDown did Wrong ! "
238
239
240
241
242
243
244
Nominal ( Abs
a) _ >
( atms , terms ) < clv ' p6 $ a l l A b s a ( u : l )
f s e t a = clv ' p5 $ s e t S e t
a
fs1 <
f f s atms
f s 2 < clv ' p5 $ s e t S e t
a
fs1
stackPush ( Equation f s 2 terms )
do
let
False
foldM
False
222
False
True
set
A.8 Unication
245
Pair t1 t2
>
( l1 , l 2 ) < clv ' p6 $ a l l P a i r ( u : l )
stackPush ( Equation f s l 1 )
stackPush ( Equation f s l 2 )
246
do
247
248
249
250
251
252
where
checkLeaf : : (
Monad m) =>
253
254
255
256
checkLeaf l 1 ( Leaf l 2 ) =
Leaf
> Term s e t perm
> SContT ( E r r o r C a l l
when
257
258
checkLeaf _ _
259
260
allPair : : (
=
Monad m) =>
263
265
allPair =
foldM
267
( l 1 /= l 2 )
( r a i s e E r r o r "Not the same l e a f ! " )
r a i s e E r r o r "Not the same l e a f ! "
String
262
266
(\( ll , l r ) t >
t
Pair t1 t2
case
of
return
268
>
( ( t1 : l l ) , ( t2 : l r ) )
269
_
270
>
r a i s e E r r o r "Not a Pair "
271
) ([] ,[])
272
273
274
allAbs : : (
Monad m) =>
275
277
278
279
280
allAbs a l =
mapM ( checkAbs
281
282
283
284
285
Atm
> [ Term s e t perm ]
> SContT ( E r r o r C a l l
)
m
( [ Atm ] , [ Term s e t perm ] )
String
276
checkAbs : : (
m ()
[ Term t t1 ]
> SContT ( E r r o r C a l l
)
m
( [ Term t t1 ] , [ Term t t1 ] )
261
264
String )
Monad m) =>
a ) l >>=
return
.
unzip
Atm
> Term t t1
> SContT ( E r r o r C a l l
m (Atm, Term t t1 )
String )
223
A.8 Unication
286
checkAbs a ( Nominal ( Abs b ) u ) =
( b , ( swap a b u ) )
checkAbs a _
=
r a i s e E r r o r "Not un a b s t r a c t i o n "
287
return
288
289
290
291
292
293
294
295
296
297
298
299
300
301
Monad
=>
checkLastOccurence : : (
m)
Var
> SContT
( S t a c k C a l l s t a c k ( Equation s e t perm ) )
( SContT ( OutputCall ( OutputSolution s e t perm ) )
( UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) ) ) ( )
302
303
304
305
306
307
308
checkLastOccurence v =
Susp p ( r , d ) < clv ' p2 $ f i n d U n i f $ Susp PermId v
( ndu ' o c c u r s d == 1)
(
terms < clv ' p4 $ f l a t t e n L T S $ ndu ' terms d
terms
[]
> clv ' p1 $ output ( OutputFresh v ( ndu ' s e t d ) )
do
when
do
case
of
309
t : l
310
>
do
311
312
let
313
314
315
)
stackPush ( Equation ( ndu ' s e t d ) terms )
clv ' p1 $ output ( OutputSubst v t )
return
f _=
nominalDataUnifNeutral
clv ' p2 $ ufDataModifyUnif f ( Susp PermId v )
316
317
318
319
320
321
322
323
324
325
326
Monad
=>
storeEquation : : (
m)
Susp s e t perm Var
> Equation s e t perm
> SContT
( S t a c k C a l l s t a c k ( Equation s e t perm ) )
( SContT ( OutputCall ( OutputSolution s e t perm ) )
( UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) ) ) ( )
224
A.8 Unication
327
328
329
330
331
332
333
334
s t o r e E q u a t i o n ( Susp p v ) ( Equation f s terms ) =
i
= permInverse p
ifs
< clv ' p4 $ setPermute
i fs
d d i f f = NominalDataUnif 0 i f s ( LtsRemap i ( toLTS terms ) )
f d
= nominalDataMerge d d d i f f
clv ' p2 $ ufDataModifyUnif f ( Susp p v )
checkLastOccurence v
do let
False
let
let
335
336
337
338
339
340
341
occurCheck : : (
UFStore
set
perm
store
Monad m) =>
elem
342
( NominalDataUnif s e t 1 perm1 )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
] ) m) ) )
()
343
344
345
Char
346
347
348
349
350
351
352
occurCheck = ufStoreFoldM f ( )
where
f ( ) (_ , I n d i r e c t _) =
()
return
353
354
355
356
f ( ) (_ , D i r e c t d ) =
( ndu ' o c c u r s d > 0)
( clv ' p3 $ r a i s e E r r o r " OccurCheck F a i l e d " )
when
357
358
359
360
361
362
363
364
365
Monad
=>
solveLoop : : (
m)
SContT ( S t a c k C a l l s t a c k ( Equation s e t perm ) )
( SContT ( OutputCall ( OutputSolution s e t perm ) )
( UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
] ) m) ) ) ) ) ( )
Char
366
367
solveLoop =
225
A.8 Unication
368
369
370
371
372
373
374
375
376
377
378
379
380
381
do
meq < stackPop
meq
> clv ' p2 $ occurCheck
eq >
Equation f s l t = eq
( s u s p s , terms ) < clv ' p4 $ s p l i t E q l t
susps
decompose
( Equation f s terms )
Susp p ( v ,_) < clv ' p2 $ u n i o n s U n i f s u s p s
clv ' p2 $ nominalDataUnifOccurAdd
v
(1
(
susps ))
s t o r e E q u a t i o n ( Susp p v )
( Equation f s terms )
solveLoop
case
of
Nothing
Just
let
do
in
if null
then
else do
length
382
383
384
385
386
387
388
389
390
391
392
393
394
Monad
=>
addOccurences : : (
m)
Term s e t perm
> UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) ( )
addOccurences ( Nominal _ t ) = addOccurences t
addOccurences ( Pair t1 t2 ) =
addOccurences t1
addOccurences t2
addOccurences ( Leaf ( Var v ) ) = nominalDataUnifOccurAdd v 1
addOccurences _
=
()
do
return
395
396
397
398
399
400
401
402
403
404
405
406
407
408
Monad
=>
unifyGen : : (
m)
Term s e t perm
> Term s e t perm
> SContT ( S t a c k C a l l s t a c k ( Equation s e t perm ) )
( SContT ( OutputCall ( OutputSolution s e t perm ) )
( UFStoreUnif s e t perm s t o r e
( SContT ( PermCall perm )
( SContT ( S e t C a l l
set
)
( SContT ( E r r o r C a l l [
] ) m) ) ) ) )
Char
226
A.8 Unication
409
()
410
411
unifyGen s t =
do
412
413
414
415
clv ' p2 $ u f S t o r e I n i t U n i f
clv ' p2 $ addOccurences s
clv ' p2 $ addOccurences t
stackPush ( Equation SetEmpty [ s , t ] )
solveLoop
416
417
418
419
420
421
422
unifyP : :
Term ( Set . Set Atm) (Map . Map Atm Atm)
> Term ( Set . Set Atm) (Map . Map Atm Atm)
>
[
] [ OutputSolution ( Set . Set Atm) (Map . Map Atm Atm ) ]
Either Char
423
424
425
426
427
428
429
430
431
432
unifyP s t = r u n I d e n t i t y
$
runErrorCallErrorT $
runSetPersistent
$
runPermPersistent $
runUFStoreUnifP
$
(_, s o l ) < runOutputP
$
runStackStateT $
unifyGen s t
sol
do
return
227
A.9 Environment Layer
A.9 Environment Layer
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
ExistentialQuantification
7
8
9
10
11
12
13
14
module
Env
import
import
Call
Naming
import
import
Control .
Control .
# }
where
Monad
Monad . S t a t e
15
16
17
18
19
20
21
22
23
data OpenClose = Open
deriving ( Eq , Ord )
data S i d e = S L e f t |
deriving ( Eq , Ord )
data
24
| Close
SRight
EnvAction s e t perm =
EnvCompose S i d e ( Perm s e t perm )
| EnvSetFresh Atm
Bool
25
26
27
28
29
30
31
32
33
34
35
data
EnvCall s e t perm (m : : > ) r
EnvPerm
| EnvImage
Atm
| EnvImageInv Atm
| EnvIsFresh Atm
| EnvSet
| EnvLocal OpenClose
| EnvAction ( EnvAction s e t perm )
=
( Perm s e t perm
(Atm
(Atm
(
( Set s e t
(()
(()
36
37
38
envPerm
= c a l l T $ EnvPerm
39
228
Bool
>
>
>
>
>
>
>
r)
r)
r)
r)
r)
r)
r)
A.9 Environment Layer
40
envSet
= c a l l T $ EnvSet
41
42
envImage
a
= c a l l T $ EnvImage
a
envImageInv
a
= c a l l T $ EnvImageInv
a
envIsFresh
a
= c a l l T $ EnvIsFresh
a
envComposeLeft
perm = c a l l T $ EnvAction
43
44
45
46
47
48
49
50
51
( EnvCompose
SLeft
perm
)
52
53
envComposeRight perm = c a l l T $ EnvAction
( EnvCompose
SRight
perm
)
envSetFresh
a v
( EnvSetFresh a v )
envLocal l v m =
do
54
55
56
57
58
= c a l l T $ EnvAction
59
60
61
62
63
l v $ c a l l T $ EnvLocal Open
r < m
l v $ c a l l T $ EnvLocal Close
r
return
64
65
66
67
68
69
70
THE INTERPRETATION FUNCTION
type
StateEnv s e t perm =
( ( Perm s e t perm ) , Set s e t , [ [ EnvAction s e t perm ] ] )
71
72
73
74
Monad
75
76
mygets : : (
Monad m) =>
77
78
mygets
= gets
79
80
=>
myget : : (
m)
StateT ( StateEnv s e t perm ) m ( StateEnv s e t perm )
myget
= get
myput : : (
( StateEnv s e t perm > a )
> StateT ( StateEnv s e t perm ) m a
Monad m) =>
StateEnv s e t perm
229
A.9 Environment Layer
> StateT ( StateEnv s e t perm ) m ( )
81
82
myput
= put
83
84
mymodify : : (
Monad m) =>
( StateEnv s e t perm > StateEnv s e t perm )
> StateT ( StateEnv s e t perm ) m ( )
85
86
mymodify = modify
87
88
89
liftStateEnvT : : (
Monad m) =>
90
91
liftStateEnvT = l i f t
m a
> StateT ( StateEnv s e t perm ) m a
92
93
94
95
96
97
98
99
getStatePerm
getStateSet
getStateUndo
setStatePerm p
setStateSet s
setStateUndo u
=
=
=
=
=
=
mygets ( \ ( p ,_,_) >
mygets ( \ (_, s ,_) >
mygets ( \ (_,_, u ) >
mymodify ( \ (_, s , u )
mymodify ( \ ( p ,_, u )
mymodify ( \ ( p , s ,_)
p)
s)
u)
> (p , s , u ))
> (p , s , u ))
> (p , s , u ))
100
101
102
modifyStatePerm f =
do
modifyStateSet
f =
do
modifyStateUndo f =
do
103
104
105
106
107
108
109
110
111
112
( p , s , u ) < myget
p' < f p
myput ( p ' , s , u )
( p , s , u ) < myget
s' < f s
myput ( p , s ' , u )
( p , s , u ) < myget
u' < f u
myput ( p , s , u ' )
113
114
115
116
117
118
Monad
=>
clv ' perm : : (
m)
m a
> StateT ( StateEnv s e t perm )
( SContT t h e c l a s s m) a
119
120
clv ' perm = l i f t S t a t e E n v T . clv ' p1
121
230
A.9 Environment Layer
122
123
124
125
126
Monad
=>
clv ' s e t : : (
m)
m a
> StateT ( StateEnv s e t perm )
( SContT t h e c l a s s ( SContT t h e c l a s s 1 m) ) a
127
128
clv ' s e t
= l i f t S t a t e E n v T . clv ' p2
129
130
131
132
133
134
135
136
137
Monad
=>
runEnvAction : : (
m)
EnvAction s e t perm
> StateT ( StateEnv s e t perm )
( SContT t h e c l a s s
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) )
()
138
139
140
141
142
runEnvAction ( EnvCompose S L e f t p ) =
modifyStatePerm (\ perm > clv ' perm $
permCompose MutateRight p perm
)
143
144
145
146
147
148
149
150
151
152
153
runEnvAction ( EnvCompose SRight p ) =
modifyStatePerm (\ perm > clv ' perm $
permCompose MutateLeft perm p
)
m o d i f y S t a t e S e t (\ s e t
>
p ' = permInverse p
s e t ' < clv ' perm $
setPermute
set '
)
do
do let
return
154
155
156
157
158
runEnvAction ( EnvSetFresh
a v) =
m o d i f y S t a t e S e t (\ s e t
> clv ' s e t $
setSet
a v set
)
True
159
160
161
162
envOppositeAction : : (
Monad m) =>
231
True
p ' set
A.9 Environment Layer
163
164
165
166
167
168
EnvAction s e t 1 perm1
> StateT ( StateEnv s e t perm )
( SContT t h e c l a s s
( SContT t h e c l a s s 1
( SContT ( S e t C a l l s e t ) m) ) )
( EnvAction s e t 1 perm1 )
169
170
171
172
envOppositeAction ( EnvCompose s i d e
p ' = permInverse p
$ EnvCompose s i d e p '
do let
return
p
) =
173
174
175
176
177
envOppositeAction ( EnvSetFresh a
_
set < getStateSet
v
< clv ' s e t $ s e t I s I n s e t a
$ EnvSetFresh a v
do
) =
return
178
179
180
181
182
183
184
185
186
187
188
189
Monad
=>
interpretEnvT : : (
m)
EnvCall s e t perm t
( SContT t h e c l a s s
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) b )
> StateT ( StateEnv s e t perm )
( SContT t h e c l a s s
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t ) m) ) )
b
190
191
192
193
interpretEnvT ( EnvPerm
k) =
perm < getStatePerm
l i f t S t a t e E n v T $ k perm
do
194
195
196
197
interpretEnvT ( EnvSet
set < getStateSet
liftStateEnvT $ k set
do
k) =
198
199
200
201
202
interpretEnvT ( EnvImage
a k) =
perm < getStatePerm
a'
< clv ' perm $ permImage
liftStateEnvT $ k a '
do
203
232
perm a
A.9 Environment Layer
204
205
206
207
interpretEnvT ( EnvImageInv a k ) =
perm < getStatePerm
a'
< clv ' perm $ permImageInv
liftStateEnvT $ k a '
do
perm a
208
209
210
211
212
interpretEnvT ( EnvIsFresh a k ) =
set < getStateSet
v
< clv ' s e t $ s e t I s I n
liftStateEnvT $ k v
do
set
a
213
214
215
216
interpretEnvT ( EnvLocal Open k ) =
modifyStateUndo (\ l >
liftStateEnvT $ k ()
do
return
( [ ] : l ))
217
218
219
220
221
222
223
224
225
interpretEnvT ( EnvLocal Close k ) =
undo < getStateUndo
(x , l ) <
undo
[]
>
"Undo l i s t e r r o r "
(x : l ) >
$ (x , l )
setStateUndo l
forM x runEnvAction
liftStateEnvT $ k ()
do
case
of
error
return
226
227
228
229
230
231
232
233
234
235
interpretEnvT ( EnvAction a c t k ) =
modifyStateUndo
(\ l >
l
[]
>
"Undo l i s t e r r o r "
(x : l2 ) >
act ' < envOppositeAction a c t
$ ( act ' : x ) : l 2
)
runEnvAction a c t
liftStateEnvT $ k ()
do
case
of
error
do
return
236
237
238
239
240
241
242
243
244
Monad m) =>
runEnv : : (
SContT
( SContT
( SContT
( SContT
> SContT
( SContT
( EnvCall s e t perm )
theclass
( PermCall perm )
( S e t C a l l s e t ) m) ) ) a
theclass
( PermCall perm )
233
A.9 Environment Layer
245
( SContT ( S e t C a l l s e t ) m) ) a
246
247
248
249
runEnv c =
do
v <
runStateT ( i n s t a n c i a t e T interpretEnvT c )
( PermId , SetEmpty , [ ] )
$
v
return
fst
234
A.10 -equivalence and Matching Algorithms
A.10 -equivalence and Matching Algorithms
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
ExistentialQuantification
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module
Matching
import
import
import
import
import
import
import
Base
Naming
Call
Term
Env
Error
Store
import
import
import
Control .
Control .
Control .
# }
where
Monad
Monad . I d e n t i t y
Monad . S t a t e
21
22
23
24
import qualified
import qualified
Data . Map as Map
Data . Set as Set
25
26
27
28
29
30
31
32
33
34
35
runBasePersistent : :
SContT ( PermCall (Map . Map Atm Atm) )
( SContT ( S e t C a l l ( Set . Set Atm) )
( SContT ( E r r o r C a l l e ) I d e n t i t y ) ) a
>
e a
runBasePersistent = runIdentity .
runErrorCallErrorT .
runSetPersistent .
runPermPersistent
Either
36
37
38
Freshness
Context
Calls
39
235
A.10 -equivalence and Matching Algorithms
40
41
42
43
44
data
FreshContextCall s e t (m : : > ) r =
F r e s h C o n s t r a i n t Var ( Set s e t ) ( ( )
| FreshContextRule Var
( Set s e t
| FreshToList
( [ ( Var , Set s e t ) ]
> r)
> r)
> r)
45
46
47
48
49
50
51
Monad
52
53
54
55
56
Monad
59
60
=>
freshContextRule : : (
m)
Var
> SContT ( FreshContextCall s e t ) m ( Set s e t )
f r e s h C o n t e x t R u l e v = c a l l T $ FreshContextRule v
57
58
=>
freshConstraint : : (
m)
Var
> Set s e t
> SContT ( FreshContextCall s e t ) m ( )
freshConstraint v s = callT $ FreshConstraint v s
Monad
=>
freshToList : : (
m)
SContT ( FreshContextCall s e t ) m [ ( Var , Set s e t ) ]
freshToList
= callT
FreshToList
61
62
63
64
The
algorithms
65
66
67
68
69
70
71
72
73
74
Monad m) =>
checkAtom : : (
Atm
> SContT
( SContT
( SContT
( SContT
( SContT
Atm
75
76
77
78
79
checkAtom a =
( EnvCall s e t perm )
( FreshContextCall s e t )
( PermCall perm )
( SetCall set )
( ErrorCall [
] ) m) ) ) )
Char
do
f r s < envIsFresh a
frs
clv ' p4 $ r a i s e E r r o r " F r e s h n e s s e r r o r "
envImage a
if
then
else
80
236
A.10 -equivalence and Matching Algorithms
81
82
ENVIRONMENT ELIMINATION PHASE
83
84
85
86
87
88
89
90
91
Monad
=>
unEnvVar : : (
m)
Var
> SContT ( EnvCall s e t perm )
( SContT ( FreshContextCall s e t ) m) (Term s e t perm )
unEnvVar v =
p < envPerm
s < envSet
clv ' p1 $ f r e s h C o n s t r a i n t v s
$ perm p ( var v )
do
return
92
93
94
95
96
CHECK TERM PHASE
data
StreamCall a b (m : :
97
98
99
Monad
>
)
r = StreamStep a ( b
> r)
=>
streamStep : : (
m)
a > SContT ( StreamCall a b ) m b
streamStep a = c a l l T $ StreamStep a
100
101
102
103
104
105
106
107
108
109
Monad
=>
checkTerm : : (
m)
Term s e t perm
> SContT ( StreamCall Var (Term s e t perm ) )
( SContT ( EnvCall s e t perm )
( SContT ( FreshContextCall s e t )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
] ) m) ) ) ) ) (Term s e t perm )
Char
110
111
112
113
114
115
116
117
118
119
120
121
checkTerm
( Nominal (NPerm p ) t ) =
envLocal clv ' p1 (
clv ' p1 $ envComposeRight p
checkTerm t
)
checkTerm
( Nominal ( Abs
a) t ) =
b < clv ' p1 $ envImage a
u < envLocal clv ' p1 (
clv ' p1 $ envSetFresh a
checkTerm t
)
( Nominal ( Abs b ) u )
checkTerm
( Pair s t )
=
s ' < checkTerm s
do
do
do
return
do
237
False
A.10 -equivalence and Matching Algorithms
t' <
checkTerm t
( Pair s ' t ' )
=
a2 < clv ' p1 $ checkAtom a
$ atm a2
= streamStep v
=
t
122
123
124
checkTerm
do
( Leaf (Atm a ) )
125
126
127
checkTerm
( Leaf ( Var v ) )
checkTerm t@ ( Leaf ( Cst c ) )
return
return
return
128
129
130
131
DECOMPOSITION PHASE
132
133
134
135
136
137
138
139
140
141
Monad
=>
decompose : : (
m)
Term s e t perm
> Term s e t perm
> SContT ( StreamCall ( Var , Term s e t perm ) ( ) )
( SContT ( EnvCall s e t perm )
( SContT ( FreshContextCall s e t )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
] ) m) ) ) ) )
()
Char
142
143
144
145
146
decompose ( Nominal (NPerm p ) t )
u =
envLocal clv ' p1 (
clv ' p1 $ envComposeLeft ( permInverse p )
decompose t u
)
do
147
148
149
decompose t
envLocal clv ' p1 (
150
)
151
do
( Nominal (NPerm p ) u ) =
clv ' p1 $ envComposeRight p
decompose t u
152
153
154
155
156
157
158
159
160
decompose ( Nominal ( Abs a ) t ) ( Nominal ( Abs b ) u ) =
a2 < clv ' p1 $ envImageInv a
b2 < clv ' p1 $ envImage
b
envLocal clv ' p1 (
clv ' p1 $ envSetFresh a2
clv ' p1 $ envSetFresh b
clv ' p1 $ envComposeLeft ( permSwapping a b2 )
decompose t u
)
do
do
True
False
161
162
decompose ( Pair
s t ) ( Pair
238
u v) =
A.10 -equivalence and Matching Algorithms
163
do
164
decompose s u
decompose t v
165
166
167
168
169
170
171
decompose ( Leaf ( Cst
f ) ) ( Leaf ( Cst
g )) =
( f /= g ) ( clv ' p5 $ r a i s e E r r o r $ " Error : " ++
(
f)
++
" /= "
++
(
g)
)
when
show
show
172
173
174
175
decompose ( Leaf (Atm
a ) ) ( Leaf (Atm
b)) =
c < clv ' p1 $ checkAtom b
( a /= c ) ( clv ' p5 $ r a i s e E r r o r "Atoms not e q u a l s " )
do
when
176
177
178
179
180
181
182
183
decompose ( Leaf ( Var
v))
t =
s
< clv ' p2 $ f r e s h C o n t e x t R u l e v
p
< clv ' p1
envPerm
sp < clv ' p3 $ setPermute
( permInverse p ) s
l
< clv ' p4 $ s e t T o L i s t sp
(\ a > clv ' p1 $ envSetFresh a
) l
streamStep ( v , t )
do
False
mapM
True
184
185
186
decompose
_
_ =
clv ' p5 $ r a i s e E r r o r " I n c o m p a t i b l e c o n s t r u c t o r s "
187
188
189
190
191
192
193
194
195
196
197
Monad
Char
198
199
200
201
202
203
=>
core : : (
m)
Term s e t perm
> Term s e t perm
> SContT ( StreamCall ( Var , Term s e t perm ) ( ) )
( SContT ( FreshContextCall s e t )
( SContT ( PermCall
perm )
( SContT ( S e t C a l l
set )
( SContT ( E r r o r C a l l [
] ) m) ) ) )
()
core s t =
do
(_,m) <
clv ' p1 $ runEnv $ runStateT
( instanciateT interpDec
( decompose s t )
)
(
()
return
239
A.10 -equivalence and Matching Algorithms
)
204
205
206
207
208
209
m
where
streamCheckTerm u =
i n s t a n c i a t e N o T ( \ ( StreamStep v k )
( checkTerm u )
> unEnvVar v >>= k )
210
211
212
213
214
i n t e r p D e c ( StreamStep ( v , t ) k ) =
u < l i f t S t a t e T $ streamCheckTerm t
modify (\m > m >> streamStep ( v , u ) )
liftStateT $ k ()
do
215
216
217
218
219
ALPHA
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
Monad
=>
alpha : : (
m)
Term s e t perm
> Term s e t perm
> SContT ( FreshContextCall s e t )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
] ) m) ) )
()
alpha s t = i n s t a n c i a t e N o T i n t e r p r e t A l p h a ( c o r e s t )
Char
where
i n t e r p r e t A l p h a ( StreamStep ( v , t ) k ) =
Susp p u < clv ' p1 $ termHeadPerm t
r <
u
Leaf ( Var w) >
w == v
s e t < clv ' p2 $ permSupp p
freshConstraint v set
clv ' p3 $ r a i s e E r r o r "No s u b t i t u t i o n s i n alpha "
_
> clv ' p3 $ r a i s e E r r o r "No s u b t i t u t i o n s i n alpha "
k r
do
case
of
if
then do
else
241
242
243
244
match : : (
Monad m) =>
240
A.10 -equivalence and Matching Algorithms
245
246
247
248
249
250
251
252
Term s e t perm
> Term s e t perm
> SContT ( FreshContextCall s e t )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
])
( SContT ( S t o r e C a l l s t o r e Var ) m) ) ) )
( Var >
(Term s e t perm ) )
Char
Maybe
253
254
255
256
257
258
259
260
261
262
263
264
265
match s t =
i < clv ' p4 $ s t o r e I n i t
runStateCall i $
instanciateT interpretMatch ( core s t )
s t a t e G e t >>= clv ' p5 . storeToFun
do
do
Nothing
where
i n t e r p r e t M a t c h ( StreamStep ( v , t ) k ) =
stateModifyM (\ s t o r e >
mu
< clv ' p5 $ s t o r e G e t s t o r e v
mu
>
clv ' p5 $ s t o r e P u t s t o r e v (
do
do
266
case of
Nothing
Just
Just u >
do clv ' p1 $ alpha u t
if ( ( t e r m S i z e u ) > ( t e r m S i z e t ) )
then ( clv ' p5 $ s t o r e P u t s t o r e v ( Just
else return s t o r e
267
268
269
270
271
)
clv ' p1 $ k ( )
272
273
274
275
276
277
278
279
280
281
Solving
And
Checking
Freshness
282
283
284
285
t)
Monad m) =>
instanciateFreshContextSolve : : (
SContT ( FreshContextCall s e t )
241
u))
A.10 -equivalence and Matching Algorithms
286
287
288
289
290
291
292
293
294
( SContT
( SContT
( SContT
( SContT
> SContT
( SContT
( SContT
( SContT
( SContT
( PermCall perm )
( SetCall set )
( ErrorCall [
])
( S t o r e C a l l s t o r e Var ) m) ) ) ) a
( S t a t e C a l l ( s t o r e Var ( Set s e t ) ) )
( PermCall perm )
( SetCall set )
( ErrorCall [
])
( S t o r e C a l l s t o r e Var ) m) ) ) ) a
Char
Char
295
296
297
298
299
300
301
302
303
304
305
instanciateFreshContextSolve m =
instanciateT interpretFreshContextSolve m
where
interpretFreshContextSolve ( FreshConstraint v s k) =
stateModifyM (\ s t o r e >
vs < clv ' p4 $ s t o r e G e t s t o r e v
s ' < clv ' p2 $ setUnion MutateLeft vs s
clv ' p4 $ s t o r e P u t s t o r e v s '
)
clv ' p1 $ k ( )
do
do
306
307
308
i n t e r p r e t F r e s h C o n t e x t S o l v e ( FreshContextRule v
clv ' p1 $ k SetEmpty
k) =
309
310
311
312
i n t e r p r e t F r e s h C o n t e x t S o l v e ( FreshToList
l < stateGetsM ( clv ' p4 . s t o r e T o L i s t )
clv ' p1 $ k l
do
313
314
315
316
317
318
319
320
321
322
323
324
325
326
Monad
=>
instanciateFreshContextCheck : : (
m)
s t o r e Var ( Set s e t )
> SContT ( FreshContextCall s e t )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
])
( SContT ( S t o r e C a l l s t o r e Var ) m) ) ) ) a
> SContT ( S t a t e C a l l ( s t o r e Var ( Set s e t ) ) )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
])
Char
Char
242
k) =
A.10 -equivalence and Matching Algorithms
( SContT ( S t o r e C a l l s t o r e Var ) m) ) ) )
327
a
328
329
330
331
332
333
334
335
336
337
instanciateFreshContextCheck storehypothesis m =
instanciateT interpretFreshContextCheck m
where
interpretFreshContextCheck ( FreshConstraint v s k) =
store < stateGet
vs
< clv ' p4 $ s t o r e G e t s t o r e v
b
< clv ' p2 $ s e t I s S u b S e t s vs
(
b ) ( clv ' p3 $ r a i s e E r r o r " C o n s t r a i n t s not i n c o n t e x t " )
clv ' p1 $ k ( )
do
when not
338
339
340
341
342
i n t e r p r e t F r e s h C o n t e x t C h e c k ( FreshContextRule v
k) =
s < clv ' p4 $ s t o r e G e t s t o r e h y p o t h e s i s v
clv ' p4 $ s t o r e P u t s t o r e h y p o t h e s i s v SetEmpty
clv ' p1 $ k s
do
343
344
345
346
i n t e r p r e t F r e s h C o n t e x t C h e c k ( FreshToList
l < stateGetsM ( clv ' p4 . s t o r e T o L i s t )
clv ' p1 $ k l
do
k) =
347
348
349
350
351
Alpha
and
Matching ,
Solving
and
checking
352
353
354
355
alphaSolve
s t =
instanciateFreshContextSolve
$ alpha s t
356
357
358
alphaCheck h y p o t h e s i s s t =
i n s t a n c i a t e F r e s h C o n t e x t C h e c k h y p o t h e s i s $ alpha s t
359
360
361
362
matchSolve
s t =
instanciateFreshContextSolve
$ match s t
363
364
365
matchCheck h y p o t h e s i s s t =
i n s t a n c i a t e F r e s h C o n t e x t C h e c k h y p o t h e s i s $ match s t
366
367
243
A.10 -equivalence and Matching Algorithms
368
We a s s u m e
that
the
freshness
context
is
already
369
370
371
data
372
373
374
375
376
Rule s e t perm =
Rule { r u l e ' c o n t e x t : : [ ( Var , Set s e t ) ] ,
rule ' l e f t
: : Term s e t perm ,
rule ' right
: : Term s e t perm
}
( ,
,
,
)
deriving Eq Ord Show Read
377
378
379
380
381
382
383
Rename
the
variable
of
a
rule
freshRule : :
( Var > Var )
> Rule s e t perm
> Rule s e t perm
384
385
386
387
388
f r e s h R u l e renamevar ( Rule c l r ) =
Rule (
( \ ( v , s ) > ( renamevar v , s ) ) c )
( substitute (
. var . renamevar ) l )
( substitute (
. var . renamevar ) r )
map
Just
Just
389
390
391
392
393
394
395
396
397
398
399
Monad
=>
rewriteNoFresh : : (
m)
Rule s e t perm
> Term s e t perm
> SContT ( S t a t e C a l l ( s t o r e Var ( Set s e t ) ) )
( SContT ( PermCall perm )
( SContT ( S e t C a l l s e t )
( SContT ( E r r o r C a l l [
])
( SContT ( S t o r e C a l l s t o r e Var ) m) ) ) )
(Term s e t perm )
Char
400
401
402
403
404
rewriteNoFresh ( Rule c l r ) t =
s < clv ' p4 $ s t o r e F r o m L i s t SetEmpty c
f < matchCheck s l t
$ substitute f r
do
return
405
406
407
408
244
in
the
store
A.10 -equivalence and Matching Algorithms
409
410
r e w r i t e F r e s h renamevar r u l e t =
rewriteNoFresh ( f r e s h R u l e renamevar r u l e ) t
245
A.11 Zipper Layer
A.11 Zipper Layer
1
{ # LANGUAGE RankNTypes ,
2
FlexibleInstances ,
3
MultiParamTypeClasses ,
4
NoMonomorphismRestriction ,
5
KindSignatures ,
6
ExistentialQuantification ,
7
GeneralizedNewtypeDeriving # }
8
9
10
11
12
13
14
15
16
module
Zipper
where
import
import
import
Base
Call
Term
import
Control .
Monad . S t a t e
17
18
19
type
Hole term = term
> term
20
21
22
23
24
25
26
27
f i l l H o l e : : ( Hole term )
fillHole =
id
type
data
{
28
29
30
}
> term
> term
Context term = [ Hole term ]
Zipper term
zip ' term
zip ' c o n t e x t
= Zipper
: : term ,
: : Context term
deriving (Show)
31
32
33
zipMake : : term > Zipper term
zipMake t = Zipper t [ ]
34
35
36
37
data Dir d i r = Up | Down d i r
deriving ( Eq , Ord , Read , Show)
38
39
246
A.11 Zipper Layer
40
41
42
43
44
Eq
=>
zipMove : : (
dir )
( term > [ ( d i r , ( term , Hole term ) ) ] )
> Dir d i r
> Zipper term
>
( Zipper term )
Maybe
45
46
47
zipMove _
Up
Nothing
( Zipper _
[]
) =
48
49
50
zipMove _
return
Up
( Zipper t ( c : l ) ) =
$ Zipper ( f i l l H o l e c t ) l
51
52
53
54
zipMove t d i r s (Down d ) ( Zipper t
l
(t ' , c) <
d ( tdirs t)
$ Zipper t ' ( c : l )
do
return
) =
lookup
55
56
57
58
59
60
zipUpdate : : term > Zipper term > Zipper term
zipUpdate u ( Zipper _ l ) = Zipper u l
61
62
63
64
65
66
zipDirs : :
( term > [ ( d i r , ( term , Hole term ) ) ] )
> Zipper term > [ Dir d i r ]
67
68
69
z i p D i r s t d i r s ( Zipper t [ ] ) =
z i p D i r s t d i r s ( Zipper t _ ) = Up : (
map
map
(Down .
(Down .
70
71
72
73
First
Class
Monadic
Signature
74
75
76
77
78
79
80
data
Z i p C a l l term d i r (m
ZipTerm
| ZipUpdate term
| ZipDirs
| ZipMove
( Dir d i r )
: : > )
( term
(()
( [ Dir d i r ]
(
Bool
247
r
>
>
>
>
=
r)
r)
r)
r)
fst )
fst )
( tdirs t)
( tdirs t ))
A.11 Zipper Layer
81
82
83
84
85
86
zipCallTerml
= callT
ZipTerm
zipCallUpdate t = c a l l T $ ZipUpdate t
zipCallDir
= callT
ZipDirs
zipCallMove
d = c a l l T $ ZipMove d
87
88
89
90
91
92
93
94
Monad
Eq
=>
interpretZip : : (
m,
dir )
( term > [ ( d i r , ( term , Hole term ) ) ] )
> Z i p C a l l term d i r n (m b )
> StateT ( Zipper term ) m b
95
96
97
98
99
100
i n t e r p r e t Z i p t d i r s = aux
where
aux ( ZipTerm
k) =
t < gets
' term
liftStateT $ k t
do
zip
101
102
103
104
aux ( ZipUpdate u k ) =
modify ( \ ( Zipper _ c )
liftStateT $ k ()
do
> Zipper u c )
105
106
107
108
aux ( Z i p D i r s
k) =
zipper < get
liftStateT $ k ( zipDirs tdirs zipper )
do
109
110
111
112
aux ( ZipMove d
k) =
zipper < get
r
<
do
case zipMove t d i r s d z i p p e r of
Nothing > return False
Just z > do put z
return True
113
114
115
116
liftStateT $ k r
117
118
119
120
121
Monad m, Eq
runZip : : (
( term
=>
dir )
> [ ( d i r , ( term , Hole term ) ) ] )
248
A.11 Zipper Layer
122
123
124
> term
> SContT ( Z i p C a l l term d i r ) m a
>m a
125
126
127
128
runZip t d i r s t m = runStateT ( i n s t a n c i a t e T ( i n t e r p r e t Z i p t d i r s ) m)
( zipMake t )
>>=
.
return
fst
249
Appendix B
Objective CAML Code
B.1 Quadratic Nominal Unication
1
2
3
type
type
type
4
5
(
6
(
atm = i n t
cst = int
var = i n t
Helpers
on
Arrays
)
7
8
9
10
11
12
13
14
let
17
18
19
20
23
24
x
> x)
)
1
arr2
let array ' d i f f a r r l =
let a r r 2 = Array . copy
( L i s t . i t e r ( fun ( i , e )
21
22
fun
let permuteArr f a r r =
let a r r 2 = Array . copy a r r in
( for i = 0 to ( Array . l e n g t h a r r 2 )
do a r r 2 . ( f i ) < a r r . ( i ) done ;
15
16
mkArr n = Array . i n i t n (
)
let
arr2
in
arr
> arr2 . ( i ) <
maxAtoms = 30
250
e) l ;
)
B.1 Quadratic Nominal Unication
25
let
allAtoms = mkArr maxAtoms
26
27
28
(
29
(
30
type perm = Perm of
let unPerm ( Perm p )
31
32
33
34
35
36
37
38
Perms
let
let
let
let
39
40
41
44
45
46
47
compose ( Perm p1 ) ( Perm p2 ) =
Perm ( Array . map (
x > p1 . ( p2 . ( x ) ) ) allAtoms )
fun
swap a b =
| i when i = a
| i when i = b
| i
43
let
let
int array
= p
idPerm = Perm allAtoms
image
( Perm p ) a = p . ( a )
inverse
p
= Perm ( permuteArr ( image p ) allAtoms )
let
42
)
)
function
> b
> a
> i
swapping a b = Perm ( Array . map ( swap a b ) allAtoms )
swapPermL a b p = compose ( swapping a b ) p
48
49
50
(
51
(
52
type f r s S e t = FrsSet of b o o l a r r a y
let unFrsSet ( FrsSet f s ) = f s
53
54
55
56
57
let
let
58
59
60
61
62
63
64
FrsSet
(
let
let
)
emptyFrsSet = FrsSet ( Array . map (
i s I n F r s S e t ( FrsSet f s ) a = f s . ( a )
permute
p
fs
= p( fs )
fun
_ > f a l s e ) allAtoms )
)
permute p ( FrsSet f s ) = FrsSet ( permuteArr ( image p ) f s )
combineFrsSet op ( FrsSet f s 1 ) ( FrsSet f s 2 ) =
FrsSet ( Array . map (
i > op f s 1 . ( i ) f s 2 . ( i ) ) allAtoms )
unionFrsSet = combineFrsSet ( )
let
fun
or
65
251
)
B.1 Quadratic Nominal Unication
66
67
let
f r s S e t S e t b l ( FrsSet f s ) =
FrsSet ( array ' d i f f f s ( L i s t . map (
68
69
70
71
72
73
let
let
fun
i
> ( i ,b)) l ))
frsSetAdd = f r s S e t S e t t r u e
frsSetDel = frsSetSet false
let
dsPerm p1 p2 = FrsSet ( Array . map
(
a > ( image p1 a ) <> ( image p2 a ) ) allAtoms )
fun
74
75
76
77
(
78
(
DAG
)
)
79
80
81
type
node = {
82
(
84
(
85
87
88
89
(
90
91
}
92
and
95
96
97
98
99
100
101
and
head =
|
|
|
|
|
not
Data
N om in al
mutable
Atm
Cst
Var
Abs
Pair
Perm
del_edge = {
of
of
of
of
of
of
changed
head
fathers
mutable
mutable
mutable
86
94
Data
mutable
83
93
changed
by
mutable
algorithm
the
algorithm
)
;
;
)
: bool
;
: ( ( perm node ) del_edge ) l i s t ;
: ( perm node ) o p t i o n
;
( changed
fresh_cond
atm
cst
var
atm
node
perm
the
: head
: node l i s t
deleted
edges
pointer
data
by
also
by
: frsSet
the
alogorithm )
)
node
node
node
edge_deleted : b o o l }
102
103
104
(
105
(
Node
management
)
106
252
)
B.1 Quadratic Nominal Unication
107
108
109
let
is_same_node ( n1 : node ) ( n2 : node ) = n1 == n2
let
list_node_iter f = List . i t e r (
function
n
>
110
)
111
112
113
114
let
let
115
addFrsCond r f r s
addFrsAtms r b
(
117
(
118
let rec
Edge
management
121
122
unionFrsSet r . fresh_cond f r s
frsSetAdd b r . fresh_cond
)
reduceHead r p i
| ( Perm ( pi2 , r1 ) , _ )
| (_ , Perm ( pi2 , s2 ) )
| (_ , _ )
120
()
116
119
= r . fresh_cond <
= r . fresh_cond <
if n . d e l e t e d then
else f n
s
>
>
>
match
with
=
( r . head , s . head )
reduceHead r1 ( compose ( i n v e r s e p i 2 ) p i ) s
reduceHead r ( compose p i p i 2 ) s2
( r , pi , s )
123
124
125
126
127
128
129
let add_undirected_edge r p i s =
let ( r1 , pi1 , s1 ) = reduceHead r p i s in
let p i 1 i n v = i n v e r s e p i 1
and del_egde = { edge_deleted = f a l s e }
in ( r1 . edges < ( ( p i 1 , s1 ) , del_egde )
130
)
131
s1 . edges <
: : r1 . edges ;
( ( p i 1 i n v , r1 ) , del_egde ) : : s1 . edges
132
133
134
let
135
list_edges_iter f =
List . iter (
( ( pi , n ) , d )
function
>
136
137
)
138
139
140
let
if n . d e l e t e d or d . edge_deleted
then ( )
else f ( ( pi , n ) , d )
delete_edge (_,_, d ) = d . edge_deleted <
true
141
142
143
144
(
145
(
DAG
)
146
147
253
)
)
B.1 Quadratic Nominal Unication
148
149
type
150
151
152
dag = { nonvar
variables
suspended
}
: node l i s t ;
: node l i s t ;
: node l i s t
153
154
155
let
reset_node n =
( n . deleted
<
n . edges
<
n . pointer
<
)
156
157
158
159
160
161
false
[]
None
;
;
let
reset_dag d =
( L i s t . i t e r reset_node d . nonvar
;
L i s t . i t e r reset_node d . v a r i a b l e s ;
L i s t . i t e r reset_node d . suspended
)
162
163
164
165
166
167
168
(
169
(
170
let
let
171
172
Substitution
)
maxVars = 30
sigma = Array . c r e a t e maxVars None
173
174
175
176
177
(
178
(
Unification
Algorithm
)
179
180
181
182
183
184
185
let
let
match
let
check_clash r p i s =
push s t a c k n = ( s t a c k := n : : ! s t a c k )
pop s t a c k
=
! stack
| []
> f a i l w i t h " Error : empty s t a c k "
| ( n : : s t ) > ( s t a c k := s t ; n )
with
186
187
188
254
)
)
B.1 Quadratic Nominal Unication
189
190
191
192
193
194
195
let ( r2 , pi2 , s2 ) = reduceHead
match ( r2 . head , s2 . head ) with
|
|
|
|
|
( Var _
(_
( Abs (_,_)
( Pair (_,_)
(Atm a
,
,
,
,
,
_
Var _
Abs (_,_)
Pair (_,_)
Atm b
196
197
198
199
| ( Cst c1
| (_
, Cst c2
, _
r pi s
)
)
)
)
) when a
&&
&&
) when c1
)
not
not
in
> ()
= image p i b
( i s I n F r s S e t r . fresh_cond a )
( i s I n F r s S e t s . fresh_cond b ) > ( )
= c2
> ()
> f a i l w i t h "FAIL CLASH"
200
201
202
203
204
205
206
207
208
209
let rec p r o p a g a t e _ u n i f i c a t i o n _ e d g e s r p i
let ( r2 , pi2 , s2 ) = reduceHead r p i s in
match ( r2 . head , s2 . head ) with
|
|
|
|
|
( Var _
(_
(Atm _
( Cst _
( Pair ( r1 , r2 )
,
,
,
,
,
_
Var _
Atm _
Cst _
Pair ( s1 , s2 )
)
)
)
)
)
210
211
212
| ( Abs ( a , r1 )
in
, Abs
( b1 , s1 ) )
214
215
216
| (_
> f a i l w i t h " p r o p a g a t i o n f a i l e d : var "
> ()
> ( add_undirected_edge r1 p i s1 ;
add_undirected_edge r2 p i s2 ;
)
>
b2 = image p i b1
let
let
213
217
s =
, _
)
p i 2 = compose ( swapping a b2 ) p i
( add_undirected_edge r1 p i 2 s1 ;
addFrsAtms r [ b2 ]
)
> assert false
218
219
220
221
222
223
let rec
match
224
225
226
227
228
with
propagate_freshness_perm f c r =
r . head
| Perm ( p , r1 ) > propagate_freshness_perm ( permute ( i n v e r s e p ) f c ) r1
| _
> addFrsCond r f c
let p r o p a g a t e _ f r e s h n e s s r =
let rec aux f c r = match r . head with
255
in
B.1 Quadratic Nominal Unication
|
|
|
|
229
230
231
232
233
234
235
236
237
in
Cst _
Atm _ > ( )
Var x > p r i n t _ s t r i n g " r . fresh_cond # x"
Pair ( r1 , r2 ) > ( propagate_freshness_perm f c r1 ;
propagate_freshness_perm f c r2
)
| Abs ( a , r1 )
> propagate_freshness_perm ( f r s S e t D e l [ a ] f c ) r1
| Perm ( p , r1 )
> aux ( permute ( i n v e r s e p ) f c ) r1
aux r . fresh_cond r
238
239
240
241
let rec
do_fathers s =
list_node_iter (
fun
t
>
match
t . head
| Perm (_,_)
| _
242
243
)
s . fathers
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
and f i n i s h
let s t a c k
with
> do_fathers t
> finish t
r =
= ref [ ]
( push s t a c k ( idPerm , r ) ;
! s t a c k <> [ ]
( pi , s ) = pop s t a c k
((
s . pointer
| Some ( pi2 , r2 ) when is_same_node r2 r > ( )
| Some _
> f a i l w i t h "FAIL LOOP"
| None
> s . p o i n t e r < Some ( ( i n v e r s e p i ) , r )
) ;
do_fathers s ;
check_clash r p i s ;
(
s . head
| Var x
> sigma . ( x ) < Some ( ( i n v e r s e p i ) , r )
| _
> propagate_unification_edges r pi s
) ;
list_edges_iter (
(e , d) >
push s t a c k e ;
d . edge_deleted < t r u e
) s . edges ;
is_same_node r s
addFrsCond r ( dsPerm idPerm p i )
( addFrsCond r ( permute ( i n v e r s e p i ) s . fresh_cond ) ;
while
let
match
match
in
do
with
in
with
function
if
then
else
256
B.1 Quadratic Nominal Unication
s . deleted <
270
true
)
) ;
propagate_freshness r ;
r . deleted < true
271
272
273
274
275
)
276
done
277
278
279
280
281
(
282
(
Main
function
)
283
284
285
286
287
288
289
290
291
(
let
a
dag
where
u = v
)
u n i f i y dag s t =
( reset_dag dag ;
add_undirected_edge s idPerm t ;
l i s t _ n o d e _ i t e r f i n i s h dag . nonvar ;
l i s t _ n o d e _ i t e r f i n i s h dag . v a r i a b l e s
)
257
)
Download