diff --git a/examples/ffp/Figure1.png b/examples/ffp/Figure1.png
new file mode 100644
index 0000000..0107a08
Binary files /dev/null and b/examples/ffp/Figure1.png differ
diff --git a/examples/ffp/Figure2.png b/examples/ffp/Figure2.png
new file mode 100644
index 0000000..8cf119a
Binary files /dev/null and b/examples/ffp/Figure2.png differ
diff --git a/examples/ffp/Figure3.png b/examples/ffp/Figure3.png
new file mode 100644
index 0000000..3a39fb7
Binary files /dev/null and b/examples/ffp/Figure3.png differ
diff --git a/examples/ffp/Functional Programming is Free.ipynb b/examples/ffp/Functional Programming is Free.ipynb
new file mode 100644
index 0000000..c7b1e78
--- /dev/null
+++ b/examples/ffp/Functional Programming is Free.ipynb
@@ -0,0 +1,2104 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Functional Programming is Free\n",
+ "\n",
+ "\n",
+ "By [*Francis Sergeraert*](https://arxiv.org/pdf/1504.00527.pdf), January 2015 \n",
+ "(Adapted for [`cl-jupyter`](https://github.com/fredokun/cl-jupyter) by Gerd Heber, March 2017)\n",
+ "\n",
+ "\n",
+ "**Abstract.** A paper [[5]](#ckmvw) has recently been published. This paper is faulty: 1) The standard requirements about the definition of an algorithm are not respected, 2) The main point in the complexity study, namely the *functional programming* component, is absent. The Editorial Board of the SIAM JC had been warned a confirmed publication would be openly commented, it is the role of this text[1](#footnote_1).\n",
+ "\n",
+ " Kent Pitman informed me that the esoteric form `#'(lambda ...)` is no longer necessary in ANSI Common Lisp; the text is updated to the simpler form `(lambda ...)`.\n",
+ "\n",
+ "# History of the subject.\n",
+ "\n",
+ "I started the subject of *Effective Homology* in 1984, trying first to implement the Edgar Brown's \"algorithm\" [[4]](#browne1) computing the homotopy groups of the finite simply connected simplicial sets. This idea was quickly understood as ridiculous; thirty years later, there remains difficult to hope for an actual implementation of this algorithm, much too complex.\n",
+ "\n",
+ "Other methods were soon imagined to overcome the essential difficulty, consisting in using the strange power of the *functional programming*. Julio Rubio's contribution was essential to make the project succeed: he understood in 1989 that the best theoretical tool to organize the work is the so-called *Basic Perturbation Lemma* [[9]](#shih), [[3]](#brownr), more and more often called now the *Homological Perturbation Theorem*, you may question `Google(Galway perturbation)` to see numerous examples.\n",
+ "\n",
+ "Using Julio's idea, a program called EAT (Effective Algebraic Topology) was soon obtained in 1990, mainly devoted to the computation of the homology groups of iterated loop spaces, already allowing us to produce homology groups so far unreachable by the \"standard\" methods.\n",
+ "\n",
+ "In the years 1995-8, Xavier Dousson's thesis was devoted to the calculation of homotopy groups; the same tools, *functional programming* combined with the *homological perturbation theorem*, were successfully applied to the so-called Whitehead and Postnikov towers, the most \"elementary\" methods to compute the homotopy groups. Leading to the Kenzo program [[6]](#dsrbss). In particular, Ana Romero extended the scope of the program to the classifying spaces of non-commutative groups, detecting an error [[8]](#romrun) in a published paper, for a homotopy group of a relatively contorted simplicial set.\n",
+ "\n",
+ "These programs, once the basics of algebraic topology are understood, are elementary and certainly not the best possible: it is well known the Adams pectral sequence and all its derivatives are central in the subject and efficient methods *must* use them. Nevertheless, the experience of the Kenzo program, where everything is so elementary, made tempting a\n",
+ "non-negligible theoretical result: If a dimension $n$ is fixed, our algorithm computing the homotopy group $\\pi_n X$ of a simply connected simplicial set $X$ has a polynomial complexity *with respect to* $X$; of course not with respect to $n$, as it is well known since David Anick's paper [[1]](#anick), unless $P = NP$.\n",
+ "\n",
+ "This polynomiality result is so natural when you know the details of the Kenzo program, and of course the authors of the corresponding 16000 lines of Lisp code know these details, that I often stated this result without any other justification. The authors of [[5]](#ckmvw), after a few years of interesting common work with myself on problems of constructive algebraic topology, suggested to write down a common paper on this polynomiality result. Why not.\n",
+ "\n",
+ "First Jiri Matousek identified a real difficulty that I had not seen: the program rests on the effective homology of the Eilenberg-MacLane spaces, itself based on the effective homology of $K(\\mathbb{Z},1)$, quite simple, but the standard\n",
+ "effective homology of this monstrous version of the circle $S^1$ has *exponential* complexity! Jiri solved the problem for $K(\\mathbb{N},1)$, using a lovely discrete vector field, and I gave the complement to obtain the same result for $K(\\mathbb{Z},1)$, essentially an elementary process of normalization. This is explained in [[7]](#krmtsr).\n",
+ "\n",
+ "No other obstacle against polynomiality was later found in the Kenzo algorithm where everything is elementary. The paper [[5]](#ckmvw) is a rereading of the Kenzo source, or rather of the Kenzo documentation [[10]](#siret), with some mathematical complements most often not included in the documentation. As it often happens in journalistic reports, the very nature of the Kenzo program is unfortunately in [[5]](#ckmvw) misunderstood. The authors have an excuse: it is hard to understand the core of such a program if you never worked with its source code, or if you never did some similar concrete programming work.\n",
+ "\n",
+ "Everything is elementary in Kenzo, except the functional programming component, to my knowledge so far never so essentially and intensively used in a computer program processing a *specific* mathematical problem. All the *general* programs \"doing mathematics\", mainly computational algebra, such as Maple, Mathematica, Axiom, Macsyma, Gap, Sage, ... also intensively use functional programming, it is inevitable, but the corresponding complexity problems are *outside* the scope of functional programming. To my knowledge, in our case, it is the first time an essential component of\n",
+ "functional programming is used in a program doing a work as specific as computing a homotopy group.\n",
+ "\n",
+ "It so happened the authors of [[5]](#ckmvw) in 2012 started preparing pages and pages about our problem of complexity. I soon warned them: sorry, your draft totally forgets the component *functional programming*; without the minimal indications about this matter, the paper is not correct. For example, a Coq proof of this polynomiality result could not avoid studying the cost of this functional programming. It was proposed I cosign the paper [[5]](#ckmvw), I answered it is not possible to cosign a paper you would reject as referee.\n",
+ "\n",
+ "Every mathematican knows how it is difficult in conversations with non-mathematicians to explain what mathematical research is, and also why it is useful~(*!*). Sometimes the same when a computer scientist tries to explain to \"pure\" mathematicians the exact nature of a computer program; and in particular where the difficulties are hidden. Modern programming, in particular functional programming with the so-called *functional programming languages*, seems so natural for a pure mathematician, so close to his habits that he cannot believe some difficulty in complexity is hidden there. Finally, it is not so hard to overcome this difficulty, but a few prerequisites cannot be avoided. It is the role of this text to clarify this point.\n",
+ "\n",
+ "# Functional Programming.\n",
+ "\n",
+ "It is common in Mathematics to study functions taking as arguments one or several functions, the value of which being also a function. For example, an elementary exercise of topology could be: Let $\\mathcal{F} := \\mathcal{C}([0,1], [0,1])$ be the space of the continuous functions $f: [0,1]\\rightarrow [0,1]$ with the usual metric topology; prove the function $F: \\mathcal{F} \\rightarrow \\mathcal{F} : f \\mapsto (f \\circ f)$ is continuous. The argument is $f$, a function, the value is $f \\circ f$ a function, and the problem concerns $F$, a function: function $\\mapsto$ function.\n",
+ "\n",
+ "We want to study in this text the analogous situation in computer science. It is not difficult in most programming languages to write some code for functions having as value a function; in programming, we say such a function *returns* a function, to emphasize the dynamic character of the process: before been invoked, the \"value\" does not yet exist; when the dominating function is *invoked*, one says also it is *called*, its work starts and when the work is finished, a new object is born in the environment, a *functional object*, the object which is *returned*. This functional object may later in turn be invoked with appropriate arguments, returning arbitrary objects, maybe again other functional objects, why not.\n",
+ "\n",
+ "A programmer using such programming tools uses *Functional Programming*[2](#footnote_2). Functional programming is more or less easy, depending on the programming language the programmer is using; some languages have been specifically organized in particular to make easy this style of programming, such a language then deserves the qualifier *functional*. The main functional languages are those of the Lisp family, those of the ML family and Haskell.\n",
+ "\n",
+ "Algebraic Topology is often considered as a difficult subject, and our experience allows us to point out the main reason causing this difficulty. With only pen and paper, you cannot reasonably practice functional programming, it is much too complex. A complexity which *begins* to be illustrated by the few diagrams of this text, used to explain the most elementary techniques which are necessary when implementing functional programming. So that an algebraic topologist wishing to work only with pen and paper must design quite sophisticated methods to overcome this essential difficulty or more precisely to use different strategies which avoid this obstacle: the exact and spectral sequences are *not* constructive; they become constructive only if you add an essential dose of functional programming in the recipes. Most often, our topologists must invent new high level exact sequences, spectral sequences, spectra, operads, ..., made of higher homology or homotopy operators, also of exotic spaces, with a *constructive* scope always limited, a scope\n",
+ "which of course does not lower at all the interest of all these objects. While our methods using functional programming have a unique constructive limitation, the available material resources (computing time and memory space).\n",
+ "\n",
+ " This expression \"Functional Programming\" is also used for a strict style of programming where *every* (*!*) object is a function, the simplest example being the $\\lambda$-calculus, the wonderful\n",
+ "theoretical machine invented by Alonzo Church to disprove Hilbert's hope for a general algorithm solving any mathematical problem. More reasonably, most often, only the dynamic process defining how the program works then has a functional nature.\n",
+ "\n",
+ "# Mathematics and Computer Science.\n",
+ "\n",
+ "It is not exceptional to observe mathematicians having a knowledge of computer science which is a little superficial. This is true for any domain of mathematics as well, but a mathematician often is not conscious of his exact status with respect to computer science, the source of many problems.\n",
+ "\n",
+ "Two extreme behaviours: some of them think a computer can only make essentially trivial computations, which \"of course\" cannot do any relevant work for \"theoretical\" Mathematics. I remember a high level topologist who, sincerely and honestly, declared he could not imagine a computer can determine whether a natural number is prime or not. This happened frequently in the last decades of the 20th century; many impressive results have been produced since then with the help of computers and it becomes difficult to find extreme examples of this sort.\n",
+ "\n",
+ "The opposite extreme, now more frequent, is the following. Some mathematicians think a computer is of course trivially able to implement any mathematical idea, and some of them are surprised when their idea, a little naive, in fact do not correspond to anything concretely feasible on a machine. Or is feasible but using non-trivial implementations, the study of which then needing significant and interesting work, in particular to justify *theoretical* mathematical results, for example in *complexity*.\n",
+ "\n",
+ "In a discussion with an excellent topologist, he explained a theoretical algorithm for computing homotopy groups is known for a long time: it is enough to use the *combinatorial* definition of the homotopy groups by Kan and Moore. He \"only\" forgot a finitely generated subgroup of a non-commutative free group of finite type is free, but not necessarily of finite type and the claimed \"algorithm\" is not valid.\n",
+ "\n",
+ "Many topologists for example are persuaded the *exact* and *spectral sequences* of Algebraic Topology can be trivially transformed into algorithms computing some homology or homotopy groups. A referee recently claimed that deducing an algorithm from the Adams spectral sequence is \"folklore\", sic. I naively thought the mathematicians are the scientists the most careful for the precise *definition* of the objects they work with; it happens an *algorithm* is a non-trivial mathematical object with a deterministic definition, due to the best representatives of our profession in the last century: Gödel, Church and Turing, and such a definition cannot be reduced to some intuitive \"folklore\". As long as the referee reports will be *anonymous*, such accidents will be frequent: Juridically, an expert cannot be anonymous, why this exception for the alleged \"experts\" judging our articles?\n",
+ "\n",
+ "This text is devoted to clarifying the particular case of *functional programming*. On the one hand, coding functions returning functions is *now* as easy in programming as in mathematics, mainly if you use a so-called *functional* programming language. On the other hand, the cost in complexity is essentially *null*, which is summarized by the title:\n",
+ "Functional Programming is *free*; good news, but why? Because of the subtle notion of *closure*; of course the notion of closure in programming, unknown by most mathematicians and even by a large proportion of computer scientists.\n",
+ "\n",
+ "Some complex programs intensively use functional programming, and studying their complexity needs a lucid knowledge of this wonderful tool, the art of producing *closures*. Working in this way, you obtain efficient programs, the structure of which is limpid; furthermore without any cost in complexity. It is the subject of this article to explain this notion of closure, not so complicated, but not trivial at all.\n",
+ "\n",
+ "# Defining an algorithm.\n",
+ "\n",
+ "The paper [[5]](#ckmvw) in principle proves some algorithm $\\pi_n: X \\mapsto \\pi_n X$ is polynomial. Before proving this property, the algorithm $\\pi_n$ must be available.\n",
+ "\n",
+ "The proof of a *theorem* is decomposed in simpler statements, often called *propositions*, maybe in turn decomposed in still simpler statements, often called *lemmas*, and these lemmas should have simple proofs directly understandable. A similar scheme is to be applied for the definition of an *algorithm*.\n",
+ "\n",
+ "The execution of a program finally is sequential, instruction by instruction, so that the description of an algorithm consists in dividing the long sequence of instructions into partial segments, divided in turn in shorter segments, and so on, up to sufficiently elementary segments the implementation of which is a routine task. Often, smaller segments consist in fact in a call of a functional object done with respect to some prepared inputs, the caller waiting for the corresponding output returned by the functional object. This is the standard top-down view of algorithms, dual of the bottom-up understanding, the latter easier to handle when writing source code.\n",
+ "\n",
+ "The article [[5]](#ckmvw) uses the Postnikov tower. The Whitehead tower is a little simpler, produces also the homotopy groups, but not the *Postnikov classes*[3](#footnote_3). The difficulty with respect to functional programming is the same for the Whitehead tower and this framework is enough for our explanations.\n",
+ "\n",
+ "Often erroneously called Postnikov *invariants*.\n",
+ "\n",
+ "The process computing $\\pi_n X$ thanks to the Whitehead tower consists in iteratively constructing the stages of the tower, so that the first level decomposition of the desired algorithm $X \\mapsto \\pi_n X$ is necessarily:\n",
+ "\n",
+ "$$\n",
+ "X = X_2 \\stackrel{w_3}{\\longmapsto} X_3 \\cdots X_{n-1}\n",
+ "\\stackrel{w_n}{\\longmapsto} X_n \\stackrel{H_n}{\\longmapsto} \\pi_n X\n",
+ "$$\n",
+ "\n",
+ "The stage $X_i$ of the tower is the inital space $X$ where the first homotopy groups $\\pi_j X$ have been \"killed\" for $j < i$, its homotopy starts at the index $i$. The Hurewicz theorem therefore allows the program to compute $H_i X_i = \\pi_i X_i = \\pi_i X$, and an appropriate fibration using this group then produces the next stage $X_{i+1}$. The last step $H_n =\n",
+ "\\pi_n$ is again an application of the Hurewicz theorem. The program starts with $X = X_2$ simply connected, without any homotopy in dimensions 0 and 1.\n",
+ "\n",
+ "An algorithm is a functional object \"*input* $\\mapsto$ *output*\" and the computer scientists know for a long time the appropriate *types* of the input and the output are to be carefully defined. I invite a reader sufficiently patient to reach this part of this text to analyse the article [[5]](#ckmvw) with the Adobe searcher for the word \"type\": the reader will see this matter of the (computer) *types* of the used objects is never considered. The reader must probably understand the definition of these types is a minor subject: the slave who some day maybe will implement the described algorithm should be able to find out these details.\n",
+ "\n",
+ "This matter of *type* is yet become central in the very *foundations* of mathematics, see the Wikipedia page \"Homotopy Type Theory\". A point which does not seem really understood by our authors. If they had taken a little care of this question of *types*, they would have realized some functional programming is inevitable in such a programming.\n",
+ "\n",
+ "In the first level decomposition of the Kenzo algorithm above, the main step is the partial algorithm $w_i: X_{i-1} \\mapsto X_i$. The input and the output are a simplicial set *with effective homology*. We must therefore consider the (computer) *type* of the simplicial sets with effective homology.\n",
+ "\n",
+ "It is not the right place to define all the details of this quite complex computer type, a large collection of functional objects defining a simplicial set in general not of finite type, combined with a set of other functional objects sufficiently rich to contain the *homotopy type* of this simplicial set: this is nothing but the key point of *Effective Homology*. To illustrate our subject we just consider the simplest component of such an object, the face operator $\\partial_{X}$ of a simplicial set $X$.\n",
+ "\n",
+ "Such a face operator is a functional object, and you must first define ... the type of its input and output. The type of the simplices of $X$ can be chosen as a dependent type $S := \\mathbb{N} \\widetilde{\\times} S_n$ where, for every integer $n \\in \\mathbb{N}$, the type $S_n$ is the type of the $n$-simplices of $X$. For example for the standard presentation of $K(\\mathbb{Z},1)$, a simplicial set *not* of finite type even if we limit the dimension, $S_n$ is the type of the lists of length $n$ made of integers. The face operator $\\partial$ is then a functional object:\n",
+ "\n",
+ "$$\n",
+ "\\partial: [\\mathbb{N} \\widetilde{\\times} S_n] \\widetilde{\\times} \\mathbb{N}_\\ast \\rightarrow\n",
+ "[\\mathbb{N} \\widetilde{\\times} S_n] : ((n,\\sigma),i) \\mapsto \\partial_i(\\sigma)\n",
+ "$$\n",
+ "\n",
+ "where $\\sigma$ is an $n$-simplex of $X$ and $i$ an index $0 \\leq i\\leq n$. For example for $X = K(\\mathbb{Z},1)$, a possible 4-simplex is $\\sigma =(-19, -14, 8, 4)$ and:\n",
+ "\n",
+ "$$\n",
+ "\\partial_{K(\\mathbb{Z},1)}((4, (-19, -14, 8, 4)), 2) = (3, (-19, -6, 4))\n",
+ "$$\n",
+ "\n",
+ "Let $X_{i-1}$ be a possible $(i-1)$-th stage of a Whitehead tower, necessarily implemented as a *simplicial set with effective homology*. This object contains a large collection of functional objects and in particular the face operator $\\partial_{X_{i-1}}$. The step $w_i$ of the program must then construct $X_i := w_i(X_{i-1})$, the output $X_i$ being again a simplicial set with effective homology, this object containing in particular a similar collection of functional objects, in particular the face operator $\\partial_{X_i}$; the construction of $\\partial_{X_i}$ depends of course on the\n",
+ "object $\\partial_{X_{i-1}}$, but also of all the other components of $X_i$, in particular on the other functional components, according to a process computationnally not simple: a collection of various Eilenberg-MacLane spaces must in particular be constructed also as simplicial sets with effective homology, each Eilenberg-MacLane space being again the output of a functional subprogram.\n",
+ "\n",
+ "The paper [[5]](#ckmvw) does not consider at all the cost of the generation of all these functional objects. It is a pity:\n",
+ "\n",
+ "- On the one hand, this *functional programming* is the heart of the Kenzo algorithm: if such a programming tool was not necessary, the topologists would have used such an algorithm for a long time.\n",
+ "- On the other hand, the complexity cost of the dynamic generation of the functional objects is in this particular case essentially null. The rest of this text is devoted to this point.\n",
+ "\n",
+ "# A toy example.\n",
+ "\n",
+ "It could be interesting in a program, given two functional objects $f_1, f_2: \\mathbb{N} \\rightarrow \\mathbb{N}$, to be able to dynamically construct the composition $f_2 \\circ f_1: \\mathbb{N} \\rightarrow \\mathbb{N}$, to be used later for some reason.\n",
+ "\n",
+ "The Common Lisp style is convenient to illustrate such a situation. Let us consider this short Lisp session.\n",
+ "\n",
+ "> The original paper uses `SETQ` to define and initialize new variables. Unfortunately, the SBCL compiler used here creates a ``\"SIMPLE WARNING: undefined variable: ...\"`` which looks rather intimidating. In order to keep the distraction at a minimum, we will use the more portable ``DEFVAR`` instead of ``SETQ`` as long as there are no unintended side-effects. In places where the use of ``DEFVAR`` is not appropriate, we will use ``SETQ`` and ignore the warning."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "F1"
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar f1 (lambda (n) (* n 3)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "#"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "f1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "F2"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar f2 (lambda (n) (+ n 8)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(15 13)"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(list (funcall f1 5) (funcall f2 5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The first statement defines a function labeled \"`f1`\" mathematically defined as $f_1: n \\mapsto 3n$; read: assign to the symbol `f1` the function defined by the $\\lambda$-expression $\\lambda n\\,(n\\,*\\,3)$; the same for $f_2: n \\mapsto n+8$. The last statement constructs a list, its first element is $f_1(5)$, the second one is $f_2(5)$. Notice the Lisp notation `(funcall f1 5)` (read: call the function located by the symbol `f1` for the input `5`) for the usual mathematical notation $f_1(5)$.\n",
+ "\n",
+ "The reader may wonder why the returned functions are \"unnamed\". In this organisation the symbol `f1` \"sees\" the functional object, while the functional object does not see at all the symbol `f1`. In particular, different symbols can see the same function. More concretely, the symbol `f1` has a pointer toward the functional object just created, and on the contrary, the functional object has no pointer toward `f1`. A mysterious string such as `100468C65B` is the hexadecimal machine address of the functional object.\n",
+ "\n",
+ "If we want now to work with $f_3 := f_2 \\circ f_1$, we can use the Lisp statement:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "F3"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar f3\n",
+ " (lambda (n)\n",
+ " (funcall f2 (funcall f1 n))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "23"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall f3 5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But this is essentially *static*, the process constructing $f_3$ should be repeated if another composition is desired. If a program must construct a large number of compositions, it would be painful to rewrite the same sort of statement for each composition. It is better to write a general composition function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "COMPOSE"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar compose\n",
+ " (lambda (af2 af1)\n",
+ " (lambda (n)\n",
+ " (funcall af2 (funcall af1 n)))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "F4"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar f4 (funcall compose f2 f1)) "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "23"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall f4 5)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "F5"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar f5 (funcall compose f1 f2)) "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "39"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall f5 5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can read: the function `compose` is the function which, given the functions `af2` and `af1`, returns the function which, given the integer `n`, returns `af2`(`af1`(`n`)).\n",
+ "\n",
+ "We have for example chosen the symbol `af1` as vaguely meaning \"abstract function #1\" to improve the readability, but any arbitrary symbol could have been chosen here, even the symbol `f1`. The qualifier \"abstract\" means that no concrete function is to be defined when the value of `compose` is *constructed*; only later, when the functional object is *used*, then the abstract function will be replaced by some \"concrete\" function, as you observe in the next statements.\n",
+ "\n",
+ "But the most important point in this short Lisp session is the following: the values of the constructed compositions `f4` and `f5` are *closures*, not functions. What does this mean? It is the role of this text to clarify the exact nature of such an object, to illustrate the amazing power of these objects, on the one hand in flexibility, on the other hand their *cost in complexity* is essentially *null*.\n",
+ "\n",
+ "# Functional objects.\n",
+ "\n",
+ "Except if you work with exotic machines, in particular machines allowing to use multiprocessing, a program is finally a sequence of *machine instructions*, sequentially executed, except when some specific instruction, a *jump* instruction, says the next instruction to be executed is to be taken at some machine address; the execution then again continues sequentially from this address up to the next jump, and so on.\n",
+ "\n",
+ "A program can also *invoke* a functional object, this functional object is then called a *callee*, while the program at the origin of this call is the *caller*. The callee must then execute some task, and when this task is finished, the *control* is given back to the caller. The scheme below illustrates such an organization; each rectangle contains some machine instruction, and the arrows show the path followed by the *control*. Such a call consists in two jumps, one starting the callee, the other one leaving it.\n",
+ "\n",
+ "\n",
+ "\n",
+ "In fact the caller, before invoking the callee, pushes onto the *stack* the *return address*; in this way, when the callee has finished its task, it can read this return address to give back the control to the caller, at the next point where the caller must continue its work; the functional object can so be called from anywhere in the program, avoiding to repeat its code in the memory if this functional object is called several times, quite frequent.\n",
+ "\n",
+ "A part of the computer memory is called the *stack*; it is a continuous area of the memory, where objects are *piled*, the system being allowed to add a new object onto it, or on the contrary to remove the last one having been added; a special system register always keeps the address of the last object. In particular, each time a functional object is launched for execution by a caller, the caller adds (pushes) the return address onto the stack, so that when the work of this functional object is finished, this address on the stack is used by the callee to return to the caller at the right place, and this return address is erased (popped); we will see this stack will be used also for another specific usage of the closures, quite essential.\n",
+ "\n",
+ "A *closure* is also a functional object, but with a sophisticated structure opening interesting new possibilities, in particular making easy functional programming. A closure is *abstractly* made of three components:\n",
+ "\n",
+ "- the *body* of the closure, containing mainly:\n",
+ " - a pointer to its *code*;\n",
+ " - a collection of pointers defining its *own* environment.\n",
+ "- the *code* of this closure;\n",
+ "- the *environment* of this closure.\n",
+ "\n",
+ "The *apparent* redundancies in this description are voluntary. In a sense, the wonderful power of our computers is due to the intensive and remarkable use of the *pointers*; a pointer is a machine object giving \"only\" the machine address of another machine object; and it is not so rare the last machine object is again a pointer, this is *indirect addressing*, the key point to implement the notion of closure (and many others).\n",
+ "\n",
+ "When the functional object *closure* will be invoked, its code will be executed, but a part of this code may use the specific environment of *this* closure, the pointers available in the body itself allowing the code to reach the relevant part of the current environment.\n",
+ "\n",
+ "There remains to detail how this game of pointers is really organized, and why this organization justifies the title of this text.\n",
+ "\n",
+ "# The standard toy example.\n",
+ "\n",
+ "The standard mini-example which is used to explain the nature of the closures, quite artificial but instructive, is the following. Let us imagine a programmer wants to construct several arbitrary *multipliers*, a multiplier $\\mu_m$ being a functional object ready to multiply an arbitrary input $n$ by some fixed number $m$. In standard programming, you could write:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MUL3"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar mul3 (lambda (n) (* n 3)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "15"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul3 5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But our programmer intends to be able to *dynamically*, that is, during *runtime*, *generate* multipliers $\\mu_m$ with various and arbitrary $m$'s, all these multipliers remaining simultaneously \"alive\", and in particular such that they could be repeatedly called in arbitrary orders. We assign a *generator* of multipliers to the symbol `genmul`, and immediately use it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "GENMUL"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar genmul\n",
+ " (lambda (m)\n",
+ " (lambda (n) (* m n))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MUL5"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar mul5 (funcall genmul 5))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MUL7"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar mul7 (funcall genmul 7))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "45"
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul5 9)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "63"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul7 9)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The reader is invited to \"think\" like Lisp when these statements are processed. This goes as follows.\n",
+ "\n",
+ "- `(setq genmul ...)`: Lisp understands the user wants to use the symbol `genmul` and in particular assigns to it the object defined by the continuation of the text.\n",
+ "- `(lambda (m) ...)`: This object is an \"ordinary\" functional object, the input *will* be some object denoted by `m` in the source code and returning some output, some object defined by the subsequent text. Important: at this time this functional object *does not* work, it is then just *generated* and installed somewhere in the memory space; it will be called only *later*. We will also detail *later*, *when* this functional object is called, how it works.\n",
+ "- It happens the next statement `(setq mul5 ...)` now *calls* `genmul`, more precisely the functional object assigned to it, the input being 5. At this time, the binding \"`m` $\\mapsto$ 5\" defined by the parameter `m` and the input 5 is installed in the environment, more precisely in the *local* environment of the functional object just starting its work.\n",
+ "- The *body* `(lambda (n) ...)` of our functional object is then executed, which body asks again for the *generation* of a functional object, not its execution.\n",
+ "- Lisp then \"knows\" this generation happens in a *local* environment, a local environment having in this case only the binding \"`m` $\\mapsto$ 5\". Then Lisp generates the next functional object `(lambda (n) ...)`, *saving* also the current local environment, that is, in this case the binding \"`m` $\\mapsto$ 5\", this environment being made visible *only* from the just created functional object. This *pair* made of a functional object *and* an environment valid only for this object is a *closure*, notion detailed later. This closure is the *output* of `genmul` for this invocation, which closure is assigned to `mul5`.\n",
+ "- Same story for `(defvar mul7 ...)`: *another* functional object is generated, assigned to `mul7`, and this object, *another* closure, sees an environment where, this time the only binding is \"`m` $\\mapsto$ 7\". This closure is assigned to `mul7`.\n",
+ "- The value of `mul5` is *called* with the argument 9, in other words, the binding \"`n` $\\mapsto$ 9\" is installed. The value of `mul5` is a closure which *still sees* the binding \"`m` $\\mapsto$ 5\", so that the *code* of our closure, the Lisp statement `(* m n)` returns 45.\n",
+ "- The value of `mul7` is *called* with the same argument 9, in other words, the binding \"`n` $\\mapsto$ 9\" is again installed. This value is a closure, not the same as the previous one, which *sees* the *different* binding \"`m` $\\mapsto$ 7\", so that the *body* of this closure, the Lisp statement `(* m n)` this time returns 63.\n",
+ "\n",
+ "These details are a little lengthy but necessary to avoid deep misunderstandings. Usually the following \"magic\" explanation is given: a closure *keeps* the local environment which *was* present at *generation time*. A little more precisely: a closure *encloses* the local environment existing at generation time, explaining the terminology. Partially true, but in general not sufficient, and the actual process is more powerful.\n",
+ "\n",
+ "# Detailing the structure of the closure `mul5`.\n",
+ "\n",
+ "When `genmul` is *called* to *generate* the closure which will be assigned to `mul5`, Lisp does the following work. The local environment of *this* invocation of `genmul` has only the binding \"`m` $\\mapsto$ 5\". Having to generate the functional object `(lambda (n) (* m n)`, Lisp *generates* in fact only a *small* structure, the body of the closure to be generated, containing only three components:\n",
+ "\n",
+ "- A specific label indicating the object so entitled is a *closure*;\n",
+ "- The address of the code `(lambda (n) (* m n)`;\n",
+ "- The address of the machine word containing the value 5 for the symbol `m`, value valid only for *this* closure.\n",
+ "\n",
+ "You understand that, when `genmul` will be later called to generate a second closure assigned to `mul7`, *another* structure of the same sort is again generated, \"disjoint\" of the first one, with the same label, with, very important, the *same* address of the (same!) code, but the third component will be this time the address of the value 7 valid for this closure.\n",
+ "\n",
+ "You must carefully distinguish in this story the functional object which, *when* it is called, generates a closure:\n",
+ "```cl\n",
+ "(lambda (m)\n",
+ " (lambda (n) (* m n)))\n",
+ "```\n",
+ "from the *code* `(lambda (n) (* m n)` of the generated closure. When the interpreter or the compiler processes the functional object assigned to `genmul`, it \"sees\" this (ordinary) functional object will have to generate a closure. The compiler then generates somewhere the (interpreted or compiled) code of the closure, and this *unique* copy of this code\n",
+ "*will* be used by *every* closure generated by `genmul`.\n",
+ "\n",
+ "However, this code, this unique code, according to the closure for which it is working, has to use *different* values for `m`, values defined beforehand, when the closure *was* generated; how this is possible?\n",
+ "\n",
+ "The solution is another use of the *stack*, already mentionned to keep the *return* address from a functional object. When Lisp executes `(funcall mul5 9)`, Lisp works as follows:\n",
+ "\n",
+ "- `(funcall ...)` means the *value* of the first argument `mul5` of `funcall` is a functional object.\n",
+ "- Examining the nature of this functional object, in this case a *closure*, Lisp pushes on the stack *three* addresses:\n",
+ " - The return address, which will be used as for an ordinary functional object;\n",
+ " - The address of the closure.\n",
+ " - The adress of the second argument `9`.\n",
+ "- This done, Lisp reads the address of the relevant code of *this* closure and gives the *control* to this code.\n",
+ " \n",
+ "- In other words this code is now executed.\n",
+ "- And when this code must use the *value* of `m` valid for *this* closure, the code has been prepared by the compiler to reach this value as follows:\n",
+ " - Read the address of the closure from the stack;\n",
+ " - Read the third component of the closure, which contains the *address* of the value of `m` valid for *this* closure.\n",
+ " - Finally read the value of `m` valid for *this* closure. Typical use of *indirect addressing*.\n",
+ "- Via the stack, the code of our closure can also reach when necessary the second argument `9` of `funcall`, that is, in this case, the unique argument `n` of the invoked closure. \n",
+ "- When the execution of the code is finished, the code erases from the top of the stack the address of the argument `9` and the address of the closure, no longer necessary, reads the return address on the stack, erases also this return address, and is now able to *return* to the caller at the right place.\n",
+ "- Most often, a functional object must also return an *output*, here `45`; the address of the output which has just been computed by the closure is then put onto the stack, in this way the caller is able to reach this output.\n",
+ "\n",
+ "Such a process:\n",
+ "\n",
+ "- Preparing some input for a callee;\n",
+ "- Giving control to this callee;\n",
+ "- The callee does its work;\n",
+ "- When the work is finished, the callee prepares the appropriate output;\n",
+ "- The callee gives back the control to the caller;\n",
+ "- All these steps using the stack to make the involved objects communicate between each other;\n",
+ "\n",
+ "is so essential in any *programming* language that the *machine* language has always specific instructions to conveniently and efficiently handle the top of the stack as roughly described above. Examine for example the entry \"Stack instructions\" in the Wikipedia page \"x86 assembly language\". The particular case of the call of a closure is nothing but a variant of this process where the address of the closure is also put onto the stack, to allow the called code to reach the environment specific to this closure.\n",
+ "\n",
+ "# A local environment is alive.\n",
+ "\n",
+ "An acute reader maybe wondered why the closures above had their third component containing the *address* of the value of `m` and not this value itself, so needing an indirection.\n",
+ "\n",
+ "But consider the next standard toy example:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MAKE_PACKAGE"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar make_package\n",
+ " (lambda (m)\n",
+ " (values\n",
+ " (lambda (n) (* m n))\n",
+ " (lambda () (setq m (1+ m))))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The object assigned to the symbol `make_package` is an ordinary functional object using one argument `m`. When this object is called, it returns *two* values, namely two closures. The first one is a multiplier $\\mu_m$ as before. The second value is another closure which increases the value of `m` by 1. For example, we can call `make_package` with the argument \"`m` $\\mapsto$ `5`\"; the returned values are respectively assigned to the symbols `mul_1` and `inc_1` as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "#"
+ ]
+ },
+ "execution_count": 20,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar mul_1)\n",
+ "(defvar inc_1)\n",
+ "(multiple-value-setq (mul_1 inc_1)\n",
+ " (funcall make_package 5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Only the first closure assigned to `mul_1` is displayed. The same for two other closures where now \"`m` $\\mapsto$ `18`\", and the symbols `mul_2` and `inc_2` are used instead:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "#"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar mul_2)\n",
+ "(defvar inc_2)\n",
+ "(multiple-value-setq (mul_2 inc_2)\n",
+ " (funcall make_package 18))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The reader probably guesses the continuation of the story:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-25"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul_1 -5)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "6"
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall inc_1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-30"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul_1 -5)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-90"
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul_2 -5)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "19"
+ ]
+ },
+ "execution_count": 26,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall inc_2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-95"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul_2 -5)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "7"
+ ]
+ },
+ "execution_count": 28,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall inc_1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-35"
+ ]
+ },
+ "execution_count": 29,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall mul_1 -5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The new point is that both closures assigned to `mul_1` and `inc_1` *share* the *same* \"local\" variable `m`, for the environment containing the initial binding was the *same* for both closures. So that once `inc_1` has increased the value of `m` from 5 to 6, the call of `mul_1` of course then uses the *updated* value of `m`. The same for the closures assigned to the symbols `mul_2` and `inc_2` with respect to their *own* local environment, *another* binding \"`m` $\\mapsto$ `18`\", updated to 19.\n",
+ "\n",
+ "These complications do not prevent for example `mul_1` and `mul_2` to share the same code. The appropriate scheme is the following one.\n",
+ "\n",
+ "\n",
+ "\n",
+ "The central components are the four closures, each one located by a symbol; in this way, the programmer can call them easily. These closures share some codes and share also some environments, but in this case not in parallel: if two closures share a code, they do not share an environment, and conversely. This, once the source code is interpreted or compiled, is *static*.\n",
+ "\n",
+ "In the diagram above, the state of the memory is also sketched when, a *dynamic* event, the closure located by `mul_1` is called with the argument `9`. Then the control is given to the code of \"`(lambda (n) (* m n))`\", reached via the closure, which code can reach the binding \"`m` $\\mapsto$ `5`\" of the *relevant* environment via the stack and its *fn-obj* component; the code can reach the binding \"`n` $\\mapsto$ `9`\" also via the stack.\n",
+ "\n",
+ "We hope this diagram could make clear the role of the closures: they are very small boxes containing the address of the corresponding code and also the addresses of the variables of the corresponding environment. *When* a closure is called, the control is given to the corresponding code, which reaches the appropriate arguments via the stack and the closure, in particular the variables of the closure itself.\n",
+ "\n",
+ "This is nothing but a *toy example*. In more general situations, it is quickly impossible to draw a planar diagram as above! Taking account of the descriptive power of the addressing mechanism - $n$ bits can denote $2^n$ different addresses! -, this is not at all a problem for the interpreter or the compiler. This is why, as it is detailed in the next section, functional programming is essentially *free*.\n",
+ "\n",
+ "# When is a closure generated?\n",
+ "\n",
+ "We hope the reader is now sufficiently advanced in this matter of closures to understand another point. Our reader could have been puzzled by the quoted adjective \"ordinary\" often attributed to a functional object. How the interpreter or the compiler can decide whether such an object must be \"ordinary\" or not, that is, in the negative case, a closure?\n",
+ "\n",
+ "Some *global* environment is always defined, that is, a collection of bindings everywhere visible in the program, except when such a binding is \"hidden\" by a local one; on the contrary a *local* environment is visible only from a small (local) part of the code, *and also* from the closures which *were* generated when this local environment was active.\n",
+ "\n",
+ "When a functional object is to be generated, Lisp examines the state of the environment where this generation is to be done. If only the global environment is visible, an ordinary functional object is generated; on the contrary, if the generation happens inside some local environment, then a closure is generated.\n",
+ "\n",
+ "The simplest way in Lisp to generate a local environment uses a `let` instruction.\n",
+ "\n",
+ "> The use of ``DEFVAR`` in the next cell would make it impossible to obtain a closure in the definition of ``ADDA2``. We use ``SETQ`` instead and expect to see a compiler warning."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "SIMPLE-WARNING: \n",
+ "undefined variable: A\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "25"
+ ]
+ },
+ "execution_count": 30,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(setq a 25)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "510"
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(let ((a 255))\n",
+ " (+ a a))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "50"
+ ]
+ },
+ "execution_count": 32,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(+ a a)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this short session above, two uses of the variable `a` coexist. The global environment defines a binding \"`a` $\\mapsto$ `25`\", while, when the `(let ...)` instruction is run, a local environment with the binding \"`a` $\\mapsto$ `255`\" is defined. In this local environment, adding `a` and `a` produces 510, but this done, this local environment is definitively dead, so that in the next statement, the same code `(+ a a)` returns this time 50: the global environment was let unchanged and is reused as such.\n",
+ "\n",
+ "Combining a `(let ...)` statement with generation of functional objects shows when the interpreter or the compiler decides to generate an ordinary functional object or a closure."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ADDA1"
+ ]
+ },
+ "execution_count": 33,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar adda1 (lambda (b)\n",
+ " (declare (special a))\n",
+ " (+ a b)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ADDA2"
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar adda2\n",
+ " (let ((a 33))\n",
+ " (lambda (b)\n",
+ " (+ a b))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "91"
+ ]
+ },
+ "execution_count": 35,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall adda1 66)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "99"
+ ]
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall adda2 66)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The functional object assigned to `adda1` is generated when only the *global* environment is visible, so that this object has only the status of (ordinary) function. On the contrary, the object to be assigned to `adda2` is generated when the *local* environment with the local binding \"`a` $\\mapsto$ `33`\" is active, so that this requires a closure saving this environment for later uses.\n",
+ "\n",
+ "This effect is more visible when the global and the local environment are both used in a closure."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "11"
+ ]
+ },
+ "execution_count": 37,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(setf (symbol-value 'a) 11)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> The use of ``DEFVAR`` in the definition of ``B``, in the the next cell, would prevent to obtain a closure in the definition of ``ADD_A_B``. We use ``SETQ`` instead and expect to see a compiler warning."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "SIMPLE-WARNING: \n",
+ "undefined variable: B\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "22"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(setq b 22)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ADD_A_B"
+ ]
+ },
+ "execution_count": 39,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar add_a_b\n",
+ " (let ((b 222))\n",
+ " (declare (special a))\n",
+ " (lambda (n) (+ a b n))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "266"
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall add_a_b 33)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> The use of ``SETQ`` in the next two cells would trigger compiler warnings. We use the slighlty more verbose ``(SETF (SYMBOL-VALUE ...))`` instead."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "110"
+ ]
+ },
+ "execution_count": 41,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(setf (symbol-value 'a) 110)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "2"
+ ]
+ },
+ "execution_count": 42,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(setf (symbol-value 'b) 2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "365"
+ ]
+ },
+ "execution_count": 43,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall add_a_b 33)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The closure uses the global binding \"`a` $\\mapsto$ 11\" and the local one \"`b` $\\mapsto$ `222`\" the last binding shadowing the global one \"`b` $\\mapsto$ `22`\". The first call computes $11 + 222 + 33 = 266$. Then the *global* bindings of `a` and `b` are modified. The second call of the closure computes $110 + 222 + 33 = 365$; in other words, the closure uses the unique (global) binding of `a`, which has been modified by `(setq a 110)`, but the local binding of `b` which is not changed by `(setq b 2)`, this last `setq` changing only the global binding of `b`, which binding is shadowed inside the local environment of the closure by its own local binding, unchanged.\n",
+ "\n",
+ "All the previous examples are artificial, just designed to make obvious how, in a good programming language, the interpreter and the compiler process the terrible problem of *identifier scope* in a relatively sophisticated but\n",
+ "powerful way. This organization made of global environments, local environments *and closures* is the result of a long evolution of the art of programming; the interested readers can consult the Wikipedia page \"Closure (computer programming)\" to get some information about this evolution.\n",
+ "\n",
+ "The author of this note does not forget his serious difficulties when he tried in the 70's to understand the very notion of the *funarg problem* in Maclisp, see:\n",
+ "\n",
+ "[http://www.maclisp.info/pitmanual/eval.html](http://www.maclisp.info/pitmanual/eval.html)\n",
+ "\n",
+ "when the birth of the notion of closure was just under way: the programmer had to manage himself the problem of identifier scope through the *association lists*. The discussion described in this web page is instructive to follow the hesitations of the best implementers of this time.\n",
+ "\n",
+ "There remains to see why the use of these closures is *free*, we mean in complexity studies, and also why this organization is exactly the right one to implement high level mathematics, we mean when notions coming from the\n",
+ "*Category Theory*, typically the *functors*, have to be implemented.\n",
+ "\n",
+ "# Functional Programming is Free.\n",
+ "\n",
+ "We mean that, in a complexity study, the cost of the *production* of a new closure during the execution of a program is \"null\", more exactly is *constant*. In most complexity studies, the input $\\alpha$ of a program $\\pi$ is assumed to have a *size* $\\sigma(\\alpha)$, the program is run to produce some output $\\omega = \\pi(\\alpha)$, and we would like to bound the\n",
+ "computing time $\\tau(\\pi, \\alpha)$ in function of $\\sigma(\\alpha)$. For example, the program $\\pi$ is said to be *polynomial* if an exponent $e$ and a coefficient $c$ can be determined, such that for any meaningful input $\\alpha$, the estimate $\\tau(\\pi, \\alpha) \\leq c(1 + \\sigma(\\alpha))^e$ is satisfied.\n",
+ "\n",
+ "Let us assume we would like to prove the program $\\pi$ is polynomial. If the number of closures to be produced is bounded *independently* of $\\alpha$, the fact that the cost of the production of a closure is constant implies the production time of these closures can be omitted: it is enough to increase the coefficient $c$ to take account of the production of closures. Otherwise, when the number of closures depends on $\\alpha$, such a hasty claim could fail, but anyway using the fact the production cost of *one* copy of the closure is constant can be used conveniently.\n",
+ "\n",
+ "Producing a closure of a certain type is only allocating a memory segment of fixed length, a segment containing the label *closure*, the address of the corresponding code, always the same, and the addresses of a *fixed* number of local values.\n",
+ "\n",
+ "Of course you must distinguish the computing time necessary to *generate* the closure from the computing time of the closure itself when it is *called* later by the program; this computing time can on the contrary strongly depend on the inputs given by the caller to the closure. For example the closure could be an Ackermann function depending on some parameters; the production of the closure is then constant, but the computing time of such a closure when it is *called* of course is not constant at all, depending on the input *and also* possibly on the specific parameters of *this* Ackermann function.\n",
+ "\n",
+ "The investigator in complexity must also be lucid about another factor. The *production* itself of a closure needs a constant time, but *preparing* the local environment valid for this closure can on the contrary be costly, depending on the parameters of this closure. Let us consider for example the case of a function depending on a natural number $n$, this function having to produce a closure which must use the $n$-th prime number. It is then better to compute this prime number before generating the closure, and to insert the result in the environment known by the closure. In Common Lisp the structure of the function generating the closure could be:\n",
+ "\n",
+ "```cl\n",
+ "(setq closure_gen\n",
+ " (lambda (n ...)\n",
+ " (let ((nth_p (funcall nth_prime n)))\n",
+ " ...\n",
+ " (lambda (...) ... nth_p ...))))\n",
+ "```\n",
+ "\n",
+ "In other words, three steps when a closure is used:\n",
+ "\n",
+ "- Preparing the appropriate local environment, the cost depends on the specific necessary work;\n",
+ "- Generating the closure itself, constant cost.\n",
+ "- Using the closure when it is called, cost depending on the code of the closure, on the arguments given by the caller, and also possibly of the nature of the environment used by the closure.\n",
+ "\n",
+ "# Implementing functors thanks to appropriate closures.\n",
+ "\n",
+ "To explain why the notion of closure is the right tool to implement *functors*, we use the following elementary example. Let us assume a programmer has to work with (the category of) monoids, and in particular, he must implement the product *bifunctor*.\n",
+ "\n",
+ "He decides a monoid is a pair, the first element being the *identity* of the monoid, the second one being the *operation* of the monoid. Such an operation is a process, a function, which, given two elements of the monoid computes their product. For example the additive monoid of the natural numbers could be implemented as:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "N+"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar N+ (list 0 #'+))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The cryptic text `#'+` means in Lisp the functional object associated to the standard addition operator '`+`'. Test:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "9"
+ ]
+ },
+ "execution_count": 45,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall #'+ 4 5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The *value* of the expression `#'+` is `#`, an external simple representation of the functional object, too complicated to be exactly displayed, it is some machine code, readable only by experts.\n",
+ "\n",
+ "Our programmer uses also the multiplicative monoid of the integers."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "N*"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar N* (list 1 #'*))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "20"
+ ]
+ },
+ "execution_count": 47,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall #'* 4 5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "An *identity* function is defined to legibly extract the identity of a monoid."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MONOID-IDENTITY"
+ ]
+ },
+ "execution_count": 48,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar monoid-identity\n",
+ " (lambda (monoid)\n",
+ " (first monoid)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0"
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall monoid-identity N+)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "And an *operation* function is also defined to conveniently compute *inside* some monoid; the second statement below constructs a list made of the respective \"products\" of `4` and `5` in the monoids `N+` and `N*`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "OPERATION"
+ ]
+ },
+ "execution_count": 50,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar operation\n",
+ " (lambda (monoid item1 item2)\n",
+ " (funcall (second monoid) item1 item2)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(9 20)"
+ ]
+ },
+ "execution_count": 51,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(list (funcall operation N+ 4 5)\n",
+ " (funcall operation N* 4 5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we implement the product *bifunctor*. If $M_1$ and $M_2$ are two monoids, an element of $M_1 \\times M_2$ is represented as a pair. So that the identity of $M_1 \\times M_2$ is the pair of the respective identities. The function which, given the monoids $M_1$ and $M_2$, constructs the identity of $M_1 \\times M_2$ therefore is:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MONOID-PRODUCT-IDENTITY"
+ ]
+ },
+ "execution_count": 52,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar monoid-product-identity\n",
+ " (lambda (monoid1 monoid2)\n",
+ " (list (funcall monoid-identity monoid1) (funcall monoid-identity monoid2))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(0 1)"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall monoid-product-identity N+ N*)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We did not yet have seen any closure. In the same way, we must be able to construct, given two *arbitrary* monoids $M_1$ and $M_2$, the operation of the product monoid $M_1 \\times M_2$."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MONOID-PRODUCT-OPERATION"
+ ]
+ },
+ "execution_count": 54,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar monoid-product-operation\n",
+ " (lambda (monoid1 monoid2)\n",
+ " (lambda (item1 item2)\n",
+ " (list (funcall operation monoid1\n",
+ " (first item1) (first item2))\n",
+ " (funcall operation monoid2\n",
+ " (second item1) (second item2))))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "#"
+ ]
+ },
+ "execution_count": 55,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall monoid-product-operation N+ N*)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The operation to be constructed uses the operation of the first monoid and makes it work on the first components of the \"items\", the same for the second components with respect to the second monoid, and finally makes a pair, a list, with both results.\n",
+ "\n",
+ "Using this constructor with respect to our monoids `N+` and `N*` this time produces a *closure*. The point is that the internal `(lambda (item1 ...) ...)` uses `monoid1` and `monoid2` constituting the *local* environment where the functional object is generated, so that this environment must be *saved*, it is the role of the closure technology.\n",
+ "\n",
+ "We have now in our toolbox the necessary ingredients allowing us to implement the product bifunctor."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "MONOID-PRODUCT"
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar monoid-product\n",
+ " (lambda (monoid1 monoid2)\n",
+ " (list (funcall monoid-product-identity monoid1 monoid2)\n",
+ " (funcall monoid-product-operation monoid1 monoid2))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "N+_X_N*"
+ ]
+ },
+ "execution_count": 57,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar N+_x_N* (funcall monoid-product N+ N*))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(9 20)"
+ ]
+ },
+ "execution_count": 58,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall operation N+_x_N*\n",
+ " (list 4 4) (list 5 5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In other words, in the monoid $\\mathtt{N+} \\times \\mathtt{N*}$, the product of $(4,4)$ and $(5,5)$ is $(9,20)$. Now these constructors can be used for arbitrary monoids. For example the monoid `CS` of the character strings and their concatenation is constructed in Lisp as follows. Using the generic function `concatenate` of Lisp, a specific function\n",
+ "`string-conc` is constructed, giving the second component of our monoid; the identity is the empty string."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "STRING-CONC"
+ ]
+ },
+ "execution_count": 59,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar string-conc\n",
+ " (lambda (string1 string2)\n",
+ " (concatenate 'string string1 string2)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\"qwertyuiop\""
+ ]
+ },
+ "execution_count": 60,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall string-conc \"qwert\" \"yuiop\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "CS"
+ ]
+ },
+ "execution_count": 61,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar CS (list \"\" string-conc))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\"qwertyuiop\""
+ ]
+ },
+ "execution_count": 62,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall operation CS \"qwert\" \"yuiop\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The product $\\mathtt{CS} \\times \\mathtt{N+}$ is then constructed and tested:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "CS_X_N+"
+ ]
+ },
+ "execution_count": 63,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar CS_x_N+ (funcall monoid-product CS N+))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(\"qwertyuiop\" 9)"
+ ]
+ },
+ "execution_count": 64,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall operation CS_x_N+\n",
+ " (list \"qwert\" 4) (list \"yuiop\" 5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The product $\\mathtt{CS} \\times (\\mathtt{N+} \\times \\mathtt{N*})$ is constructed in the same way."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "CS_X_[N+_X_N*]"
+ ]
+ },
+ "execution_count": 65,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(defvar CS_x_[N+_x_N*] (funcall monoid-product CS N+_x_N*))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(\"qwertyuiop\" (8 25))"
+ ]
+ },
+ "execution_count": 66,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(funcall operation CS_x_[N+_x_N*]\n",
+ " (list \"qwert\" (list 4 5))\n",
+ " (list \"yuiop\" (list 4 5)))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The product bifunctor is present in our environment and can be used as in category theory. Its main component is a functional object constructing a new functional object from two other functional objects, the closure notion allowing the user to naturally construct this higher functional object, for the constructed closure \"keeps\" the environment where it has been generated. In this way, arbitrary complex uses of the product bifunctor can be done whithout any confusion: every closure will see the right references. *And* the complexity cost in this case is constant, only the addresses of `monoid1` and `monoid2` are used; the code constituting the heart of the function `monoid-product-operation` being once for all installed in the environment, which code can be used by \\emph{any} product of monoids, thanks to the indirect addressing mechanism.\n",
+ "\n",
+ "# When computing homotopy groups.\n",
+ "\n",
+ "Computing the homotopy groups of a finite simply connected simplicial set, or more generally a simply connected simplicial set *with effective homology*, using its Whitehead or Postnikov tower, is just the application of a long sequence of applications of functors similar to the one used as example in the previous section. For example the construction of $\\partial_{X_i}$ from $X_{i-1}$ discussed in [Section 4](#Defining-an-algorithm.) is obtained by such a long process, and the same for the other components of $X_i$.\n",
+ "\n",
+ "When the functional components of the initial objects are polynomial, all the used functors are so simple that of course the resulting objects obviously again have polynomial functional components.\n",
+ "\n",
+ "But this is not enough: the \"measure of polynomiality\" of the output functional objects must in turn polynomially depend on the measure of polynomiality of the inputs.\n",
+ "\n",
+ "The final scaffolding of functors is certainly a little impressive, but once you master the notion of closure, the proof of polynomiality is a simple recursive process.\n",
+ "\n",
+ "We just give here a minimal significant example of the method to be applied. Let $A, B, C, D$ be four \"ordinary\" types of objects for which a size is defined, such as integers, lists of such objects, matrices of such objects, etc. Let $[A \\rightarrow B]$ (resp. $[C \\rightarrow D]$) be the functional types of the functional objects which, when an input is an element of $A$ (resp. $C$), return an element of $B$ (resp. $D$). Let finally $\\alpha \\in [[A \\rightarrow B] \\rightarrow [C \\rightarrow D]]$ be a sort of \"functor\" between these functional types. Question, how to define the polynomiality of $\\alpha$?\n",
+ "\n",
+ "We would like to express that *any* polynomiality of $f: A \\rightarrow B$ implies *some* polynomiality of $\\alpha(f): C \\rightarrow D$, but in a \"polynomial\" way. After a little work, you quickly find the appropriate definitions are the following ones.\n",
+ "\n",
+ "**Definition.** A functional object $f: A \\rightarrow B$ is an element of $P^d_c(A,B)$ if for any element $a \\in A$, the estimate $\\tau(f, a) \\leq c(1+\\sigma(a))^d$ holds, where $\\sigma(a)$ is the size of the input $a$ and $\\tau(f, a)$ is the computing time of the output $f(a)$.\n",
+ "\n",
+ "**Definition.** The functional object $\\alpha : [A \\rightarrow B] \\rightarrow [C \\rightarrow D]$ is polynomial if for every degree $d \\in \\mathbb{N}$, there exists a degree $d' \\in \\mathbb{N}$ and a polynomial $\\chi_d \\in \\mathbb{N}[c]$ satisfying the following requirement: if $f \\in P^d_c(A,B)$, then $\\alpha(f) \\in P^{d'}_{\\chi_\\alpha(c)}$.\n",
+ "\n",
+ "In particular, no type of relation between $d'$ and $d$ is required, but a *unique* $d'$ must be associated to a fixed $d$; on the contrary the \"measure of $d'$-polynomiality\" of $\\alpha(f)$ must polynomially depend on the \"measure of $d$-polynomiality\" of $f$.\n",
+ "\n",
+ "It is then obvious the composition of polynomial maps $f_i: [A_{i-1} \\rightarrow B_{i-1}] \\rightarrow [A_i \\rightarrow B_i]$ is again polynomial.\n",
+ "\n",
+ "The reader must be lucid: the definitions above concern the relations between the respective complexities of $\\alpha: A \\rightarrow B$ and $f(\\alpha): C\\rightarrow D$, and not the complexity of $f$ itself. In the case of the Kenzo program, this complexity is uniformly bounded by a constant and can finally be omitted. This is the point never studied in [[5]](#ckmvw). In more complex situations, the complexity of $f$ could have a non-negligible role.\n",
+ "\n",
+ "Also, you must handle more complicated cases where inputs and outputs are mixtures of \"ordinary\" objects with an \"ordinary\" size and functional objects of arbitrary level. There is an essentially unique way to extend the simple case explained above to the general situation; it is not useful to detail this point here.\n",
+ "\n",
+ "In this way, the Kenzo algorithm can be mathematically described exactly as it was programmed many years ago, directly following Postnikov; and the desired polynomiality proof is then quite simple; and correct.\n",
+ "\n",
+ "# Functional vs Ordinary programming.\n",
+ "\n",
+ "To be complete, it is probably useful to detail the exact status of *functional* programming with respect to \"ordinary\" programming.\n",
+ "\n",
+ "The standard definitions of theoretical programming carefully distinguish the *primitive* recursive programs from the *general* recursive programs; necessarily illustrated by the example of the Ackermann function, which can be programmed as a general recursive program, not as a primitive recursive program. A reader of this text could wonder whether there exists such a theoretical difference between functional programming and ordinary programming.\n",
+ "\n",
+ "In a sense, this text proves there is no difference. The closure technology is nothing but a process translating a functional program into an ordinary program, so that you cannot program *more* algorithms with functional programming. But if you do not want to use functional programming, you will have to manage by yourself the serious related problem of identifier scope. The closure technology has precisely been invented to free the user of these low level programming problems, without any cost in complexity, thanks to a skilful use of indirect addressing[4](#footnote_4).\n",
+ "\n",
+ "The reader could usefully consult again the Wikipedia page \"Closure (computer programming)\"; it is there explained the so-called functional languages all have integrated the notion of closure, in a so essential way that the keyword *closure* is even not present in these languages: their users employ closures as Monsieur Jourdain wrote in prose.\n",
+ "\n",
+ "In the same Wikipedia page, it is explained how in the other languages it is possible to explicitly implement closures if necessary; the *object oriented programming* tool is then often used to solve the problem of identifier scope, while in fact this tool is not at all devoted to this matter. Once these techniques are understood, you should be able to translate the Lisp Kenzo program into any other language, with a source code, for the non-functional languages, certainly much less convenient; ask for example `Google(compose1 c++)` to see the problems met by the `C++`-writers of the *toy* example of [Section 5](#A-toy-example.) and imagine them translating the 16000 lines of Kenzo in `C++`.\n",
+ "\n",
+ "But using these (pseudo-)alternatives does not change anything to the problem: anyway, functional objects have to be dynamically generated during the execution of the program and you must control the cost of these generations. If you can prove the cost of the generation of *one* functional object is uniformly bounded - be careful: independently of the *size of the initial object* - you have finished. Otherwise, you have to study this dependency, good luck! You understand now how the closure technology is powerful: in the case of the Kenzo program, it is easy to prove the cost of the generation of a closure is uniformly bounded, independently of the initial object.\n",
+ "\n",
+ "Other techniques can be used. For example it can be proved the functional style used in Kenzo can be replaced by a strong recursive style; in fact closer ($!$) to the *strict* theoretical functional style. More precisely, the bottom-up programming style of Kenzo may be replaced by a top-down programming style, producing a program where it *seems* no functional object is dynamically constructed. It is not at all convenient, would you like to start the building of a house by the roof and finish by the foundations? And this would not change anything in the problem: this time you have to study the cost of the handling of the recursiveness, not easy in this situation with a terrible graph of crossed multi-recursiveness. And the \"upside-down\" understanding of the algorithm probably makes the complexity study rather painful. Anyway it would be a *different* algorithm.\n",
+ "\n",
+ "In fact, the paper [[5]](#ckmvw) does not give the smallest indication about the solution of the authors for this problem. Following the Kenzo program, the authors vaguely explain that for any simplicial set with effective homology $X$, an algorithm $\\pi^X_n$ can be written down computing $\\pi_n(X)$ which is claimed uniformly polynomial with respect to $X$, but the study of the algorithm $X \\mapsto \\pi^X_n$ is missing. No indication at all about which is written before and after ($!$) launching the execution, for example about the Eilenberg-MacLane spaces to be *generated*, with all their *functional* components, during the execution of $w_i$, see [Section 4](#Defining-an-algorithm.).\n",
+ "\n",
+ "Also, trivially using functional programming, the Kenzo program is independent of the $n$ of $\\pi_n(X)$; nothing is said about this point in [[5]](#ckmvw). It would be funny, for a process so simply iterative, to see a program depending on the number of steps.\n",
+ "\n",
+ "In summary, the cost of the generation of the countless functional objects is not considered in the paper, while it is the heart of the program. Nothing is explained either about a possible method to avoid functional programming. Observe in particular the footnote 11 of [[5]](#ckmvw), which explains \"functional programming\" could be an \"alternative\" framework to process our complexity problem ; the confusion is total: the program as it is described in [[5]](#ckmvw) must inevitably use functional programming; and \"functional programming\" cannot be a *tool* to study the complexity of an algorithm.\n",
+ "\n",
+ "The article [[5]](#ckmvw) reasonably updated could be a useful introduction to the subject, but the claimed proof of polynomiality is incomplete. It is true it is difficult to master this subject if you do not have any serious concrete experience of programming. The same authors, along the same lines, have proved an impressive collection of computability and undecidability results, results which, as far as I know, are correct; opening, in the positive case, a wonderful field for concrete programming. It is a pity to spoil the excellent appreciation due to all these results by the paper [[5]](#ckmvw).\n",
+ "\n",
+ "An amusing illustration of which happens without closures is the funny \"solution\" used by Bourbaki [[2]](#bourbk) to solve this problem by the *graphic* connections between Hilbert's $\\tau$'s and the white boxes $\\Box$.\n",
+ "\n",
+ "## References\n",
+ "\n",
+ "[1] D. J. Anick. The computation of rational homotopy groups is #p-hard. In M. Tangora, editor, *Computers in geometry and topology*. Decker, 1989. Lecture Notes in Pure and Applied Mathematics, vol. 114.\n",
+ "\n",
+ "[2] N. Bourbaki. Théorie des Ensembles, Chapitre I. Hermann, 1960.\n",
+ "\n",
+ "[3] R. Brown. The twisted Eilenberg-Zilber theorem. In *Celebrazioni Arch. Secolo XX, Simp. Top.*, pages 34–37, 1967.\n",
+ "\n",
+ "[4] E. Brown Jr. Finite computability of Postnikov complexes. *Annals of Mathematics*, 65:1–20, 1957.\n",
+ "\n",
+ "[5] M. Cadek, M. Krcal, J. Matousek, L. Vokrinek, and U. Wagner. Polynomial-time computation of homotopy groups and Postnikov systems in fixed dimension. *Siam Journal on Computing*, 43:1728–1780, 2014.\n",
+ "\n",
+ "[6] Xavier Dousson, Julio Rubio, Francis Sergeraert and Yvon Siret. The Kenzo program. [http://www-fourier.ujf-grenoble.fr/~sergerar/Kenzo/](http://www-fourier.ujf-grenoble.fr/~sergerar/Kenzo/)\n",
+ "\n",
+ "[7] M. Krcal, J. Matousek, and F. Sergeraert. Polynomial-time homology for simplicial EilenbergMacLane spaces. *J. Found. Comput. Math.*, 13:935–963, 2013.\n",
+ "\n",
+ "[8] A. Romero and J. Rubio. Homotopy groups of suspended classifying spaces: an experimental approach. *Mathematics of Computation*., 82:2237–2244, 2013.\n",
+ "\n",
+ "[9] W. Shih. Homologie des espaces fibrés. *Publications Mathématiques de l’IHES*, 13, 1962.\n",
+ "\n",
+ "[10] Yvon Siret. The Kenzo documentation. [http://www-fourier.ujf-grenoble.fr/~sergerar/Kenzo/Kenzo-doc.pdf](http://www-fourier.ujf-grenoble.fr/~sergerar/Kenzo/Kenzo-doc.pdf)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "SBCL Lisp",
+ "language": "lisp",
+ "name": "lisp"
+ },
+ "language_info": {
+ "codemirror_mode": "text/x-common-lisp",
+ "mimetype": "text/x-common-lisp",
+ "name": "common-lisp",
+ "pygments_lexer": "common-lisp",
+ "version": "X3J13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}