diff --git a/examples/cltm/Common Lisp, Typing, and Mathematics.ipynb b/examples/cltm/Common Lisp, Typing, and Mathematics.ipynb new file mode 100644 index 0000000..190f0c7 --- /dev/null +++ b/examples/cltm/Common Lisp, Typing, and Mathematics.ipynb @@ -0,0 +1,6671 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Common Lisp, Typing, and Mathematics\n", + "\n", + "By [*Francis Sergeraert*](https://www-fourier.ujf-grenoble.fr/~sergerar/Papers/Ezcaray.pdf), August 2001\n", + "(Adapted for [`cl-jupyter`](https://github.com/fredokun/cl-jupyter) by Gerd Heber, February 2017)\n", + "\n", + "## Introduction.\n", + "\n", + "Common Lisp is seldom used by mathematicians and this is an anomaly. The high level of quality of Common Lisp comes from a simple reason: Common Lisp is a *mathematical* object, certainly one of the most beautiful and *potentially* productive existing at this time. Common Lisp is a descendant of the fantastic $\\lambda$-calculus, designed by Church to produce one of the deepest *mathematical* results of the last century, namely the negative answer to Hilbert's Entscheidungsproblem (\"decision problem\"): no algorithm can determine whether an arbitrary mathematical statement is true, false or undecidable.\n", + "\n", + "Then, by a quite indirect route, $\\lambda$-calculus became a fascinating programming language, Lisp. Lisp's birth is old, at the end of the fifties, and forty years later it is clear it remains the most advanced \"common\" programming language, \"common\" meaning *with an ANSI norm*, in this case the $\\ldots$ Common Lisp norm. The Common Lisp language is so advanced that it is not so easy to use it for serious applications: a reasonable lucidity about its complex and far-reaching structure is required; so that the initiation stage in the learning process of this language is a little hard. The present paper is a tutorial to introduce *mathematicians* to the main components of Object Oriented Programming in Common Lisp. The extraordinary work of Guy Steele and his X3J13 committee during the years 1980-1994 led to a powerful language, on one hand, but on the other hand taking seriously account of standard *practical constraints* in *actual* programming work. Common Lisp is now widely used for complex industrial applications, mainly because of the powerful *dynamic* tools that are available; but it is not used very much for scientific applications, in particular for mathematical applications, and this must be corrected.\n", + "\n", + "No *concrete* language can be completely formalized, but the mathematical precision of the definition of Common Lisp gives its user much security, the precision which is required for sophisticated mathematical applications, in particular when complicated data-sharing arrangements are necessary. Another aspect of Common Lisp is without any equivalent with the other languages: the user may *freely* design his application at the best programming level, sometimes at a very high level, directly handling rich and complex mathematical structures; for example it is explained in [Section 5.2](#Kenzo-classes.) that three simple lines of Lisp code are sufficient to explain to the machine that a simplicial group is simultaneously a Kan space and a Hopf algebra, sharing a common graded differential coalgebra structure, but with some added data, namely two simplicial morphisms describing the simplicial group structure. Other times, in the *same* program, you may on the contrary work at a very low level, in an assembler-like style. The language is carefully *stratified* to help the developer to remain lucid and at ease about these questions. The Common Lisp *macro generator*, without any equivalent in other common languages, allows also the programmer to easily use his low level code when he is on the contrary working at a high level.\n", + "\n", + "Which is a good element for appreciation is the ability of a language to be quickly adapted to new *mandatory* evolutions; at this time, Object Oriented Programming (OOP) must be integrated in a language claimed *common*, and Common Lisp succeeded in doing it so nicely that it can even be used to *describe* what OOP exactly is. The CLOS (Common Lisp Object\n", + "System) chapter of [[STEL2]](#STEL2) is one of the most amazing parts of Computer Science. The general *dynamic* feature of Common Lisp has in particular been kept: the user may dynamically change the class of his objects; he may also redefine dynamically the classes without loosing the objects of this class; and the user may freely decide exactly what happens for these objects. If this is not yet flexible enough, the Metaobject Protocol (MOP), not yet ormalized, but already widely used, allows our user to freely define his own OP system; CLOS is in fact only a particular application of MOP, see [[KCRB]](#KCRB).\n", + "\n", + "This paper is devoted to a few didactical examples to help mathematicians to understand how Common Lisp and more precisely CLOS can be used for mathematical applications. It is organized as follows. Firstly, a small set of artificial examples is used to explain the general structure of CLOS. The main work in OOP, and certainly the most difficult one, is at the initialization stage of the instances (objects). Even if a user does not intend to use the current Common-Lisp, studying how the initialization process is designed in this environment will explain to him the good points of view in this domain.\n", + "\n", + "Then a more significant example is described to show the actual workstyle when CLOS is used in a non-beginner job. Because the OOP subject is strongly related to *typing*, a possible description of what typing *could be* is used as a theme of an exercise showing how CLOS allows to implement the consequences of this description. This example is very simple and should be a good tutorial to simultaneously understand the initial points of OOP under Common Lisp and what typing is, at least from the point of view of the *actual* programmer.\n", + "\n", + "The next section is more mathematically oriented: it is explained how the basic mathematical *categories*, sets, magmas and monoids, can be directly implemented in CLOS, without meeting any kind of difficulty. The possibility for the Lisp user to arbitrarily mix OOP and high level functional programming (lexical closures) gives at once the required tools. The specialized programming environments such as Axiom, Gap, Magma $\\ldots$ so can be skipped and the mathematician may freely work at an arbitrary level *in the programming language itself*, keeping a perfect control of the environment, without being bothered by a restrictive environment, seldom well-adjusted to an arbitrary new *research* work.\n", + "\n", + "It was the case of the author and his colleagues when the central problems of Algebraic Topology were considered from a computational point of view [[SRGR3]](#SRGR3)[[RBSR6]](#RBSR6). The resulting Lisp program Kenzo [[DRSS]](#DRSS) shows a concrete example of use of CLOS for a relatively large implementation work[1](#footnote_1). The Kenzo program is not at all didactic, it is an actual program able to produce mathematical results that are unreachable otherwise. Some points of the experience acquired by Kenzo's authors are discussed in the last section and in this way the reader will see a few more concrete points of CLOS.\n", + "\n", + "1 16000 Common Lisp lines and a 340pp documentation.\n", + "\n", + "## CLOS (= Common Lisp Object System).\n", + "\n", + "Strictly speaking, the notion of *Common Lisp Object System*, CLOS in short, does not make sense anymore. The first definition of Common Lisp, known as Common-Lisp-1984 [[STEL1]](#STEL1), did not have any \"object system\". Typing was already strong in CL-1984, at least if wished by the user; standard types were defined and the initial type system was freely extendable by the user, but the now classical notions of *classes*, *instances*, *generic functions* and *methods* were not yet available. The pre-ANSI version of Common Lisp, namely Common-Lisp-1990, contained a *proposal* for an OOP system, called Common Lisp Object System [Ch. 28, pp 770-864 [STEL2]](#STEL2); the pre-versions of this object system were already widely used at this time by the Lisp programmers. Finally, the ANSI specification [[ANSI]](#ANSI) of Common Lisp (1994) completely integrated the so-called Common Lisp Object System in the very definition of the language. The standard Lisp programmer is now assumed working according to the spirit of OOP, constantly using classes, instances of classes, methods and so on, like in C++ or Java[2](#footnote_2). But the perfect integration of the general ideas of OOP with the so powerful Common Lisp gives its user many capabilities which are at this time outside of scope with other languages, mainly when *functional programming* is involved. However we continue to call CLOS the part of the ANSI Common Lisp language devoted to OOP.\n", + "\n", + "This object system in Common Lisp coexists with the old typing system and also the old *structure* system, which allowed the user to define, construct and use the classical *record* objects, with several fields, typed or not. From a hierarchical point of view, the typing system and the structure system are *subsystems* of the object system: special predefined classes correspond to the main classical data types, and the same for the structured objects. But normaly the current Lisp programmer mainly works with the object system, organizing his workspace in classes, subclasses, methods and so on.\n", + "\n", + "The Common Lisp object system is elegant and powerful. As usual in Common Lisp, it is organized so that the user keeps a very large freedom. A *standard style* is carefully designed, but the user may go far from this standard style if special situations are encountered.\n", + "\n", + "2 With a slightly different terminology.\n", + "\n", + "### A quick comparison with C++ and Java.\n", + "\n", + "A few indications are given in this section about the main OOP features of Java, C++ and CLOS. In Java, the classes are \"above\" the methods (member functions): the specific functions for a class are defined *inside this class* so that the class and member function hierarchies are more or less *parallel*. This leads sometimes to artificial classes, typically the `System` class and the `Math` class, when this *rule* becomes non-sense in particular situations. The *member functions* are available under C++ too.\n", + "\n", + "In C++ you may also *overload* the functions, in particular the member functions. There are no member functions in CLOS[3](#footnote_3), there is only an overloading feature. But three other features are provided by CLOS which make particularly convenient and elegant the developer work:\n", + "\n", + "- The initialization process of the objects is essential in OOP; the general CLOS organisation of this work is really wonderful, in particular conceptually very simple, leading the programmer towards the good points of view, see [Section 2.3](#Initializing-instances.) for a small introduction to this point.\n", + "- The *method qualifiers* give much flexibility to organize the work of all the *applicable* methods; numerous simple examples in this paper.\n", + "- The functional basis of Common Lisp allows the user to easily install *functional slots* (members) of any sort, in particular *compiled closure slots*, dramatically extending the programming scope; [Section 4](#CLOS-and-Mathematical-Structures.) gives striking applications of this feature; in particular the instances may be themselves *funcallable*, in other words the instances become also functional objects, see [Section 3](#What-typing-is.) for a typical illustration.\n", + "\n", + "In CLOS, the *basic ingredients* are clearly distinguished:\n", + "\n", + "- The *class system*, mainly used through the `defclass` statement, allows the user to define the structure of his *instances* (objects), in particular through the notion of *subclass* (derived class).\n", + "- The *function type* is *primitive*; a function is an object which can be *funcalled* (called) for some work about some *arguments*.\n", + "- The *packaging system* allows the developer to precisely define what parts of his source code are normally directly known and/or reachable by a user of his program, obtaining in particular the equivalent of the `private` C++ code.\n", + "\n", + "Then, if the *class system* and the *functional* organization of Common Lisp are to be simultaneously considered, the particular notions of *generic functions* and *methods* must be used.\n", + "\n", + "- A *generic function* is a functional object the exact work of which will depend on the class of its arguments.\n", + "- The code for *some* specific work of a generic function corresponding to *some* particular class distribution of its arguments and *some qualifier* is a *method* object; each method is defined by a `defmethod` statement.\n", + "- In general *several* methods are involved when a *generic function* is invoked, they are called the *applicable* methods. CLOS gives its user a large freedom to organize these methods with respect to each other, to cover any possible situation, in particular when a sophisticated chronology of their work is required. This feature has no equivalent in C++ or Java, this is obtained by the *method qualifiers*.\n", + "\n", + "In this way, the CLOS *methods* can be thought of arranged in a *three*-dimensional array where each entry is associated to a generic function, the first index, the class distribution of the parameters, the second index, and the method qualifier, the third index.\n", + "\n", + "The CLOS method qualifiers have no equivalent in C++ or Java; a predefined qualifier organization is provided, already rather rich, sufficient for most of the ordinary cases[4](#footnote_4). If still more sophisticated combinations of methods are required, the `define-method-combination` function allows the developer to freely extend this organization.\n", + "\n", + "For example, and this will be detailed in [Section 4](#CLOS-and-Mathematical-Structures.), a mathematician who installs under CLOS the traditional mathematical categories must organize his work as follows:\n", + "\n", + "- The `defclass` statements will be used to define what a *set* object is, what a *group* object is, what a *chain complex* object is, and so on.\n", + "- The `defgeneric` statements will be used to define functions working on these objects, but these \"functions\" are traditionnally called in this case *functors* in mathematics; therefore one `defgeneric` statement for the *sum* functor, another *defgeneric* for the *product* functor, another `defgeneric` for the *classifying space* functor, and so on.\n", + "- Finally each generic function will have various methods to adapt the generic function to specific cases; for example the product of two objects of some category is also an object of this category with the corresponding structure to be defined. Therefore one product method for the *sets*, another product method for the *magmas*, another product method for the *monoids*, and so on. The `call-next-method` and `change-class` functions will allow these methods to *possibly* refer to the *less specific* ones.\n", + "\n", + "3 A C++ or Java member function is nothing but a CLOS generic function where the first argument is given *before* the function reference, with an implicit `with-slots` for this particular argument; such a feature can be easily added to the environment with the Metaobject Protocol; in general CLOS prefers to keep the maximal symmetry between the critical arguments.\n", + "\n", + "4 For example quite sufficient for the Kenzo program.\n", + "\n", + "### Very simple CLOS examples.\n", + "\n", + "Classes can be defined with the traditional hierarchy and methods may be defined which will be called, or not, according to the argument classes. The general `call-next-method` function allows the user to invoke shadowed methods.\n", + "\n", + "Let us define[5](#footnote_5) a simple class `C1` and a subclass `C2`.\n", + "\n", + "5 In the original paper, the small Lisp examples showed for illustration have really been run under Allegro Common Lisp and may be repeated under any ANSI Common Lisp; the Lisp prompt is here '`>`'; the maltese cross $\\maltese$ corresponds to the `` key asking for the evaluation of the typed-in statement; usually the symbols are keyed in lower case, and Lisp displays the answers with upper case." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS C1 ()\n", + " ((sl1 :initarg :sl1 :reader sl1)))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS C2 (C1)\n", + " ((sl2 :initarg :sl2 :reader sl2)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `C2` class is a *subclass* of the `C1` class. A `C1`-object, in Lisp you must say a `C1`-*instance*, has only\n", + "one component, you must say in Lisp one *slot*, labelled `sl1`, and a `C2`-instance has one further slot labelled `sl2`. Because of the `:initarg` slot-options, these slots may be initialized through the keyword arguments `:sl1` and `:sl2`. We create a `C1`-instance and a `C2`-instance." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "I1" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar i1 (make-instance 'c1 :sl1 1))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "I2" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar i2 (make-instance 'c2 :sl1 11 :sl2 22))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see, and this is the *default display*, only the class of the instance and its machine address are displayed. We would like to display our simple instances with their slots[6](#footnote_6). In general every object is displayed through the generic function `print-object`, and the user is advised to write specific *methods* for this generic function to obtain the desired display. Just to illustrate the general organisation of methods in CLOS, we\n", + "will define three `print-object` methods to \"print\" a `C1`- or `C2`-instance, to show the `sl1` and possibly the `sl2` slot(s). The `C2`-method calls the `C1`-method through `call-next-method`, and the `:after` method allows to print the '`>`' terminal.\n", + "\n", + "6 This is not standard: frequently the slots are numerous and complicated, and it is not sensible to display the instances with all their slots." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRINT-OBJECT ((c1 c1) stream)\n", + " (declare (type stream stream))\n", + " (format stream \"#<~S sl1=~S\" (class-name (class-of c1)) (sl1 c1))\n", + " c1)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRINT-OBJECT :after ((c1 c1) stream)\n", + " (declare (type stream stream))\n", + " (format stream \">\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRINT-OBJECT ((c2 c2) stream)\n", + " (declare (type stream stream))\n", + " (call-next-method)\n", + " (format stream \" sl2=~S\" (sl2 c2))\n", + " c2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `format` statement is analogous to the traditional `printf` of C. Frequently[7](#footnote_7), Lisp objects are displayed as strings which begin with '`#<`'. Now we redisplay our existing `C1`- and `C2`-instances.\n", + "\n", + "7 In principle a *non-readable* display must begin with '`#<`', but in this case, because of the complete display, in fact it could be read ..." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(# #)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(list i1 i2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can play to change the class of these instances." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class i1 'c2 :sl2 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class i2 'c1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see CLOS has taken the most natural decisions, but we will explain later how the process can be freely customized by the programmer.\n", + "\n", + "There is also the possibility of `:around` methods, encapsulating the so-called *primary* ones:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRINT-OBJECT :around ((c1 c1) stream)\n", + " (declare (type stream stream))\n", + " (format stream \"*\")\n", + " (call-next-method)\n", + " (format stream \"*\")\n", + " c1)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRINT-OBJECT :around ((c2 c2) stream)\n", + " (declare (type stream stream))\n", + " (format stream \"+\")\n", + " (call-next-method)\n", + " (format stream \"+\")\n", + " c2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And we see the role of these methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(+*#*+ *#*)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(list i1 i2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again it is interesting to deduce from this example the chronology of the calls of the five user-defined `print-object` methods. The standard organization allows the user to define *primary* and *auxiliary* methods, the last ones being *qualified* by the keyword `:before`, `:after` or `:around`; the methods are *specialized* by giving arbitrary combination of classes of the arguments; here, only the first argument was specialized, but we will see later natural situations where\n", + "several arguments are specialized. Simple but mathematically coherent rules, in particular based on topological sorting, explain what methods work in a particular case and in what order. Furthermore these possibilities may be freely modified or extended by the user through the `define-method-combination` function.\n", + "\n", + "### Initializing instances.\n", + "\n", + "It is well known the main part and the main difficulties of the OOP job are in the initialization work for the instances. Let us examine the general organization of the CLOS initialization process.\n", + "\n", + "The essential components of the initialization process in CLOS are the following generic functions:\n", + "\n", + "- The *standard* `make-instance` allocates the memory space for the instance to be created and calls `initialize-instance` to initialize it;\n", + "- The *standard* `initialize-instance` in fact calls `shared-initialize` to do the initialization work.\n", + "\n", + "The role of `shared-initialize` is the following. Frequently the initialization work to be done is essentially the same when an instance is created, or *re*initialized, or when its class is changed, or finally when the underlying class is itself redefined. If possible, the user is advised to define this common work in `shared-initialize` methods. If on the contrary there are significant differences between these cases, the user may write particular methods for the following generic functions:\n", + "\n", + "![](the-main-initialization-generic-functions.png)\n", + "\n", + "- `initialize-instance`: called by `make-instance` to initialize a just created instance; the user can extend and/or modify the standard initialization by writing specific methods for some classes.\n", + "- `reinitialize-instance`: called by the user when he wants to \"refresh\" some instance; sometimes it is the same as initializing from scratch, sometimes not; in the first case it is better to write an appropriate `shared-initialize` method; in the second case, the user must write specific methods for `reinitialize-instance`.\n", + "- `change-class`: called by the user when the class of an instance is *changed*; this function asks for a kind of conversion. Normally this function calls the generic function `update-instance-for-different-class` and the user may write methods for the latter. A simple example follows.\n", + "- `defclass`: if a `defclass` is in fact a *redefinition* of the class, each time an instance of the old version is considered, the generic function `update-` `instance-for-redefined-class` is applied, allowing in principle to coherently continue the work.\n", + "- `make-instance`: the user can also entirely redefine the creation and initialization process.\n", + "\n", + "Writing appropriate methods for these generic functions, the user can freely define the initialization work, clearly distinguishing the various levels from each other; if sufficient, the programmer may partially or totally use the *standard style* without any further work. You understand that combining these methods with the user-defined class hierarchy, combining also with the various possibilities of *auxiliary methods* (`:before`, `:after`, `:around`), the user has a fantastically large workspace. However, once this structure is reasonably understood, it is not difficult to be used, and quickly very powerful.\n", + "\n", + "### The example of `change-class`.\n", + "\n", + "The various situations that are met in the artificial simple example of this section around the `change-class` generic function allow to easily understand the general CLOS organisation of the initialization work." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS E12 ()\n", + " ((sl1 :initarg :sl1 :reader sl1)\n", + " (sl2 :initarg :sl2 :reader sl2)))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS E13 ()\n", + " ((sl1 :initarg :sl1 :reader sl1)\n", + " (sl3 :initarg :sl3 :reader sl3)))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "INS" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar ins (make-instance 'e12 :sl1 1 :sl2 2))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class ins 'e13)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ins" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(sl1 ins)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "UNBOUND-SLOT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(sl3 ins)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ther is no relation at all between both classes `E12` and `E13`; there is a common slot *name*, namely `sl1`, but this does not imply any relation between both classes. With an exception, if ever the class of an instance is changed from `E12` to `E13`, this common slot name will be considered by the standard `change-class` which in fact calls the standard `update-instance-for-different-class`: this method transmits the slots with the same *names* in both classes, gives up the other slots in the source instance, and lets unbound the other slots of the target instance. Here, the `sl1` slot has been kept, the `sl2` slot has been given up, and the `sl3` slot in the result is let unbound, which implies the error when this slot is read by the `:reader` method `sl3`. Note also the pseudo-new instance is at the same machine address, but the small\n", + "invisible steps of the garbage-collector continually modify the actual addresses, which is made obvious by the value of the symbol `ins` locating our *unique* but *variable* instance.\n", + "\n", + "Let us convert again our instance, but with an additional keyword argument." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class ins 'e12 :sl2 22)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "22" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(sl2 ins)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The default methods are sufficient to pass initial arguments to `change-class` to obtain partial initializations of the new *version* of the *same* instance. Let us consider now the situation where for some strange \"theoretical\" reason, when the class is changed from `E12` to `E13`, the value of the `sl3` slot must be computed as the product of the `sl2` slot of the source instance multiplied by some number included in the `change-class` data. This is obtained as follows." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD UPDATE-INSTANCE-FOR-DIFFERENT-CLASS :after\n", + " ((source e12) (target e13) &key (multiplier 1))\n", + " (declare (type number multiplier))\n", + " (setf (slot-value target 'sl3)\n", + " (* (sl2 source) multiplier)))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class ins 'e13 :multiplier 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1 88)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(list (sl1 ins) (sl3 ins))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because we have used an `:after` method, the standard *primary* method is firstly called, solving the `sl1` transfer. And after this is done, our auxiliary method works. Note in particular the elegant handling of the extra multiplier argument; because this keyword argument is used in our method, it becomes available for the user when a `change-class` `E12` $\\rightarrow$ `E13` happens.\n", + "\n", + "We want finally to consider the situation where a conversion `E13` $\\rightarrow$ `E12` must be installed, where the new version of the instance must have both slots redefined to zero. In this case the old version of the instance is useless, so that we can directly obtain this conversion by a `change-class` method." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD CHANGE-CLASS :after ((ins e13) (class (eql 'e12)) &rest rest)\n", + " (declare (ignore rest))\n", + " (setf (slot-value ins 'sl1) 0\n", + " (slot-value ins 'sl2) 0))" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class ins 'e12)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0 0)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(list (sl1 ins) (sl2 ins))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What typing is.\n", + "\n", + "The small examples of the previous section were artificial, and we want to consider now a *plausible* situation. We want to use CLOS to implement a simple coherent *typing system*, allowing the user to simultaneously use arbitrary types and high levels of *functional programming*, both subjects being furthermore strongly dependent on each other.\n", + "\n", + "What is typing? If a *mathematical* definition of typing is wished, many definitions are possible, and one of them is to be illustrated by a small CLOS program. Note this *new* typing system will be installed while the standard one remains active in our environment.\n", + "\n", + "Typing consists in giving the programmer the ability of *partial* descriptions of his algorithms. Let us call $\\mathcal{A}$ the *universe* of all the machine objects, $\\mathcal{A}$ for anything. Any algorithm can be viewed as a map $\\mathcal{A}\\rightarrow\\mathcal{A}$ *not everywhere defined*. Typically the Euclid algorithm is defined on the set $\\mathbb{N}_* \\times \\mathbb{N}_*$ of pairs of positive integers, and returns such an integer. The set $\\mathbb{N}_*$ is a subset of $\\mathcal{A}$, and the latter contains also the set $\\mathcal{L}$ of lists of any length; among these lists, some of them are made of two positive integers; let us call $\\mathcal{L}(\\mathbb{N}_* , \\mathbb{N}_*)$ the corresponding subtype. The type specification (signature) for the Euclid algorithm $E$ is then $E: \\mathcal{L}(\\mathbb{N}_* , \\mathbb{N}_*) \\rightarrow \\mathbb{N}_*$.\n", + "\n", + "Speaking so, we have used a few subsets of $\\mathcal{A}$. Such a simple example could create some wrong ideas. On one hand, one could think two different types should be disjoint, but it is not the case for $\\mathcal{L}$ and $\\mathcal{L}(\\mathbb{N}_* , \\mathbb{N}_*)$. If not disjoint, one could then require one of the considered types is included in the\n", + "other one; for example $\\mathcal{L}(\\mathbb{N}_* , \\mathbb{N}_*) \\subset \\mathcal{L}$ and it seems sensible to organize the types as a large *oriented graph* describing more and more finely the various sets of objects the user works with. Simple typing systems usually are of this sort.\n", + "\n", + "Another idea has a much larger scope; it consists in deciding a type is nothing but some subset of $\\mathcal{A}$, where the *membership property* may be verified by an algorithm. In other words we start only with two predefined types, $\\mathcal{A}$ and $\\mathcal{B}$; the second one, the *Boolean* type, has only two objects, $\\top$ and $\\bot$, implemented in Lisp as the symbols `T` and `NIL`. It is then interesting to define a type as an algorithm $\\mathcal{A}\\rightarrow\\mathcal{B}$ *everywhere* defined; in other words, the algorithm defining a type has the special signature $\\mathcal{A}\\rightarrow\\mathcal{B}$.\n", + "\n", + "This is sufficient in simple programming, but fails as soon as functional programming must be considered. In fact we meet the Russell paradox or if you prefer the Gödel theorem. Let us call $\\mathcal{T}$ the type of ... types, that is the set of functional objects $\\alpha:\\mathcal{A}\\rightarrow\\mathcal{B}$. Then no algorithm can verify the membership of $\\mathcal{T}$! Let us assume $\\tau$ is such an algorithm; therefore $\\tau:\\mathcal{A}\\rightarrow\\mathcal{B}$ is everywhere defined and $\\tau(\\alpha) = \\top$ if and only if $\\alpha$ defines a type, that is, $\\alpha:\\mathcal{A}\\rightarrow\\mathcal{B}$ is also everywhere defined. Then one could design the subtype $\\mathcal{T}'$ made of the algorithms $\\alpha \\in \\mathcal{T}$ such that $\\alpha(\\alpha) = \\bot$, in other words the type of \"typing algorithms\" $\\alpha$ that are *not* \"element\" of the type associated with $\\alpha$. It would be easy to write down the algorithm $\\tau'$ corresponding to the new type $\\mathcal{T}'$:\n", + "\n", + "$$\n", + "\\begin{array}{rcll}\n", + " \\tau'(\\alpha) &=& \\mathtt{if}\\ \\tau(\\alpha) &\\mathtt{then}\\\n", + " \\mathtt{not}\\ \\alpha(\\alpha)\\\\\n", + " & & & \\mathtt{else}\\ \\bot\n", + "\\end{array}\n", + "$$\n", + "\n", + "The algorithm $\\tau'$ firstly examines whether its argument $\\alpha$ is an algorithm $\\mathcal{A}\\rightarrow\\mathcal{B}$; if yes, the algorithm $\\alpha$ may work on any object, in particular on itself, and $\\tau'$ returns the opposite of $\\alpha(\\alpha)$; otherwise the answer is negative. Once the algorithm $\\tau:\\mathcal{A}\\rightarrow\\mathcal{B}$ is available, then the object $\\tau':\\mathcal{A}\\rightarrow\\mathcal{B}$ is available too, but Cantor and Russell remarked there is no possible answer for $\\tau'(\\tau')$: the answer of $\\tau(\\tau')$ should certainly be $\\top$, so that we obtain the contradictory relation $\\tau'(\\tau') = \\mathtt{not}\\ \\tau'(\\tau')$. Therefore the algorithm $\\tau$ may not exist.\n", + "\n", + "The traditional (pseudo-) solution for this difficult problem consists in *delaying* the examination of the type of functional objects. If an object $\\alpha$ is claimed to be an algorithm $\\mathcal{T}_1\\rightarrow\\mathcal{T}_2$, this\n", + "cannot be verified by a *general* algorithm; instead, we must *wait* for an actual run of the algorithm $\\alpha$: if the computation of $\\alpha(\\omega)$ is asked for, some process can *then* verify the argument $\\omega$ is really in the type $\\mathcal{T}_1$; if yes the computation of $\\alpha(\\omega)$ is started and the result $\\omega'$ is examined in turn, verifying the relation $\\omega' \\in\\mathcal{T}_2$. In other words it is not possible to verify the type of a functional object *in general*; the only possible verification consists, each time the algorithm works, in verifying that the argument and the result have the correct type; this \"verification\" is not at all complete, it can be applied only to a finite number of calls of the algorithm $\\alpha$.\n", + "\n", + "This organization must be recursive: the source and/or target types could in turn be functional, so that the verifications $\\omega \\in \\mathcal{T}_1$ and $\\omega' \\in \\mathcal{T}_2$ maybe must also be \"delayed\" with the meaning just explained. In particular if $\\omega$ and $\\omega'$ are both functional, then the functional object $\\omega$ *will* probably *be* used when the object $\\omega'$ will work. It is only at this time the correctness of the claimed types for $\\omega$, at least for this particular use, may be verified. Examples of this sort are showed in this paper.\n", + "\n", + "In this way, when a whole program is executed, all the *particular uses* of the functional objects imply that the type of arguments and result are verified, so that when the program is finished, for all the invocations of functional objects, types of input and output are confirmed. Thinking a little about this situation finally leads to the following conclusion: as far as they have been used, the type rules about functional objects have been satisfied *for this specific run*. A quite satisfactory conclusion.\n", + "\n", + "To illustrate the Common Lisp object system, we take as exercise subject the implementation of such an organization in Common Lisp, using the main components of CLOS.\n", + "\n", + "### Classes as structured types.\n", + "\n", + "A *class* is firstly a structured type. Each *instance* (element) of a *standard* class has several *slots* (components, fields, members), and each slot has various properties described in the definition of the class. The *builtin* classes, corresponding to old classical types (integers, symbols, ...), are not standard.\n", + "\n", + "The classes are organized in a hierarchical way: a class may be or not a *subclass* of another one, and this defines an *order* between the defined classes. This order must be coherent but in general it is not *total*, that is, for two classes $\\mathcal{C}_1$ and $\\mathcal{C}_2$, they may or may not be compared. But any class is a subclass of the maximal type~$\\mathcal{A}$, which contains any object, in particular the corresponding class object itself; to prove this point, we assign this class object to the symbol `universe` and verify the object so located is of the type so described; this maximal class $\\mathcal{A}$ corresponds to the set (type) $\\mathcal{A}$ of the previous section." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "UNIVERSE" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar universe (find-class 't))" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(typep universe universe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The *types* of the proposed organization for typing in this paper are pointed out by the letters `TP` only. This is necessary because we have to simultaneously work with three typing systems:\n", + "\n", + "1. The old one of Common Lisp, still present in our workspace; we call it the *type-system*;\n", + "2. The *class-system* which is the main constituent of CLOS;\n", + "3. The theoretical proposal of our exercise; it is called the `TP`*-system* in this text.\n", + "\n", + "> In the following, we use a metaobject protocol for CLOS which is not part of the ANSI Common Lisp standard. The SBCL compiler implements this protocol in the `sb-mop` package." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package :sb-mop)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `TP`-class.\n", + "\n", + "Each `TP`-type is described by an instance of the `TP` class now to be defined. We define this class as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS TP ()\n", + " ((name :type symbol :initarg :name :initform (gensym) :reader name))\n", + " (:metaclass funcallable-standard-class))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At this time, only one slot called `name` is defined for an instance of `TP`. Four *slot-options* have been used with the following meanings:\n", + "\n", + "- The `:type` option explains the `name` slot must contain a `symbol`;\n", + "- The `:initarg` option says the `name` slot may be initialized through the keyword `:name`;\n", + "- The `:initform` option says that if the `name` slot is not otherwise initialized, it must be automatically initialized by the `gensym` function, a predefined Lisp function which creates from scratch a certainly *new* symbol;\n", + "- Finally the `:reader` option asks CLOS to construct a *method* named `name` allowing the user to call this method to obtain the value of the slot.\n", + "\n", + "The `:metaclass` class option is explained later.\n", + "\n", + "A *type descriptor* of the `TP` system is always an instance of the `TP`-class. The corresponding type is functional or not; if functional the type descriptor is in fact an instance of the `FTP` class, a subclass of the `TP` class, else the type descriptor is an instance of the `DTP` class (discriminant type), another subclass of the `TP` class. The situation is here particularly simple: a type descriptor is *always* a `TP` instance, and in fact always an instance of only one subclass, either `FTP`, or `DTP`. Much more complicated situations between classes and subclasses can be designed under CLOS, but this simple situation allows us to show the main points of the nature of CLOS. In other words, the class diagram is this one.\n", + "\n", + "![](class-diagram.png)\n", + "\n", + "The type descriptor is firstly some `TP` instance, but it is convenient in this situation to locate the descriptor through a symbol and this is the reason of the `name` slot: this slot contains the symbol which in principle locates the `TP`-type descriptor. So that we have two (almost) \"symmetric\" pointers: the symbol points to the descriptor and the `name` slot of this descriptor points to this symbol; this is nothing but an *explicit* C++-`this` method. If ever the user is not really interested in the symbol, the `gensym` will automatically generate a symbol for completeness.\n", + "\n", + "![](tp-instance.png)\n", + "\n", + "The method `name` may now be applied to a `TP`-descriptor to obtain the associate symbol.\n", + "\n", + "We have explained in the previous section the role of the `print-object` generic function. Here, our strategy consists in simply locating `TP`-types through symbols to be considered as *labels*, so that it is natural to display such a type by the associate symbol." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRINT-OBJECT ((tp tp) stream)\n", + " (format stream\n", + " \"#<~S ~S>\"\n", + " (class-name (class-of tp)) (name tp)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What is finally displayed through the (implicit) call of this method could be for example `#`; it is a tradition in Lisp to begin the display of a *non-readable* object by '`#<`'; this notion is carefully defined in ANSI Common-Lisp, but it is not the subject of this text. The value of `(name tp)` is the associate *name* of the\n", + "type-descriptor; the value of `(class-of tp)` is the `class` of `tp`, therefore the *class-object* `DTP` or `FTP`, and\n", + "the `class-name` is the symbol naming this class. We do not want to explain here the technical details of the `format` Lisp function, which is similar to the traditional `printf` C function, but fantastically more flexible.\n", + "\n", + "A little earlier the symmetry property between a `TP`-type descriptor and the symbol locating it was explained. It is a little painful for the user to manage the necessary pointers, but an appropriate method can be used to automate the process during the initialization stage." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD INITIALIZE-INSTANCE :after ((tp tp) &rest rest)\n", + " (declare (ignore rest))\n", + " (set (name tp) tp))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `:after` *qualified* method shown above means that *after* the standard initialization process, something must be done: the symbol in the `name` slot must be bound to the `TP`-instance itself.\n", + "\n", + "### The `DTP`-class.\n", + "\n", + "We define now the subclass `DTP` (discriminant type) of the class `TP`." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS DTP (TP)\n", + " ((sub :type list :accessor sub)\n", + " (sup :type list :accessor sup))\n", + " (:metaclass funcallable-standard-class))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the first line, the pseudo-argument `TP` indicates the new class is a subclass of the class `TP`. Two new slots are defined, so that a `DTP`-instance will have three slots because of the `name` slot already defined for a `TP`-instance:\n", + "\n", + "- The `sub` slot (sub-types) is a list of the types that are known equal or smaller than the one which is described by the `DTP`-instance.\n", + "- The `sup` slot (super-types) is a list of the types that are known equal or larger than the one which is described by the `DTP`-instance.\n", + "\n", + "Note that `:accessor` methods (`sub` and `sup`) have been required for the corresponding slots; this means these methods will allow to *read* the corresponding slot but they can also be used to *write* it or *update* it. In fact, these slots must in general be modified after the creation, to maintain the coherence of the `TP`-system.\n", + "\n", + "We create now the minimal and the maximal type objects of the `TP`-system, namely the `tp-void` and `tp-any` types. A special initialization work is done here because the general initialization process will assume these types are *already* defined in the environment." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TP-VOID" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFVAR tp-void (MAKE-INSTANCE 'DTP :name 'tp-void))" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(TP-VOID TP-ANY)" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(SETF (sub tp-void) '(tp-void)\n", + " (sup tp-void) '(tp-void tp-any))" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(SET-FUNCALLABLE-INSTANCE-FUNCTION tp-void\n", + " #'(lambda (obj)\n", + " (declare (type t obj) (ignore obj))\n", + " (the boolean nil)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Firstly the `DTP`-instance to be assigned to the `tp-void` symbol is created by the call of the generic function `make-instance`, then the `sub` and `sup` slots are defined, and finally, because of the `:metaclass` class option, it is possible to associate a functional object to this instance, namely the function which always returns `nil`; this is nothing but the characteristic function of the void type in our environment. Example of use of this function:" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall tp-void 'anything)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because of the `funcall`, the functional object associate to the object pointed by the symbol `tp-void` is called with the symbol `anything` as argument. Whatever is the argument, the answer is `nil`. We can verify the pointer symmetry between the `name` slot and the corresponding symbol:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tp-void" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TP-VOID" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(name tp-void)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We do exactly the same work for the `tp-any` type, perfectly symmetric of the `tp-void` work." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TP-ANY" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFVAR tp-any (MAKE-INSTANCE 'DTP :name 'tp-any))" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(TP-ANY)" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(SETF (sub tp-any) '(tp-void tp-any)\n", + " (sup tp-any) '(tp-any))" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(SET-FUNCALLABLE-INSTANCE-FUNCTION tp-any\n", + " #'(lambda (obj)\n", + " (declare (type t obj) (ignore obj))\n", + " (the boolean t)))" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall tp-any 'anything)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The general process of initialization of a `DTP`-instance can now be defined. Firstly we need an `add-relation` function, allowing us to *add* a new order relation to the environment, something like \"`integer` < `number`\", *and all the consequent relations*. This function *updates* the `sub` and `sup` slots of the involved `DTP`-instances with the `union` Lisp function." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFGENERIC ADD-RELATION (dtp1 dtp2))" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD ADD-RELATION ((dtp1 symbol) (dtp2 symbol))\n", + " (the list\n", + " (with-slots (sub) (eval dtp1)\n", + " (declare (type list sub))\n", + " (with-slots (sup) (eval dtp2)\n", + " (declare (type list sup))\n", + " (dolist (item sub)\n", + " (declare (type symbol item))\n", + " (setf (sup (eval item))\n", + " (union (sup (eval item)) sup)))\n", + " (dolist (item sup)\n", + " (declare (type symbol item))\n", + " (setf (sub (eval item))\n", + " (union (sub (eval item)) sub)))\n", + " (list dtp1 dtp2)))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A new `:after` method can then be defined this time for the `DTP`-type. Again because this is an `:after` method, the defined process is *added* to the standard initialization process, in particular giving the allocation of the instance, the initialization of arguments through initargs and initforms." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD INITIALIZE-INSTANCE :after ((dtp dtp)\n", + " &key prdc\n", + " (dsub '(tp-void))\n", + " (dsup '(tp-any)))\n", + " (declare\n", + " (type (function (t) boolean) prdc)\n", + " (type list dsub dsup))\n", + " (with-slots (name sub sup) dtp\n", + " (declare\n", + " (type symbol name)\n", + " (type list sub sup))\n", + " (setf\n", + " sub (union dsub (list name))\n", + " sup (union dsup (list name)))\n", + " (dolist (item dsub)\n", + " (declare (type symbol item))\n", + " (add-relation item name))\n", + " (dolist (item dsup)\n", + " (declare (type symbol item))\n", + " (add-relation name item)))\n", + " (set-funcallable-instance-function dtp prdc))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This method for the generic function `initialize-instance` uses not only the `dtp`-instance to be initialized, but also the keyword optional arguments `:prdc` (predicate), `dsub` (direct subtypes) and `dsup` (direct supertypes). This mechanism works as follows: if an `initialize-instance` method uses a keyword argument, this argument is available to the user when it creates an instance of the corresponding class; usually this argument is used for a small work to be done during the initialization stage. Artificial example:" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defclass cc () ((slot :initarg :slot :reader slot)))" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defmethod initialize-instance :after ((cc cc) &key incslot)\n", + " (incf (slot-value cc 'slot) (* 2 incslot)))" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "CC-INSTANCE" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar cc-instance (make-instance 'cc :slot 3 :incslot 5))" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(slot cc-instance)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `:slot` argument, because it is an `:initarg`, is used to ordinarily initialize the unique slot named `slot` of a\n", + "`c`-instance; but the `:after` `initialize-instance` method, then adds twice the other keyword argument `:incslot`.\n", + "\n", + "For the initialization of a `dtp`-instance, the `:prdc` argument is used to define the predicate function defining the type, which is associated to the the `dtp`-instance and used when the `dtp`-instance is *funcalled*; the `:dsub` and `:dsup` arguments allow the user to give the list of *direct* sub- and super-types; the method then computes, with the help of `add-relation`, all the sub- and super-types. If not used, these arguments default to an obvious value.\n", + "\n", + "Let us define the `TP`-version of the boolean type." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TP-BOOLEAN" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFVAR TP-BOOLEAN\n", + " (MAKE-INSTANCE 'DTP\n", + " :name 'TP-BOOLEAN\n", + " :prdc #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (if (member obj '(nil t))\n", + " t nil)))))" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall tp-boolean 't)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall tp-boolean 'true)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "We use now the standard number types `tp-number`, `tp-integer` and `tp-fixnum` to give examples of use of the `:dsub` and `:dsup` arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TP-NUMBER" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar tp-number (make-instance 'dtp :name 'tp-number :prdc #'numberp))" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TP-FIXNUM" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar tp-fixnum\n", + " (make-instance 'dtp :name 'tp-fixnum\n", + " :prdc #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (typep obj 'fixnum)))\n", + " :dsup '(tp-number)))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TP-INTEGER" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar tp-integer\n", + " (make-instance 'dtp :name 'tp-integer :prdc #'integerp\n", + " :dsub '(tp-fixnum) :dsup '(tp-number)))" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(TP-INTEGER TP-FIXNUM TP-NUMBER TP-ANY)" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(sup tp-fixnum)" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall tp-fixnum 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall tp-fixnum 3.3)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall tp-number 3.3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In particular the relations \"`tp-fixnum` < `integer`\" and \"`tp-integer` < `tp-number`\" have implied \"`tp-fixnum` < `tp-number`\". Note in these examples, the `prdc` slots for `tp-number` and `tp-integer` have been defined through symbols, for example `#'numberp` points to the functional value of the symbol `numberp`, in this case a predefined Lisp function examining whether its argument is a number. On the contrary, the `prdc` slot of the `tp-fixnum` `TP`-instance is a function constructed in the call of `make-instance`.\n", + "\n", + "The user may also construct several new types, and *after* the fact, explicitly using the `add-relation`, define the order relations between them." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T1" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar t1\n", + " (make-instance 'dtp\n", + " :name 't1\n", + " :prdc #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean (eql obj 1)))))" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T12" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar t12\n", + " (make-instance 'dtp\n", + " :name 't12\n", + " :prdc #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (if (member obj '(1 2))\n", + " t nil)))))" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T123" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar t123\n", + " (make-instance 'dtp\n", + " :name 't123\n", + " :prdc #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (if (member obj '(1 2 3))\n", + " t nil)))))" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T1234" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar t1234\n", + " (make-instance 'dtp\n", + " :name 't1234\n", + " :prdc #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (if (member obj '(1 2 3 4))\n", + " t nil)))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have constructed the types without using the `:dsub` and `:dsup` arguments. Then the user can maintain the structure of his type set; the `mapcar` shows the list of the `sup` slots of the just defined types." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "((T1 TP-ANY) (T12 TP-ANY) (T123 TP-ANY) (T1234 TP-ANY))" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(mapcar #'sup (list t1 t12 t123 t1234))" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(T1 T12)" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(add-relation 't1 't12)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "((T1 T12 TP-ANY) (T12 TP-ANY) (T123 TP-ANY) (T1234 TP-ANY))" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(mapcar #'sup (list t1 t12 t123 t1234))" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(T123 T1234)" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(add-relation 't123 't1234)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "((T1 T12 TP-ANY) (T12 TP-ANY) (T123 T1234 TP-ANY) (T1234 TP-ANY))" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(mapcar #'sup (list t1 t12 t123 t1234))" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(T12 T123)" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(add-relation 't12 't123)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "((T12 T1 T123 T1234 TP-ANY) (T1234 T123 T12 TP-ANY) (T123 T1234 TP-ANY) (T1234 TP-ANY))" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(mapcar #'sup (list t1 t12 t123 t1234))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What is striking in such a process is the fact that the order relations must be explained to the machine by the user, again a case of a necessary *intuitionistic* work! Of course it would be easy to design a simple generator of enumerative types which would itself determine the order relations that are satisfied, but, because of Cantor, Russell and Gödel, this is definitively impossible for general types.\n", + "\n", + "### The `FTP` and `SF` classes.\n", + "\n", + "Now we process the `FTP`-class, devoted to the `functional` types. To simplify the presentation, we consider only the functions $\\mathcal{T}_1\\rightarrow\\mathcal{T}_2$, in other words the functions with exactly *one* argument. It is easy to prove this is theoretically sufficient, but practically it is not. The standard trick consists in considering a function with two arguments as a function defined for a list of two elements. The further technicalities for the case of an arbitrary number of arguments are far from the subject of this paper, so that we do not want to consider them[8](#footnote_8).\n", + "\n", + "8 Common Lisp is also there fantastically more advanced than the other current languages: the numerous types of argument transfer that are available give the user a great flexibility to manage the various cases; they are allowed only because of the great *mathematical* precision of the definition of the language structure.\n", + "\n", + "The `FTP`-class is defined as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS FTP (TP)\n", + " ((sorc :type symbol :initarg :sorc :reader sorc)\n", + " (trgt :type symbol :initarg :trgt :reader trgt))\n", + " (:metaclass funcallable-standard-class))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This time, two slots, `sorc` (source) and `trgt` (target), are added to the `name` slot of the underlying `TP`-structure; these slots must be two `TP`-types, in fact two symbols locating them, according to our organization. Note these source and target types can be discriminant or functional.\n", + "\n", + "A functional object will be an instance of the class `SF`, for s*afe*-f*unction*, safe because the types of argument and result are systematically verified when the function is called:" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS SF ()\n", + " ((sorc :type symbol :initarg :sorc :reader sorc)\n", + " (trgt :type symbol :initarg :trgt :reader trgt))\n", + " (:metaclass funcallable-standard-class))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The definition of `FTP` and `SF` are almost the same! In the case of an `FTP`-instance, the associated function will examine whether some object is in this type. In the case of an `SF`-instance, the associated function will be the functional object itself; the slots `sorc` and `trgt` are then additional information about the correct types of argument and result.\n", + "\n", + "To explain the main part of the work to be done now, let us consider a functional type $\\mathcal{T}_1\\rightarrow\\mathcal{T}_2$ and a particular function $f$ declared to be $f:\\mathcal{T}'_1\\rightarrow\\mathcal{T}'_2$; under what circumstances, is this particular $f$ of the functional type $\\mathcal{T}_1\\rightarrow\\mathcal{T}_2$? There is only one solution: we must have the order relations: $\\mathcal{T}_1\\leq\\mathcal{T}'_1$ and $\\mathcal{T}_2\\leq\\mathcal{T}'_2$, and the reader understands now why we took care of these order relations. We therefore need a `subtypep` Lisp function to compare types. If both arguments are discriminant types, the answer is read in a `sup` slot; if both arguments are functional, the appropriate comparisons must be applied to the respective sources and targets; if the types are not of the same nature, one being discriminant and the other one being functional, the answer is certainly negative[9](#footnote_9).\n", + "\n", + "9 It would be easy to work only with (pseudo-) \"discriminant\" types, even for functional objects, but the organization chosen here makes more obvious the deep difference of nature between both kinds of types; in particular in this way, a `DTP`-type and an `FTP`-type can never be compared.\n", + "\n", + "The Lisp translation:" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "SUBTYPE-P" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFUN SUBTYPE-P (tp1 tp2)\n", + " (declare (type symbol tp1 tp2))\n", + " (the boolean\n", + " (etypecase (eval tp1)\n", + " (dtp\n", + " (etypecase (eval tp2)\n", + " (dtp (if (member tp2 (sup (eval tp1)))\n", + " t nil))\n", + " (ftp nil)))\n", + " (ftp\n", + " (etypecase (eval tp2)\n", + " (dtp nil)\n", + " (ftp (and (subtype-p (sorc (eval tp2)) (sorc (eval tp1)))\n", + " (subtype-p (trgt (eval tp1)) (trgt (eval tp2))))))))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We cannot try the `subtype-p` function until the initialization work for `FTP`-instances has been completed. Remember in general a `TP`-instance must also be *funcallable* to verify the type of any object. The standard initialization work is therefore completed as follows." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD INITIALIZE-INSTANCE :after ((ftp ftp) &rest rest)\n", + " (declare (ignore rest))\n", + " (set-funcallable-instance-function ftp\n", + " #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (and (typep obj 'sf)\n", + " (subtype-p (sorc ftp) (sorc obj))\n", + " (subtype-p (trgt obj) (trgt ftp)))))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This time the associated function is entirely deduced from the `sorc` and `trgt` slots, available *after* the standard initialization work. You see the *only* objects of type some `FTP`-descriptor are `SF`-instances, and the appropriate order relations must be verified between source, target and the considered functional type. Now we can create a\n", + "few `FTP`-instances and do the obvious tests for `subtype-p`." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(subtype-p 'tp-fixnum 'tp-integer)" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(subtype-p 'tp-fixnum 'tp-fixnum)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(subtype-p 'tp-integer 'tp-fixnum)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "FII" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar fii (make-instance 'ftp\n", + " :name 'fii\n", + " :sorc 'tp-integer\n", + " :trgt 'tp-integer))" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "FNF" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar fnf (make-instance 'ftp\n", + " :name 'fnf\n", + " :sorc 'tp-number\n", + " :trgt 'tp-fixnum))" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(subtype-p 'fii 'tp-fixnum)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(subtype-p 'fii 'fnf)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(subtype-p 'fnf 'fii)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to make them work, the initialization of the `SF`-instances remains to be defined. An `SF`-instance is funcallable and the associate function will be the ordinary function the user intends to define, with the type verifications automatically added. The Lisp translation:" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD INITIALIZE-INSTANCE :after ((sf sf) &key f)\n", + " (declare (type (function (t) t) f))\n", + " (set-funcallable-instance-function\n", + " sf\n", + " (with-slots (sorc trgt) sf\n", + " #'(lambda (arg)\n", + " (declare (type t arg))\n", + " (unless (funcall (eval sorc) arg)\n", + " (error \"The argument ~S should be of type ~S.\"\n", + " arg sorc))\n", + " (the t\n", + " (let ((rslt (funcall f arg)))\n", + " (unless (funcall (eval trgt) rslt)\n", + " (error \"The result ~S should be of type ~S.\"\n", + " rslt trgt))\n", + " rslt))))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the extra `:f`initialization argument. An obvious test:" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2+" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar 2+ (make-instance 'sf\n", + " :sorc 'tp-integer\n", + " :trgt 'tp-integer\n", + " :f #'(lambda (n) (+ n 2))))" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall 2+ 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The argument 5.5 should be of type TP-INTEGER.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall 2+ 5.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Improvements.\n", + "\n", + "It is a little painful to use the `make-instance` method with its keywords. Let's use another aspect of the Common Lisp environment, the *macro generator* to automate the appropriate call to `make-instance`. Here we detail the work for the creation of `SF`-instances." + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "MAKE-SF" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMACRO MAKE-SF (sorc trgt f)\n", + " `(make-instance 'sf\n", + " :sorc ,sorc :trgt ,trgt :f ,f))" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(MAKE-INSTANCE (QUOTE SF) :SORC (QUOTE NUMBER) :TRGT (QUOTE TP-INTEGER) :F (FUNCTION (LAMBDA (N) (+ N 5))))" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(macroexpand\n", + " '(make-sf 'number 'tp-integer #'(lambda (n) (+ n 5)))))" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5+" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar 5+ (make-sf 'tp-number 'tp-integer\n", + " #'(lambda (n) (+ n 5))))" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "11" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall 5+ 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The result 11.5 should be of type TP-INTEGER.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall 5+ 6.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `defmacro` must be considered as defining a *source text translator* converting a call to `make-sf` into some\n", + "`make-instance`. The translation mechanism is *tested* by using the `macroexpand` Lisp function. Then the `5+` function is really created and then used. This time the error is in the result type.\n", + "\n", + "Constructing the `DTP` and `FTP` instances can be processed in the same way." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "MAKE-DTP" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMACRO MAKE-DTP (name prdc)\n", + " `(make-instance 'dtp\n", + " :name ,name :prdc ,prdc))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To explain the coherence of this type system, we add another stage of verification: a discriminant type is essentially a function $\\mathcal{A}\\rightarrow\\mathcal{B}$, so that the functional type of this function should be also verified. Let us solve this challenge. We slightly modify the `DTP`-initializer." + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "REDEFINITION-WITH-DEFMETHOD: \n", + " redefining INITIALIZE-INSTANCE :AFTER (#) in DEFMETHOD\n" + ] + }, + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD INITIALIZE-INSTANCE :after ((dtp dtp)\n", + " &key prdc\n", + " (dsub '(tp-void))\n", + " (dsup '(tp-any)))\n", + " (declare\n", + " (type (function (t) boolean) prdc)\n", + " (type list dsub dsup))\n", + " (with-slots (name sub sup) dtp\n", + " (declare\n", + " (type symbol name)\n", + " (type list sub sup))\n", + " (setf\n", + " sub (union dsub (list name))\n", + " sup (union dsup (list name)))\n", + " (dolist (item dsub)\n", + " (declare (type symbol item))\n", + " (add-relation item name))\n", + " (dolist (item dsup)\n", + " (declare (type symbol item))\n", + " (add-relation name item)))\n", + " (set-funcallable-instance-function dtp\n", + " (make-sf 'tp-any 'tp-boolean prdc)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As an example, let us consider the frequent case of a user who forgets the positive answer of the `member` function is not the boolean `T`:" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(3 4 5)" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(member 3 '(1 2 3 4 5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Such a user could erroneously define an enumerative type as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "COLORS" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar colors (make-dtp 'colors\n", + " #'(lambda (obj)\n", + " (member obj '(red orange yellow green blue indigo violet)))))" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall colors 'black)" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The result (BLUE INDIGO VIOLET) should be of type TP-BOOLEAN.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall colors 'blue)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and you see the type error ... during the type verification is detected and a clear message is displayed. This cannot be applied to the `boolean` type itself, because an infinite loop would be generated. You see also in such a case the functional object associated with an instance (`DTP`) is not purely functional but another (`SF`)-instance which in turn has an associated functional object.\n", + "\n", + "### The function `compose`.\n", + "\n", + "It is customary in discussions between Lisp and C++ or Java programmers to use the example of the `compose` function to illustrate how weak C++ is when *functional programming* is involved; have a look at [[CMPS]](#CMPS) for the \"official\" solution in C++ and see how technically difficult it is. Here we give a simple Lisp solution, which furthermore is at once valid for arbitrary data types.\n", + "\n", + "Firstly we construct the `pair` and `any-sf` types[10](#footnote_10):\n", + "\n", + "10 Exercise: a previous version of this paper had the claimed \"subtle\" definition of the `any-sf` type: `(make-ftp 'any-sf 'tp-void 'tp-any)`; this really defines a type, but the `compose` `SF`-instance would not have the `any-sf` type, why? Analyzing precisely the difference between both definitions for `any-sf` is quite interesting and shows that any *strict* typing system cannot be entirely satisfactory, some *freedom* must necessarily be given to the programmer; it is exactly the point of view of the Common Lisp designers." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PAIR" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar pair (make-dtp 'pair\n", + " #'(lambda (obj)\n", + " (and (consp obj)\n", + " (consp (cdr obj))\n", + " (null (cddr obj))))))" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "ANY-SF" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar any-sf (make-dtp 'any-sf\n", + " #'(lambda (obj)\n", + " (typep obj 'sf))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then the `2-sf-that-may-be-composed` type may be defined[11](#footnote_11).\n", + "\n", + "11 Exercise: Design a `make-subdtp` constructor to automate the generation of the `:dsup` argument which uses the predicate associated to the initial type." + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2-SF-THAT-MAY-BE-COMPOSED" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar 2-sf-that-may-be-composed \n", + " (make-dtp '2-sf-that-may-be-composed\n", + " #'(lambda (obj)\n", + " (and (funcall pair obj)\n", + " (funcall any-sf (first obj))\n", + " (funcall any-sf (second obj))\n", + " (subtype-p (trgt (second obj)) (sorc (first obj)))))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> GH: Fix the `make-dtp` macro to accomodate the `:dsup` parameter!" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PAIR)" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf (sup 2-sf-that-may-be-composed) '(pair))" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "COMPOSE" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar compose\n", + " (make-sf '2-sf-that-may-be-composed 'any-sf\n", + " #'(lambda (arg)\n", + " (let ((sf2 (first arg))\n", + " (sf1 (second arg)))\n", + " (make-sf (sorc sf1) (trgt sf2)\n", + " #'(lambda (arg)\n", + " (funcall sf2 (funcall sf1 arg))))))))" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4+" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar 4+ (funcall compose (list 2+ 2+)))" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall 4+ 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The argument 5.5 should be of type TP-INTEGER.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall 4+ 5.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A more subtle example: the composed function is in the right type with respect the argument, but a *used* function is not." + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3/2+" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar 3/2+ (make-sf 'tp-integer 'tp-integer\n", + " #'(lambda (x) (+ x 3/2))))" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3+" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar 3+ (funcall compose (list 3/2+ 3/2+)))" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The result 15/2 should be of type TP-INTEGER.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall 3+ 6)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last type-error example; this time, the `compose` function itself observes its argument does not have the required type." + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The argument (# #) should be of type 2-SF-THAT-MAY-BE-COMPOSED.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(funcall compose (list compose compose))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CLOS and Mathematical Structures.\n", + "\n", + "We give a small example to explain how CLOS can easily be used to implement the classical *mathematical structures*. Most often, an object of some *type* in mathematics is a structure with several components, frequently of *functional* nature. In this small presentation, we consider the case of a user who wants to handle *sets*, *magmas*, *associative* magmas and *monoids*; these simple particular cases are sufficient to understand how CLOS gives the right tools to process mathematical structures. In the following section, we will describe how these simple methods have been used in [[DRSS]](#DRSS) to implement the main structures of *Algebraic Topology* such as *chain complexes*, *simplicial sets*,\n", + "*simplicial groups*, *Hopf algebras*, and also the various *morphisms* between these objects.\n", + "\n", + "## Sets.\n", + "\n", + "We define a `/SET/` class whose instances correspond to the *sets* of the classical *set theory*." + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS /SET/ ()\n", + " ((name :type symbol :initarg :name :initform (gensym) :reader name)\n", + " (prdcf :type function :initarg :prdcf :reader prdcf)\n", + " (cmprf :type function :initarg :cmprf :reader cmprf)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this organization, a set is made of three slots. The `name` slot has the same role as for the types of the previous section: it is only an auxiliary tool to locate easily the sets; the slot contains the symbol that locates the set, and we automate this organization:" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD SHARED-INITIALIZE :after ((set /set/) slot-names &rest rest)\n", + " (declare (ignore slot-names rest))\n", + " (set (name set) set))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The appropriate `print-object` method; when a *set* or an object of a subclass will be displayed, the output will show the corresponding class and the `name`:" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRINT-OBJECT ((set /set/) stream)\n", + " (declare (type stream stream))\n", + " (format stream \"#<~S ~S>\" (class-name (class-of set)) (name set))\n", + " (the /set/ set))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The second slot of a set, namely the `prdcf` slot (predicate-function), contains a function which can be called to examine whether some *arbitrary* object is an element of the set. In order to use this slot easily, we define two functions, `owns` and `in`; the first one may answer yes or no, that is, `t` or `nil`; and the second one generates an *error* if the membership relation is not satisfied[12](#footnote_12).\n", + "\n", + "12 The sections bout our two main didactical examples, typing and mathematical categories, are entirely *independent*; but the lucid reader will observe the root class of the second application, namely the `/set/` class, can after all be also considered as a typing system in a mathematical context!" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "OWNS" + ] + }, + "execution_count": 116, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFUN OWNS (set obj)\n", + " (declare\n", + " (type /set/ set)\n", + " (type t obj))\n", + " (the boolean\n", + " (funcall (prdcf set) obj)))" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "IN" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFUN IN (set obj)\n", + " (declare\n", + " (type /set/ set)\n", + " (type t obj))\n", + " (if (funcall (prdcf set) obj)\n", + " obj\n", + " (error \"The object ~S is not in ~S.\"\n", + " obj set)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The explanation of the third slot `cmpr` is given a little later; we construct the set `N` of non-negative integers." + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "N" + ] + }, + "execution_count": 118, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar N\n", + " (MAKE-INSTANCE '/set/\n", + " :name 'N\n", + " :prdcf #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (and (integerp obj)\n", + " (>= obj 0))))\n", + " :cmprf #'=))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The symbol `N` locates the constructed set" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 119, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "N" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and the function `owns` allows the user to examine the membership relation:" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(owns N +1)" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(owns N -1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `cmprf` slot is essential in this context. There are frequently strong differences between an element of a set *as thought by the mathematician* and its possible machine representations, the *s* being important. The notion of *equality* in mathematics is \"primitive\" and rarely *logically* considered by mathematicians. On the contrary, this notion is crucial in Computer Science, and Common Lisp is by far the most precise language from this point of view[13](#footnote_13). Here, we want to let the user freely decide how equality is defined between the elements of his sets, and this is the role of the `cmprf` slot (comparison function). For example, the comparison between elements in the set `N` is done by the Lisp predefined function `#'=`, in particular appropriate to compare integers. Again we define a function `cmpr` to easily use the `cmprf` slot of a set.\n", + "\n", + "13 In particular all the standard predefined Lisp functions allow the user to *freely* define the equality relation to be used for every particular call." + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "ELT-CMPR" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFUN ELT-CMPR (set elmn1 elmn2)\n", + " (declare\n", + " (type /set/ set)\n", + " (type t elmn1 elmn2))\n", + " (the boolean\n", + " (funcall (cmprf set)\n", + " (in set elmn1) (in set elmn2))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the use of the `in` function to verify that the compared elements are really in the considered set. Now we can compare two elements of `N`." + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(elt-cmpr N 4 9)" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The object -9 is not in #.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(elt-cmpr N 4 -9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us construct now the set `Z/5`, that is the set of *integers modulo 5*. In our context, the most elegant method to implement this set consists in admitting a representation of an element of `Z/5` by an *arbitrary* integer, possibly negative or `> 4`, and to define the comparison with the help of the `mod` function, a predefined Lisp function." + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Z/5" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar Z/5\n", + " (MAKE-INSTANCE '/SET/\n", + " :name 'Z/5\n", + " :prdcf #'integerp\n", + " :cmprf #'(lambda (elmn1 elmn2)\n", + " (= 0 (mod (- elmn1 elmn2) 5)))))" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(elt-cmpr Z/5 4 9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This time the comparison between 4 and 9 is positive: these machine integers are *different* representations of the *same* mathematical object. You will understand that it is easy in this framework to define *quotient* sets.\n", + "\n", + "### Magmas.\n", + "\n", + "A magma is a set provided with a law of composition without any particular required property. It is a set with an additional ingredient, a function able to work on two elements of the magma and returning another element, their composition according the composition law. It is natural in this context to define the `magma` *subclass* of the `/set/` class; any `magma` instance is a set with a further slot, the `lawf` slot." + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 127, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS MAGMA (/set/)\n", + " ((lawf :type function :initarg :lawf :reader lawf)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again a function is added to the environment allowing the user to easily refer the `lawf` slot of a magma." + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 128, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFGENERIC LAW (magma &rest rest))" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 129, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD LAW ((magma magma) &rest rest)\n", + " (case (length rest)\n", + " (1 (in magma (first rest)))\n", + " (2 (in magma\n", + " (funcall (lawf magma)\n", + " (in magma (first rest))\n", + " (in magma (second rest)))))\n", + " (otherwise\n", + " (error \"Non-correct arguments in:~@\n", + " (LAW ~S~{ ~S~}).\" magma rest))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We intend in general to allow the user to refer the *law* defining a magma with an *arbitrary* number of elements, at least if this makes sense. In such a case, CLOS allows the user to define a generic function, here the `law` generic function, with one mandatory argument, called here `magma` and any number of other arguments put together in a list reachable through the symbol `rest`[14](#footnote_14). The ingredient `(magma magma)` in the parameter list has two meanings: the first `magma` names the first parameter, and the second `magma` explains the corresponding argument must be of *class* `magma`, otherwise the method is not applicable.\n", + "\n", + "14 More precisely, the symbol `&rest` *marks* in the parameter list a new zone, in this case with a symbol locating the \"other\" arguments; the Lisp programmer almost always chooses the symbol `rest` to locate this list.\n", + "\n", + "For a magma without any further claimed property, it is sensible to allow one argument, and then this argument is returned, or two arguments and then the *product* of these arguments according to the composition law is returned.\n", + "Otherwise an error message is displayed. Note how the `in` function is used to verify the correct type of the arguments.\n", + "\n", + "We have defined the *set* `N` and it is possible to transform it now into a *magma*. Note how the standard initialization process of an instance allows the user to obtain such a conversion without any further work." + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class N 'magma\n", + " :lawf #'(lambda (n1 n2)\n", + " (* n1 (- n2 3))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The *law* which is defined is $(n_1,n_2) \\mapsto n_1(n_2-3)$. The obvious trials:" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law N 4 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The object -5 is not in #.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law N 4 -5)" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The object -3 is not in #.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law N 1 0)" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 134, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law N 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "Non-correct arguments in:\n", + "(LAW # 4 5 6).\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 135, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law N 4 5 6)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The constructed magma in fact is not correct; let us construct a classical correct one:" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Q" + ] + }, + "execution_count": 136, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar Q \n", + " (make-instance 'magma\n", + " :name 'Q\n", + " :prdcf #'rationalp\n", + " :cmprf #'=\n", + " :lawf #'+))" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13/6" + ] + }, + "execution_count": 137, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law Q 3/2 2/3)" + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "Non-correct arguments in:\n", + "(LAW # 3/2 2/3 4/5).\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 138, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law Q 3/2 2/3 4/5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is simply the set `Q` of rationals equipped with the standard addition law. The last result is not satisfactory: the law is *associative* and it should be possible to make operate this law on an arbitrary number of arguments. The solution consists in defining a new class `A-MAGMA` (associative magma). There is no new slot in this class: membership of the new class only means the object, some magma, has an associative law." + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 139, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS A-MAGMA (magma) ())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But this new class may be used to define a new method for the generic function `law`; in this new case, an arbitrary *positive* number of arguments can be used. A simple recursive process defines the new method from the old one: if the argument number is three or more, the computation is decomposed, otherwise the *next* method is called; the law is associative and this definition is coherent." + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 140, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD LAW ((a-magma a-magma) &rest rest)\n", + " (if (> (length rest) 2)\n", + " (law a-magma\n", + " (in a-magma (first rest))\n", + " (apply #'law a-magma (rest rest)))\n", + " (call-next-method)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see in particular how the very basic Lisp function `apply` allows to recall the same method with one argument removed. We inform now the environment that our magma `Q` is associative:" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class Q 'a-magma)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "which allows us to compute the composition in `Q` of an arbitrary number of elements. Because the `magma` method will eventually be called, a possible type fault is intercepted." + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "163/60" + ] + }, + "execution_count": 142, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law Q 1/2 2/3 3/4 4/5)" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The object 0.75 is not in #.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 143, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law Q 1/2 2/3 0.75 4/5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Monoids.\n", + "\n", + "And the process can be continued for ever and ever, allowing the user to enrich as far as necessary the considered mathematical structures. Here, the following step consists in considering the structure of *monoids*. A monoid is an\n", + "associative magma with a unit. Only a `unit` slot is to be added to the `a-magma` structure. It is impossible in general to verify the claimed *unit* $e$ satisfies the required property, but however the property $e\\ast e = e$ can be tested; doing this test through a `shared-initialize` method allows it to be used in a `make-instance`, or in a `change-class`, or in a `reinitialize-instance` as well. The membership of the monoid may be also tested." + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 144, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS MONOID (a-magma)\n", + " ((unit :initarg :unit :reader unit)))" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 145, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD SHARED-INITIALIZE :after ((monoid monoid) slot-names &rest rest)\n", + " (declare (ignore rest))\n", + " (let ((unit (unit monoid)))\n", + " (unless (owns monoid unit)\n", + " (error \"Sorry, the claimed unit ~S is not in ~S.\"\n", + " unit monoid))\n", + " (unless (elt-cmpr monoid unit (law monoid unit unit))\n", + " (error \"Sorry, ~S does not look like a unit in ~S.\"\n", + " unit monoid))))" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "Sorry, 1 does not look like a unit in #.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 146, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class Q 'monoid :unit 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(reinitialize-instance Q :unit 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the error about the unit is intercepted *after* the standard initialization process, so that our `Q` is then become a `monoid`, as observed in the error message, but a wrong monoid; it is natural in this situation to use `reinitialize-instance` to redefine the object. A little more sophisticated use of `:around` methods would allow us to verify the coherence `before` changing class, and to refuse it if detected non-coherent.\n", + "\n", + "It is interesting to convert `Z/5`, currently a set, into a monoid with the unit `10`. Why not?" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 148, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(change-class Z/5 'monoid\n", + " :lawf #'+\n", + " :unit 10)" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(elt-cmpr Z/5 10 (law Z/5 10 10))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In a monoid, the composition law may logically be called for *zero* argument; in this case, the *unit* is the result. A new method for the generic function `law` is therefore stacked." + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD LAW ((monoid monoid) &rest rest)\n", + " (if (= 0 (length rest))\n", + " (unit monoid)\n", + " (call-next-method)))" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0 10)" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(list (law Q) (law Z/5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This looks a little artificial because `(law Q)` and `(unit Q)` are in fact synonymous, but think of the frequent case where you have to process a statement like `(apply #'law Q some-list)` where the last argument is a *computed* list, which could be sometimes empty; with our new `monoid` method for the `law` generic function, even if the value of `some-list` is the empty list, the correct process is applied.\n", + "\n", + "### What about the morphisms?\n", + "\n", + "So far we have only worked with the *objects* of the mathematical categories of sets, magmas, associative magmas, monoids. What about the *morphisms*? The solution is easy, but needs a little lucidity about the usual *compatibility* properties with ambient structures. We only sketch a possible solution, the expansion of which being obvious.\n", + "\n", + "The root of our classes of morphisms is the class of morphisms between sets.\n", + "\n", + "```lisp\n", + "(DEFCLASS SET-MRPH ()\n", + " ((sorc :type set :initarg :sorc :reader sorc)\n", + " (trgt :type set :initarg :trgt :reader trgt)\n", + " (f :type function :initarg :f :reader f)))\n", + "```\n", + "\n", + "A `set-mrph` instance is essentially a *source*, some set, a *target*, some set, and a function mapping any object of the source to an object of the target. Again the standard *functional* properties of Common Lisp give the developper the right environment. We could also hide the `f` slot into the special hidden functional slot of a *funcallable* instance, as we did for the `TP`-instances in [Section 3](#What-typing-is.); it is only a question of taste.\n", + "\n", + "We need something to be able to \"funcall\" a `set-mrph`:\n", + "\n", + "```lisp\n", + "(DEFUN ? (mrph elmn)\n", + " (...\n", + " code which:\n", + " 1) verifies elmn is in the source of mrph;\n", + " 2) applies the f slot of mrph to elmn to compute the image;\n", + " 3) verifies this image is in the target of mrph;\n", + " 4) returns the image.\n", + " ))\n", + "\n", + "(? some-set-mrph some-source-element)\n", + "```\n", + "\n", + "Now a magma-morphism is nothing but a set-morphism satisfying the standard compatibility properties with the magma structures of source and target. So that:\n", + "\n", + "```lisp\n", + "(DEFCLASS MAGMA-MRPH (set-mrph) ())\n", + "```\n", + "\n", + "and the same for `a-magma-mrph`, `monoid-mrph` and so on. Note this organization allows the user to clearly distinguish a\n", + "*set*-morphism between magmas from a *magma*-morphism between the same magmas; in the first case the compatibility properties are not satisfied or optional; in the second case the compatibility properties are assumed satisfied. Such morphisms are intensively used in the Kenzo program and considered in the following section.\n", + "\n", + "### What about functors?\n", + "\n", + "Working with categories, the mathematician inevitably will have to work with functors. In this organisation a tower of compatible functors is nothing but *one* generic function with various methods corresponding to the different cases. For example the *cartesian product* functor is defined for sets, magmas, associative magmas, monoids, and many other categories. It is sufficient to implement *all these functors* in *one* generic function, as follows." + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFGENERIC PRODUCT (set1 set2))" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRODUCT ((set1 /set/) (set2 /set/))\n", + " (make-instance '/set/\n", + " :name (intern (format nil \"~S-PRDC-~S\" (name set1) (name set2)))\n", + " :prdcf #'(lambda (obj)\n", + " (declare (type t obj))\n", + " (the boolean\n", + " (and (listp obj)\n", + " (= 2 (length obj))\n", + " (owns set1 (first obj))\n", + " (owns set2 (second obj)))))\n", + " :cmprf #'(lambda (pair1 pair2)\n", + " (declare (type list pair1 pair2))\n", + " (the boolean\n", + " (and (elt-cmpr set1 (first pair1) (first pair2))\n", + " (elt-cmpr set2 (second pair1) (second pair2)))))))" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(product N Z/5)" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(elt-cmpr N-prdc-Z/5 '(4 5) '(9 5))" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(elt-cmpr N-prdc-Z/5 '(4 5) '(4 10))" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-ERROR: \n", + "The object (-4 10) is not in #.\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 157, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(elt-cmpr N-prdc-Z/5 '(4 5) '(-4 10))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see how clear and natural is the code of the `product` method. It is only a question of constructing the appropriate `prdcf` and `cmprf` slots, some functions, from the corresponding slots of the arguments `set1` and `set2`; the process is always the same, so that the Lisp *closures*, a subtle notion not available in C++ or Java[15](#footnote_15), give the user the natural tool, even he does not know exactly what a closure is!\n", + "\n", + "15 available in Maple since the Release 5, but no OOP in Maple...\n", + "\n", + "For the richer structures of `magma`, `a-magma` and `monoid`, it is sufficient to write down new specific methods for the *same* generic function, defining the additional work to be done, using the previous work thanks to `call-next-method`." + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 158, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRODUCT ((magma1 magma) (magma2 magma))\n", + " (change-class (call-next-method) 'magma\n", + " :lawf #'(lambda (pair1 pair2)\n", + " (list\n", + " (law magma1 (first pair1) (first pair2))\n", + " (law magma2 (second pair1) (second pair2))))))" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 159, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRODUCT ((magma1 a-magma) (magma2 a-magma))\n", + " (change-class (call-next-method) 'a-magma))" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 160, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD PRODUCT ((monoid1 monoid) (monoid2 monoid))\n", + " (change-class (call-next-method) 'monoid\n", + " :unit (list (unit monoid1) (unit monoid2))))" + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Q2" + ] + }, + "execution_count": 161, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFVAR Q2 (product Q Q))" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Q4" + ] + }, + "execution_count": 162, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFVAR Q4 (product Q2 Q2))" + ] + }, + { + "cell_type": "code", + "execution_count": 163, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "((4/3 32/21) (13/8 76/45))" + ] + }, + "execution_count": 163, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(law Q4 '((1/2 2/3) (3/4 4/5))\n", + " '((5/6 6/7) (7/8 8/9)))" + ] + }, + { + "cell_type": "code", + "execution_count": 164, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "((0 0) (0 0))" + ] + }, + "execution_count": 164, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unit Q4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note also the applicability rule for methods of a generic function implies the product of a monoid by a magma, for example, will fortunately be a magma." + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 165, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(product Q N)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CLOS and the Kenzo program.\n", + "\n", + "> Some of the comments in this section apply to an [older Kenzo version](https://github.com/gheber/kenzo/tree/master/archive/Kenzo-2). They are called out whenever there's a difference in implementation and behavior with the [newer version](https://github.com/gheber/kenzo).\n", + "\n", + "The Kenzo program is the first significant *machine program* about classical Algebraic Topology. It is not only a program implementing various *known* algorithms; *new* methods have been developped to *transform* the main \"tools\" of Algebraic Topology, mainly the spectral sequences, not at all *algorithmic* in the traditionnal organisation, into actual *computing* methods.\n", + "\n", + "## An example of Kenzo work.\n", + "\n", + "Let us show a simple example to illustrate which is possible with this program. The homology group $H_5 \\Omega^3 \\mathrm{Moore}(\\mathbb{Z}_2,4)$[16](#footnote_16) is \"in principle\" reachable thanks to old methods, see [[CRML]](#CRML), but experience\n", + "shows even the most skilful topologists meet some difficulties to determine it, see [[RBSR6]](#RBSR6)[[SRGR5]](#SRGR5). With the Kenzo program, you construct the Moore space.\n", + "\n", + "16 The space $\\mathrm{Moore}(\\mathbb{Z}_2,4)$ is a \"canonical\" space having only non-trivial homology in dimension~4, namely $\\mathbb{Z}_2$, and $\\Omega^3\\mathrm{Moore}(\\mathbb{Z}_2,4)$, its third loop space, is the space of continuous maps from the 3-sphere~$S^3$ to this Moore space; the challenge is to determine the fifth homology group of this functional space." + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "---done---" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 166, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(cat-init)" + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K1 Simplicial-Set]" + ] + }, + "execution_count": 167, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def m4 (moore 2 4))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The program returns the Kenzo-object `#1`, a simplicial set, that is, a combinatorial version of the Moore space which is asked for, and this object is assigned to the symbol `m4`. Then you construct the third loop-space of this Moore space." + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K30 Simplicial-Group]" + ] + }, + "execution_count": 168, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def o3m4 (loop-space m4 3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The combinatorial version of the loop space is *highly* infinite: it is a combinatorial version of the space of *continuous* maps $S^3 \\rightarrow \\mathrm{Moore}(\\mathbb{Z}_2,4)$ but functionnally coded as a small set of functions\n", + "in a `simplicial-group` object, that is, a simplicial set with an added group structure compatible with the simplicial structure. Finally the fifth homology-group is asked for." + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Computing boundary-matrix in dimension 5.\n", + "Rank of the source-module : 23.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 1/23 (dimension 5) :\n", + "<>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 2/23 (dimension 5) :\n", + "<>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 3/23 (dimension 5) :\n", + "<>][3 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 4/23 (dimension 5) :\n", + "<>][2 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 5/23 (dimension 5) :\n", + "<>]>>][4 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 6/23 (dimension 5) :\n", + "<>]>>][4 <>][3 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 7/23 (dimension 5) :\n", + "<>]>>][4 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 8/23 (dimension 5) :\n", + "<>]>>][3 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 9/23 (dimension 5) :\n", + "<>][2 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 10/23 (dimension 5) :\n", + "<>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 11/23 (dimension 5) :\n", + "<>][3 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 12/23 (dimension 5) :\n", + "<>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 13/23 (dimension 5) :\n", + "<>]>>][1 <>]>>][3 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 14/23 (dimension 5) :\n", + "<>]>>][2 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 15/23 (dimension 5) :\n", + "<>]>>][3 <>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 16/23 (dimension 5) :\n", + "<>]>>][1 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 17/23 (dimension 5) :\n", + "<>]>>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 18/23 (dimension 5) :\n", + "<>][2 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 19/23 (dimension 5) :\n", + "<>]>>][1 <>]>>][1 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 20/23 (dimension 5) :\n", + "<>]>>][1 <>]>>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 21/23 (dimension 5) :\n", + "<>]>>][2 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 22/23 (dimension 5) :\n", + "<>]>>][1 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 23/23 (dimension 5) :\n", + "<>]>>][1 <>]>>][1 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + "Computing boundary-matrix in dimension 6.\n", + "Rank of the source-module : 53.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 1/53 (dimension 6) :\n", + "<>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 2/53 (dimension 6) :\n", + "<>][5 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 16s.\n", + "Computing the boundary of the generator 3/53 (dimension 6) :\n", + "<>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 4/53 (dimension 6) :\n", + "<>][2 <>][3 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 5/53 (dimension 6) :\n", + "<>][3 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 6/53 (dimension 6) :\n", + "<>][2 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 7/53 (dimension 6) :\n", + "<>]>>][5 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 8/53 (dimension 6) :\n", + "<>]>>][5 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 9/53 (dimension 6) :\n", + "<>]>>][5 <>][3 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 10/53 (dimension 6) :\n", + "<>]>>][5 <>][2 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 11/53 (dimension 6) :\n", + "<>]>>][4 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 12/53 (dimension 6) :\n", + "<>]>>][4 <>][3 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 13/53 (dimension 6) :\n", + "<>]>>][4 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 14/53 (dimension 6) :\n", + "<>][2 <>]>>][3 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 15/53 (dimension 6) :\n", + "<>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 16/53 (dimension 6) :\n", + "<>][3 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 17/53 (dimension 6) :\n", + "<>][2 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 18/53 (dimension 6) :\n", + "<>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 19/53 (dimension 6) :\n", + "<>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 20/53 (dimension 6) :\n", + "<>][3 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 21/53 (dimension 6) :\n", + "<>][2 <>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 22/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][4 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 23/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][4 <>][3 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 24/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][4 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 25/53 (dimension 6) :\n", + "<>]>>][2 <>]>>][3 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 26/53 (dimension 6) :\n", + "<>]>>][3 <>][2 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 27/53 (dimension 6) :\n", + "<>]>>][4 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 28/53 (dimension 6) :\n", + "<>]>>][4 <>][3 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 29/53 (dimension 6) :\n", + "<>]>>][4 <>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 30/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][3 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 31/53 (dimension 6) :\n", + "<>]>>][2 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 32/53 (dimension 6) :\n", + "<>]>>][3 <>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 33/53 (dimension 6) :\n", + "<>][2 <>]>>][1 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 17s.\n", + "Computing the boundary of the generator 34/53 (dimension 6) :\n", + "<>][2 <>]>>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 35/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 36/53 (dimension 6) :\n", + "<>][3 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 37/53 (dimension 6) :\n", + "<>][2 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 38/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][1 <>]>>][3 <>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 39/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][2 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 40/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][3 <>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 41/53 (dimension 6) :\n", + "<>]>>][2 <>]>>][1 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 42/53 (dimension 6) :\n", + "<>]>>][2 <>]>>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 43/53 (dimension 6) :\n", + "<>]>>][3 <>][2 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 44/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][1 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 45/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 46/53 (dimension 6) :\n", + "<>]>>][2 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 47/53 (dimension 6) :\n", + "<>][2 <>]>>][1 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 48/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][1 <>]>>][1 <>]>>][2 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 49/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][1 <>]>>][2 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 50/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][2 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 51/53 (dimension 6) :\n", + "<>]>>][2 <>]>>][1 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 52/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][1 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 18s.\n", + "Computing the boundary of the generator 53/53 (dimension 6) :\n", + "<>]>>][1 <>]>>][1 <>]>>][1 <>]>>][1 <>]>>][1 <>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + "\n", + "\n", + "Homology in dimension 5 :\n", + "\n", + "\n", + "Component Z/2Z\n", + "\n", + "Component Z/2Z\n", + "\n", + "Component Z/2Z\n", + "\n", + "Component Z/2Z\n", + "\n", + "Component Z/2Z\n", + "\n", + "\n", + "---done---\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 19s.\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(homology o3m4 5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and the result $H_5 \\Omega^3 \\mathrm{Moore}(\\mathbb{Z}_2,4) = \\mathbb{Z}_2^5$ is obtained in 1m30s with a PC 400MHZ. In natural situations a little more complicated, the Kenzo program has already computed new homology groups unreachable so far with \"classical\" Algebraic Topology, even from a theoretical point of view.\n", + "\n", + "## Kenzo classes.\n", + "\n", + "> This class hierarchy applies to the [older version of Kenzo](https://github.com/gheber/kenzo/tree/master/archive/Kenzo-2).\n", + "\n", + "![](kenzo-class-diagram.png)\n", + "\n", + "In the figure, the class diagram of Kenzo objects is shown. The situation is to be compared with which was explained in [Section 4](#CLOS-and-Mathematical-Structures.) about the most elementary mathematical structures. The lefthand part of the class diagram is made of the main mathematical categories that are used in combinatorial Algebraic Topology. A *chain complex* is a graded differential module; an *algebra* is a chain complex with a compatible multiplicative structure, the same for a *coalgebra* but with a comultiplicative[17](#footnote_17) structure. If a multiplicative and a comultiplicative structures are added and if they are compatible with each other in a natural sense, then it is a *Hopf algebra*, and so on.\n", + "\n", + "17 That is, some *co*operator $A \\rightarrow A \\otimes A$.\n", + "\n", + "The `hopf-algebra` and `simplicial-group` classes are typical cases where a *multiple inheritance* situation is met; we show the `actual` Kenzo definitions of these classes.\n", + "\n", + "```lisp\n", + "(DEFCLASS HOPF-ALGEBRA (coalgebra algebra)\n", + " ())\n", + " \n", + "(DEFCLASS SIMPLICIAL-GROUP (kan hopf-algebra)\n", + " ((grml :type simplicial-mrph :reader grml1)\n", + " (grin :type simplicial-mrph :reader grin1)))\n", + "```\n", + "\n", + "You see the definition of the *hopf-algebra* class is particularly striking; it explains that a Hopf-algebra is nothing but an algebra *and* a coalgebra; the compatibility conditions between both structures *cannot* be verified by a program and they necessarily depend on the programmer's \"lucidity\". In the same way, a *simplicial group* is a *kan* object and a `hopf-algebra` object sharing some common data, namely a coalgebra structure, with two further slots, `grml` (group\n", + "multiplication) and `grin` (group inversion), those slots being some simplicial morphisms.\n", + "\n", + "In such a multiple inheritance situation, it is important the `call-next-method` function works as hoped-for. Look at this artificial situation just to show the process; The `C` class has two subclasses `CD` and `CE`, which have in common the subclass `CDE`; the artificial `initialize-instance` methods let you verify that `call-next-method` *remembers its story* when deciding what really the *next method* must be. Here, when processing the `CD`-level, `call-next-method` \"remembers\" the process was initiated from the `CDE`-level, so that the `CE`-level stage is not forgotten.\n", + "\n", + "![](cde.png)" + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defclass C () ())" + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defclass CD (C) ())" + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 172, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defclass CE (C) ())" + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 173, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defclass CDE (CD CE) ())" + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 174, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defmethod initialize-instance ((c c) &rest rest)\n", + " (declare (ignore rest))\n", + " (print \"C-initialization\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 175, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 175, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defmethod initialize-instance ((cd cd) &rest rest)\n", + " (declare (ignore rest))\n", + " (print \"beginning CD-initialization\")\n", + " (call-next-method)\n", + " (print \"finishing CD-initialization\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 176, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defmethod initialize-instance ((ce ce) &rest rest)\n", + " (declare (ignore rest))\n", + " (print \"beginning CE-initialization\")\n", + " (call-next-method)\n", + " (print \"finishing CE-initialization\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 177, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 177, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defmethod initialize-instance ((cde cde) &rest rest)\n", + " (declare (ignore rest))\n", + " (print \"beginning CDE-initialization\")\n", + " (call-next-method)\n", + " (print \"finishing CDE-initialization\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\"C-initialization\" " + ] + }, + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 178, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(make-instance 'C)" + ] + }, + { + "cell_type": "code", + "execution_count": 179, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\"beginning CD-initialization\" \n", + "\"C-initialization\" \n", + "\"finishing CD-initialization\" " + ] + }, + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(make-instance 'CD)" + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\"beginning CE-initialization\" \n", + "\"C-initialization\" \n", + "\"finishing CE-initialization\" " + ] + }, + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 180, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(make-instance 'CE)" + ] + }, + { + "cell_type": "code", + "execution_count": 181, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\"beginning CDE-initialization\" \n", + "\"beginning CD-initialization\" \n", + "\"beginning CE-initialization\" \n", + "\"C-initialization\" \n", + "\"finishing CE-initialization\" \n", + "\"finishing CD-initialization\" \n", + "\"finishing CDE-initialization\" " + ] + }, + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 181, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(make-instance 'CDE)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And you may also play with the *auxiliary* `:before`, `:after` and `:around` methods to order as you like the various initialization steps. As a typical example, when the essential part of the initialization work of any `kenzo-object` is done, then the object is *finally* pushed in a list which is used later as explained in the next section. This is obtained as follows.\n", + "\n", + "```lisp\n", + "(DEFMETHOD INITIALIZE-INSTANCE :after ((k kenzo-object) &rest rest)\n", + " (push k *k-list*))\n", + "```\n", + "\n", + "In this way this is done if and only if the initialization work is successfully finished, even for the more specialized structures: if for example the specialized initialization work for a simplicial set fails and stops on error, then the pushing statement concerning the weakest structure is not run.\n", + "\n", + "## Optimizing computations.\n", + "\n", + "The Kenzo program is certainly one of the most *functional* programs ever written down. It is frequent that several thousands of functions are present in memory, each one being *dynamically* defined from other ones, which in turn are defined from other ones, and so on. In this quite original situation, the same calculations are frequently *asked again*. To avoid repeating these calculations, it is better to store the results and to systematically examine for each calculation whether the result is already available.\n", + "\n", + "Because of this situation, it is very important not to have *several copies* of the same function; otherwise it is impossible for one copy to guess some calculation has already been done by another copy. This is a very important question in this program, so that the following strategy has been used. Each Kenzo object has a rigorous *definition*, stored as a list in the `dfnt` slot of the object. This is the main reason of the top class `kenzo-object`: making easier this process. The actual definition of the `kenzo-object` class:\n", + "\n", + "```lisp\n", + "(DEFCLASS KENZO-OBJECT ()\n", + " ((idnm :type fixnum :reader idnm)\n", + " (dfnt :type list :reader dfnt)\n", + " (prpr :type list :reader prpr)\n", + " (cmmn :type list :reader cmmn)))\n", + "```\n", + "\n", + "> This class exists only in the [older version of Kenzo](https://github.com/gheber/kenzo/tree/master/archive/Kenzo-2).\n", + "\n", + "Then, when any `kenzo-object` is to be considered, its *definition* is constructed and the program firstly looks in `*k-list*` whether some object corresponding to this definition already exists; if yes, no `kenzo-object` is constructed, the already existing one is simply returned. Look at this small example where we construct the second loop space of $S^3$, then the first loop space, and then again the second loop space. In fact the initial construction of the second loop space required the first loop space, and examining the identification number `K??` of these objects shows that when the first loop space is later asked for, Kenzo is able to return the already existing one." + ] + }, + { + "cell_type": "code", + "execution_count": 182, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K405 Simplicial-Set]" + ] + }, + "execution_count": 182, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def s3 (sphere 3))" + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K422 Simplicial-Group]" + ] + }, + "execution_count": 183, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def o2s3 (loop-space s3 2))" + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K410 Simplicial-Group]" + ] + }, + "execution_count": 184, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def os3 (loop-space s3 1))" + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K422 Simplicial-Group]" + ] + }, + "execution_count": 185, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def o2s3-2 (loop-space s3 2))" + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 186, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq o2s3 o2s3-2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last statement shows the symbols `o2s3` and `o2s3-2` points to the same machine address. In this way we are sure any kenzo-object has no duplicate, so that the memory process for the values of numerous functions cannot miss an already computed result. Let us look some `dfnt` slots:\n", + "\n", + "> In the newer version of Kenzo, the slot name is `orgn` for *origin*." + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(LOOP-SPACE [K410 Simplicial-Group])" + ] + }, + "execution_count": 187, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(orgn o2s3)" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(LOOP-SPACE [K405 Simplicial-Set])" + ] + }, + "execution_count": 188, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(orgn (k 410))" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SPHERE 3)" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(orgn (k 405))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see in this way the history of the construction process can be freely examined by the user, which is important in the development stage.\n", + "\n", + "## Delaying initializations.\n", + "\n", + "The complete structure of a Kenzo object is extremely complicated, and many components are often useless. Another CLOS feature is therefore used to avoid the maybe non-necessary initialization works. The following artificial example explains how this is possible; it is a kind of \\emph{autoloading} mechanism, elegant, easy to be used, and useful to avoid initializing needless slots. We assume a `C` class, where each `C` object has two slots, `sl1` and `sl2`; the first one is necessary, but the second one would be the result of a *complex* process here simulated as being 1000 times the value of the first one." + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFCLASS F ()\n", + " ((sl1 :type integer :initarg :sl1 :reader sl1)\n", + " (sl2 :type integer :reader sl2)))" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 191, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFMETHOD SLOT-UNBOUND (class (fi f) (slot-name (eql 'sl2)))\n", + " (declare (ignore class))\n", + " (setf (slot-value fi 'sl2) (* 1000 (sl1 fi)))\n", + " (sl2 fi))" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "FI" + ] + }, + "execution_count": 192, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DEFVAR FI (make-instance 'f :sl1 23))" + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 193, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(SLOT-BOUNDP fi 'sl2)" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "23000" + ] + }, + "execution_count": 194, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(sl2 fi)" + ] + }, + { + "cell_type": "code", + "execution_count": 195, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 195, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(SLOT-BOUNDP fi 'sl2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see the generic function `slot-unbound` is available which is called by the error manager when a non-initialized slot is asked for. The standard process finally does generate an error. But the user can write specialized methods for this generic function, allowing him instead to initialize the missing slot by some process using the available information. You see the initialization process lets uninitialized the `sl2` slot of the `F`-instance located by `fi`, but when this slot is asked for, the \"right\" value is in fact returned! A new examination by `slot-boundp` shows the slot is now bound.\n", + "\n", + "This process is extremely convenient to organize the data as a living object where each time some missing component is questionned, an automatic \"repairing process\" is started, computing the missing information. The process may be recursive, so that if, in the repairing process, some other datum is again missing, another repairing process is recursively started, and so on.\n", + "\n", + "> The following discussion (counters) applies to the [older version of Kenzo](https://github.com/gheber/kenzo/tree/master/archive/Kenzo-2).\n", + "\n", + "This possibility is intensively used in the Kenzo program. Look at this small example. Firstly we reinitialize the environment by `cat-init`. When the fourth loop space $\\Omega^4 S^5$ is constructed, you see only 26 Kenzo objects are present in the environment. Then the homology group $H_2 \\Omega^4 S^5$ is asked for. The answer, $\\mathbb{Z}_2$ is quickly obtained, but the number of present Kenzo objects is now 504; an enormous set of `slot-unbound` calls has generated the construction of 478 new Kenzo objects, necessary to do the calculation. Furthermore a `:before` method had been added just to count the number of `slot-unbound` calls, a convenient debugging trick; you see the homology calculation has recursively generated 240 `slot-unbound` calls." + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "---done---" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 196, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(cat-init)" + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K1 Simplicial-Set]" + ] + }, + "execution_count": 197, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def s5 (sphere 5))" + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[K42 Simplicial-Group]" + ] + }, + "execution_count": 198, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def o4s5 (loop-space s5 4))" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " 4 SMGRs\n", + " 0 KANs\n", + " 8 SMMRs\n", + " 9 SMSTs\n", + " 0 HOPFs\n", + " 0 ALGBs\n", + " 9 CLGBs\n", + " 0 HMEQs\n", + " 0 RDCTs\n", + " 35 MRPHs\n", + " 18 CHCMs\n", + "---done---" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 199, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(how-many-objects)" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "COUNTER" + ] + }, + "execution_count": 200, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defparameter counter 0)" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 201, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defmethod slot-unbound :before (class instance slot)\n", + " (declare (ignore class instance slot))\n", + " (incf counter))" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Computing boundary-matrix in dimension 2.\n", + "Rank of the source-module : 1.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 19s.\n", + "Computing the boundary of the generator 1/1 (dimension 2) :\n", + "<>]>>]>>][1 <>]>>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + "Computing boundary-matrix in dimension 3.\n", + "Rank of the source-module : 2.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 19s.\n", + "Computing the boundary of the generator 1/2 (dimension 3) :\n", + "<>]>>][2 <>]>>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 19s.\n", + "Computing the boundary of the generator 2/2 (dimension 3) :\n", + "<>]>>]>>][1 <>]>>]>>][1 <>]>>]>>]>> \n", + "End of computing.\n", + "\n", + "\n", + "\n", + "\n", + "Homology in dimension 2 :\n", + "\n", + "\n", + "Component Z/2Z\n", + "\n", + "\n", + "---done---\n", + "\n", + ";; Clock -> 2017-02-26, 2h 19m 19s.\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(homology o4s5 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " 4 SMGRs\n", + " 0 KANs\n", + " 8 SMMRs\n", + " 17 SMSTs\n", + " 0 HOPFs\n", + " 0 ALGBs\n", + " 17 CLGBs\n", + " 16 HMEQs\n", + " 46 RDCTs\n", + " 395 MRPHs\n", + " 90 CHCMs\n", + "---done---" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 203, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(how-many-objects)" + ] + }, + { + "cell_type": "code", + "execution_count": 204, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 204, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "counter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mixing low level and high level programming.\n", + "\n", + "Computing time is crucial for the applications of the Kenzo program. The complexity of the implemented algorithms is highly exponential, so that the developer must carefully consider how he can improve the computing time of the written down Lisp code. In particular, if the heart of the program may be written close to the machine language, large amounts of computing time can be saved. But conversely this must not penalize the *readability* and the *modularity* of the program.\n", + "\n", + "Which is striking with the current version of Common Lisp is the possibility of easily mixing *low level* and *high level* programming. The features about OOP previously described in this paper show how Common Lisp is powerful in high level programming, allowing the user to directly handle the sophisticated objects of Algebraic Topology such as chain complexes, products and coproducts, Hopf algebras, simplicial sets and simplicial groups.\n", + "\n", + "iut on the other hand, the Kenzo program intensively uses the low level part of the Common Lisp language, that is, the quasi-assembler language which is the very root of the language, such as the popular (?) `car`, `cdr`, and `cons`. This is possible thanks to the Common Lisp *macrogenerator*, already mentioned [Section 3.5](#Improvements.). Let us consider the case of the type `absm`, that is, *abstract simplex*. These objects are really the most elementary consituents of the Kenzo geometric objects, and they are so intensively used, billions of times for every sgnificant Kenzo run, that you *must not* use CLOS for these kernel structures. Kenzo defines the `absm` type as follows:\n", + "\n", + "```lisp\n", + "(DEFUN ABSM-P (object)\n", + " (declare (type any object))\n", + " (the boolean\n", + " (and (consp object)\n", + " (eq :absm (car object))\n", + " (typep (cdr object) 'iabsm))))\n", + "\n", + "(DEFTYPE ABSM () '(satisfies absm-p))\n", + "```\n", + "\n", + "The `absm-p` function explains an `absm` is a `cons` (pair) where the lefthand component is the keyword `:absm` and the righthand one is an `iabsm`, that is, an *internal* `absm`; in the same way, elsewhere in the program, it is explained an `iabsm` is again a `cons` where the righthand component is anything and the lefthand component is a fixnum coding a *degeneracy operator*. Most of computations in Algebraic Topology are in fact low level computations about degeneracy\n", + "operators where such an operator is a decreasing list of small integers, like `(5 2 0)`; because this list is *strictly* decreasing, it can be represented by the fixnum 37 because $37 = 2^5 + 2^2 + 2^0$, so that all the standard calculations about degeneracy operators become fine calculations *at the bit level* on binary fixnums. But Common Lisp has all the\n", + "predefined functions to do such a job, so that the programmer can efficiently work according to this strategy. A considerable memory space is saved so and furthermore the calculations are much faster.\n", + "\n", + "If a degeneracy operator is to be extracted from an `absm`, the `dgop` macro is used:\n", + "\n", + "```lisp\n", + "(DEFMACRO DGOP (absm)\n", + " `(the dgop (cadr (the cadr ,absm)))\n", + "```\n", + "\n", + "which explains that in fact the call of `dgop` is synonymous with a call of the assembler-like `cadr`, but the types of argument and result are verified:" + ] + }, + { + "cell_type": "code", + "execution_count": 205, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "37" + ] + }, + "execution_count": 205, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(dgop (absm 37 'something))" + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "TYPE-ERROR: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 206, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(dgop 'not-an-absm)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When the program is compiled, the compiler firstly translates the source code when a macro call is found, so that it is an assembler-like statement which is compiled; furthermore an appropriate compiler option allows the compiled code to ignore or not the type verifications through the `(the ...)` statements. When the program is finalized for production work, of course these type verifications are discarded to save computing time. You see in this way the Lisp code is *readable*, this code being firstly translated in low level Lisp statements, therefore very efficiently compiled, without loosing if\n", + "necessary the type verifications.\n", + "\n", + "# References\n", + "\n", + "[ANSI] [ANSI Common-Lisp.](http://www.lispworks.com/documentation/HyperSpec/Front/index.htm)\n", + "\n", + "[CRML] Gunnar Carlsson and R. James Milgram. *Stable homotopy and iterated loop spaces*. in [[JAMS]](#JAMS), pp 505-583.\n", + "\n", + "[CMPS] [Chapter 1. Boost.Bind](http://www.boost.org/doc/libs/1_63_0/libs/bind/doc/html/bind.html#nested_binds)\n", + "\n", + "[DRSS] Xavier Dousson, Julio Rubio, Francis Sergeraert and Yvon Siret.*The Kenzo program*. [Source](http://www-fourier.ujf-grenoble.fr/~sergerar/Kenzo/)\n", + "\n", + "[JAMS] *Handbook of Algebraic Topology* (Edited by I.M. James). North-Holland (1995).\n", + "\n", + "[KCRB] Gregor Kiczales, Jim des Rivières and Daniel G. Bobrow. *The Art of the Metaobject Protocol*. MIT Press, 1991.\n", + "\n", + "[RBSR6] Julio Rubio, Francis Sergeraert.*Constructive Algebraic Topology*. Talk at the EACA Conference of Tenerife, 1999. To appear in *Bulletin des Sciences Mathématiques*.\n", + "\n", + "[SRGR3] Francis Sergeraert.*The computability problem in algebraic topology*. Advances in Mathematics, 1994, vol. 104, pp 1-29.\n", + "\n", + "[SRGR5] Francis Sergeraert. $\\maltese_k$, *objet du 3*e *type*. Gazette des Mathématiciens, 2000, vol. 86, pp 29-45.\n", + "\n", + "[STEL1] Guy L. Steele Jr. *Common Lisp*. Digital Press, 1984.\n", + "\n", + "[STEL2] Guy L. Steele Jr. *Common Lisp, second edition*. Digital Press, 1990." + ] + } + ], + "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": 1 +} diff --git a/examples/cltm/cde.png b/examples/cltm/cde.png new file mode 100644 index 0000000..616e4dc Binary files /dev/null and b/examples/cltm/cde.png differ diff --git a/examples/cltm/class-diagram.png b/examples/cltm/class-diagram.png new file mode 100644 index 0000000..19137d9 Binary files /dev/null and b/examples/cltm/class-diagram.png differ diff --git a/examples/cltm/kenzo-class-diagram.png b/examples/cltm/kenzo-class-diagram.png new file mode 100644 index 0000000..6f1e4ad Binary files /dev/null and b/examples/cltm/kenzo-class-diagram.png differ diff --git a/examples/cltm/the-main-initialization-generic-functions.png b/examples/cltm/the-main-initialization-generic-functions.png new file mode 100644 index 0000000..0cbc9c4 Binary files /dev/null and b/examples/cltm/the-main-initialization-generic-functions.png differ diff --git a/examples/cltm/tp-instance.png b/examples/cltm/tp-instance.png new file mode 100644 index 0000000..055243b Binary files /dev/null and b/examples/cltm/tp-instance.png differ diff --git a/examples/pcl/Packages in Common Lisp.ipynb b/examples/pcl/Packages in Common Lisp.ipynb new file mode 100644 index 0000000..129453c --- /dev/null +++ b/examples/pcl/Packages in Common Lisp.ipynb @@ -0,0 +1,5655 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Packages in Common Lisp, a tutorial\n", + "\n", + "By [*Francis Sergeraert*](https://www-fourier.ujf-grenoble.fr/~sergerar/Papers/Packaging.pdf), January 2014\n", + "(Adapted for [`cl-jupyter`](https://github.com/fredokun/cl-jupyter) by Gerd Heber, December 2016)\n", + "\n", + "> In `cl-jupyter`, the current package is called `\"CL-JUPYTER-USER\"`. On the other hand, the paper was written with `\"COMMON-LISP-USER\"` as the current package. Let's begin at the beginning!" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false, + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(in-package \"COMMON-LISP-USER\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "latex_metadata": { + "author": "Francis Sergeraert", + "date": "January 2014", + "title": "Packages in Common Lisp, a tutorial" + } + }, + "source": [ + "## Introduction.\n", + "\n", + "It is not possible to write a serious piece of code without the use of some kind of *identifiers* or names. Such identifiers are convenient means for the programmer to reference, process and modify data using more or less descriptive names, which represent the data and depend in some way on the context. The standard *modularization* technology encourages the divison of large programs in *modules*. Such modules can be relatively independent of each other, but conversely, they must be able to *communicate*. Most programming languages offer methods for such an organization. For example, the set of identifiers of a module could be divided into two parts, the *private* identifiers, known only inside this module, and the *public* identifiers, known everywhere.\n", + "\n", + "In this domain as in many others, Common Lisp is by far the most powerful and flexible programming language. At the expense of considerable complexity, Common Lisp's modular technology requires a good grasp of the corresponding *definitions* in the language standard. If the ANSI definition of the language is the only available documentation, these definitions are not easy to understand. An identifier in Lisp is a *symbol* which has a rich internal structure and which has lifetime and visibility during a Lisp session. The modular structure of a Lisp program is defined through the *packaging* system, which allows a programmer to divide a large program into several files, where each file is aware of all the symbols of a certain package, among them the current *default* package, and only the *external* (public) symbols of some other packages. A symbol which is not external in a package is *internal* (private). Also, a package may *import* (that is, \"see\") an arbitrary collection of symbols, internal or external to other packages. In the general spirit of Lisp, the whole structure of such an organization, if necessary, can be modified dynamically, even though this is not usually the best course of action.\n", + "\n", + "In this tutorial, a *mathematical* definition of this system is provided. This should elucidate the process for many a Lisp programmer and enable her to use it more effectively. A basic but *inexact* description of the packaging system is the following:\n", + "\n", + "- A *package* is a Lisp object in the same way that numbers, lists, and character strings are Lisp objects. In particular, the *type* of a package object is the system class [`package`](http://clhs.lisp.se/Body/t_pkg.htm). In Common Lisp, packages are first class citizens, which means they can be the value of a symbol, can be passed as an argument to a function, can be returned by a function, etc.\n", + "- A *symbol* is a Lisp object whose *type* is the system class [`symbol`](http://clhs.lisp.se/Body/t_symbol.htm). The comments made about packages apply for symbols. In particular, a symbol can be the value of another symbol, or even of itself.\n", + "- Symbols are divided into packages.\n", + "- At any time during a Lisp session, some package is the *default package* and, as a first approximation, only the symbols of this package are *accessible*. The default package can be modified at any time by the user or a program.\n", + "- If necessary, there are several methods to access the symbols of packages other than the default package. Conversely, a package may make its symbols more or less easily accessible from the other packages.\n", + "\n", + "This first approximation of the packaging system conveys a general idea of its organization, but general ideas can be deceiving. For example, the idea that the symbols are divided into packages is essentially *false*. More precisely, this divison can be made to appear essential (!), via the so-called home package of a symbol, but it plays only a *minor* role and can be ignored by the programmer risk-free. This notion of a home package is terribly misleading for beginners when they have to design non-trivial packaging structures. In a Lisp environment, one has to deal with two realms, the space of symbols and the space of packages. It is only *inside* of packages that something can be read, which, as an organization, looks a *little* like a division of symbols into packages, but the symbols themselves essentially *ignore* this division. Our subject is to clarify and explain this organization.\n", + "\n", + "The ANSI definition of the packaging system is a *complete* description of this organization, and, in a sense, nothing is missing. However, experience shows that it is easily misunderstood, especially, in non-trivial situations, where it can lead to erroneous programming choices and mysterious bugs. The ANSI definition of packaging is complete and it is the perfect *Reference Manual*. However, for a topic of this complexity a reference guide is not sufficient and a *User Guide* might be useful. And for that matter, an elementary *mathematical* description of the Common Lisp packaging system might help programmers to understand it, and then to use it with greater ease and also to greater effect. In every domain, when precise mathematical language can be employed and is understood, the state-of-affairs improves.\n", + "\n", + "We begin with a review of the ANSI definition of the packaging system while adding in parallel a mathematical description of the main concepts. Finally, we give a complete mathematical definition of this system as a set of mathematical formulas.\n", + "\n", + "## Main sets of objects.\n", + "\n", + "The first part of the packaging system is made of three disjoint sets:\n", + "\n", + "- The set $St$ of *strings*.\n", + "- The set $P$ of *packages*.\n", + "- The set $Sm$ of *symbols*.\n", + "\n", + "In our description of the packaging system, the statuses of these sets are different. Think of the (infinite) set $St$ of *strings* as made of *all* the character strings that *can* be used by a user or a program at any time. Any string can be used at any time, whatever the history of the Lisp session in which we are working may be. In the packaging system, most often, a particular string is used only for a short time, mainly during the read-eval-print loop, when the Lisp reader works. A string is just an intermediary connecting the user and the Lisp session, more precisely the current environment. In contrast, in a given environment, at any time during a Lisp session, some finite set $P$ of *specific* packages is installed in this environment, and also a finite set $Sm$ of *specific* symbols is available. Those packages and symbols can *then* be used, and they are the historical consequence of the current Lisp session. Various Lisp functions allow the user to modify the sets $P$ and $Sm$, and the way they are combined and releated to each other. In other words, consider the set of strings $St$ as constant, vast and independent of the environment, while, on the contrary, the sets $P$ and $Sm$ are restricted, they change during a Lisp session, and they are an essential component of the *current* environment.\n", + "\n", + "## Packages.\n", + "\n", + "There is a simple, almost bijective (one-to-one), correspondence between strings and packages, and a convenient place to start our subject. First, at any time during a Lisp session, a *default package* is defined and it can be discovered via the global symbol [`*package*`](http://clhs.lisp.se/Body/v_pkg.htm):" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "*package*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is to be read as: the *value* of the *symbol* `*package*` is the package whose *name* is the string `\"COMMON-LISP-USER\"`. The types `symbol` and `package` are system-defined, disjoint sets of machine objects. A symbol is frequently used as a convenient intermediate object, which allows the user to reach some object of arbitrary nature, its *value*. Here, `*package*` is a symbol, the value of which is the package named `\"COMMON-LISP-USER\"`. Technically, a symbol contains a pointer to its (possible) value, here a package which happens to be the package used ordinarily in a simple Lisp session. There are no facilities to refer to a package object *directly*. However, one can use its *name* and the function [`find-package`](http://clhs.lisp.se/Body/f_find_p.htm) to obtain a reference:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(find-package \"COMMON-LISP-USER\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Conversely, given a package, the function [`package-name`](http://clhs.lisp.se/Body/f_pkg_na.htm) returns its name:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"COMMON-LISP-USER\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-name (find-package \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The example was given to illustrate that the functions `find-package` and `package-name` are the inverse of each other, defining a 1-1 correspondence between the packages *currently* defined in the environment and their respective names, some character strings. Because of rules not of interest here, it is common and convenient to use only uppercase character strings to name packages. If a name does not correspond to any current package, the `find-package` function returns the symbol `nil`, usually displayed uppercase:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(find-package \"COMMON-LISP-user\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "which illustrates that a package name is case-sensitive. Frequently, only a few packages are in use, and because of their importance, descriptive names are used. These descriptive names can be a little long, and it is possible to define and use various *nicknames* for these names. For example, the `\"COMMON-LISP-USER\"` package usually has the nicknames `\"CL-USER\"` and `\"USER\"`. The function [`package-nicknames`](http://clhs.lisp.se/Body/f_pkg_ni.htm) can be used to retrieve the list of nicknames (strings) for a package, which doesn't include the name of the package." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(find-package \"COMMON-LISP-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(\"CL-USER\")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-nicknames (find-package \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If a package is to be unambiguously identified by its name or one of its nicknames, two different packages must have disjoint sets of *name + nicknames*.\n", + "\n", + "## Tracking a symbol.\n", + "\n", + "The Lisp symbols are very particular when compared to the identifiers of other programming languages. They persist throughout the runtime of an environment and are an important component of the power of Lisp. A package is referenced via its name; the same goes for a symbol, but the process is much more complex. The first and most common method to create or \"to *allocate* a symbol\" consists in just writing it down and passing it to the Lisp reader using the standard Lisp conventions. In a Lisp environment, there is a set of \"pre-defined\" symbols ready to be used. To caution the reader about the complexity of the subject, we begin with a seemingly strange example:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL NIL)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"X\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "UNBOUND-VARIABLE: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(X :INTERNAL)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"X\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The function [`find-symbol`](http://clhs.lisp.se/Body/f_find_s.htm) is analogous to the function `find-package`: given some *name*, a character string, it looks for a symbol with this name. Remember the correspondence between packages and names? A similar correspondence exists between symbols and names, but it's more involved. In particular, a name is *never sufficient* to determine a symbol, a *package* is also necessary. This is why the `find-symbol` function requires *two* arguments, the name of the symbol and the package with respect to which the search is to be done.\n", + "\n", + "At the beginning of our story, no symbol with the name `\"X\"` existed in the default package `\"COMMON-LISP-USER\"`, and the return value of `find-symbol` is negative, `nil`. (We will explain the second `nil` value in a moment.) Then we pass the Lisp reader the one character expression '`x`'. Lisp evaluates it according to its *read-eval-print* cycle. First, Lisp *reads* the expression and notices that the symbol `x` is used. It examines the current package `\"COMMON-LISP-USER\"`, and does *not* find such a symbol. Before doing anything else, Lisp *allocates* (creates) this symbol, the right terminology being: \"Lisp *interns* a symbol `x` of name `\"X\"` in the package `\"COMMON-LISP-USER\"`.\" To refer to this symbol, the pair made of *its* name `\"X\"` and the (not *its*!!) package `\"COMMON-LISP-USER\"` are necessary, but it would be cumbersome to have to write the expression `(find-symbol \"X\" \"COMMON-LISP-USER\")` every time this symbol is used. To simplify the process of referring to a symbol the designers of Lisp have organized the workspace as follows: if a symbol is read, its character string is capitalized, giving its *name*, and the default package is assumed. In other words, the text `'x'` is roughly equivalent to the text `(find-symbol \"X\" \"COMMON-LISP-USER\")`. How convenient! ('Roughly', because '`x`' may trigger the allocation of a symbol. In contrast, `find-symbol` never allocates a symbol; but see the function `intern` later.) In the second expression of our example, once Lisp has \"understood\" that the user intends to use the symbol `x`, and observing that such a symbol is not present in the package `\"COMMON-LISP-USER\"`, it allocates a symbol of name `\"X\"` and *interns* it in the package `\"COMMON-LISP-USER\"`. More specifically, a pointer referring to the just allocated symbol is added to the list $InS(p)$ of the internal symbols of the package $p$.\n", + "\n", + "Note also that it is common in Lisp to input the symbols in lowercase letters, and Lisp prints them in uppercase. This might be convenient for distinguishing input from output in an interactive session, but requires some clarification. In particular, there is no difference between the symbols `nil` and `NIL`, whereas the strings `\"nil\"` and `\"NIL\"` are different." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL NIL)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"nil\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL :INHERITED)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"NIL\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The fruitless search for a *symbol* with the lower-case name `\"nil\"` (a *string*) returns a double negative answer, but it is the second `NIL` that is meaningful! Compare that with the second expression which has the same first return value, but a different second one. The symbol `NIL` also plays in Lisp the role of the boolean *false*, which creates an ambiguity: if the answer is the symbol `NIL` is that a negative answer or is it a positive answer made of the symbol `NIL`? The ambiguity is resolved by the *second* return value. In the first case, the second `NIL` *confirms* that the first `NIL` is to be interpreted as the boolean *false*. In the second expression, the second return value `:INHERITED`, not a negative, indicates that the symbol `NIL` has been found, and that its *accessibility* via the package `\"COMMON-LISP-USER\"` is *inherited*, a point to be explained later. In other words, the function `find-symbol` returns a genuine symbol *and* its accessibility via the package argument, or a pair of `nil`'s if no symbol is found. The logician can deduce that an accessibility cannot be the symbol `nil`!\n", + "\n", + "Let us return to the one character expression '`x`'. The read part of the read-eval-print loop cycle reads the symbol `x` and interns it in the package `\"COMMON-LISP-USER\"`. The expression is *read* and must now be *evaluated*. The evaluation of a symbol consists in looking for a *value* of this symbol. To this end a value pointer in the symbol's internal representation is examined. In this case it is found that, at this time, there is no value for the freshly allocated symbol `x`. Therefore the *eval* step of the *read-eval-print* cycle generates an error, terminating the cycle with an informative error message. In any case, a symbol of name `\"X\"` is now present in the package `\"COMMON-LISP-USER\"`, as confirmed by the value of the last expression. The symbol's accessibility via the `\"COMMON-LISP-USER\"` package is *internal*, which is to be explained later.\n", + "\n", + "## Examining a symbol.\n", + "\n", + "A symbol is a Lisp object with a rich internal structure and there are several functions for exploring symbols and their properties. Let us examine in detail the symbol `x` allocated in the previous section." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"X\"" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-name 'x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The [`symbol-name`](http://clhs.lisp.se/Body/f_symb_2.htm) function returns the *name* of a symbol; it is analogous to the `package-name` function for a package. The Lisp function `symbol-name` is crucial for our subject, and we denote it as a mathemaical function $sn: Sm \\longrightarrow St$, a function which in general is not injective: several symbols may have the same name, this is our main subject.\n", + "\n", + "Note that in the previous expression the symbol `x` has been quoted `'x` to prevent its evaluation. More precisely, `'x` is an abbreviation for the expression `(quote x)`, where [`quote`](http://clhs.lisp.se/Body/s_quote.htm) is a *special* function which *does not* evaluate its argument, and it returns this argument as is. Hence, the previous expression is equivalent to:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"X\"" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-name (quote x))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, the non-quoted `x` does not generate an error. If instead you do not quote the symbol `x` (implicitly via `'`, or explicitly via `quote`) it will be evaluated before being used and generate an error:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "UNBOUND-VARIABLE: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-name x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The package which *owns* a symbol, its *home package*, can be determined with the function [`symbol-package`](http://clhs.lisp.se/Body/f_symb_3.htm)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package 'x)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(X :INTERNAL)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"X\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we have just seen, a symbol produces a name and a package (with the help of `symbol-name` and `symbol-package`). Conversely, given a name and a package, `find-symbol` produces the symbol with this name *via* this package. The reader might think that there is a correspondence between symbols and pairs of strings and packages:\n", + "$$Sm \\longleftrightarrow St\\times P$$\n", + "However, the situation is more involved: for the beginner, this might be a convenient point of view, but in fact such a point of view is wrong. If you intend to fully understand the complex relationship between strings, packages and symbols, please forget this tempting but terribly misleading correspondence. The goal of the next sections is to establish the correct point of view. For completeness we finish this section with a brief glance at the other data possibly stored in a symbol. A symbol can have a value. You may remember that in our Lisp session the symbol `x` does not (yet) have a value. This can be established by the predicate [`boundp`](http://clhs.lisp.se/Body/f_boundp.htm):" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(boundp 'x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The main method to give a symbol a value is the [`setf`](http://clhs.lisp.se/Body/m_setf_.htm) function (a macro). In most Common Lisp implementations, the `setf` macro may be used directly, in which case the default status of the argument symbol is *variable*. In strict ANSI implementations such as SBCL here, a `defvar` statement is required to explicitly define this status. Other possible statuses are *constant* (via `defconstant`) and *parameter* (via `defparameter`)." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "X" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar x)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4 5 6)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf x '(4 5 6))" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(boundp 'x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the first argument of `setf`, the symbol `x`, is not evaluated. Otherwise an error would be generated, since the symbol `x` does not yet have a value. After the `(setf ...)` expression has been evaluated, the symbol `x` does have a value, in this case, the list `(4 5 6)`. This value would now be the result of the evaluation of the expression `x`. It can also be obtained with the function [`symbol-value`](http://clhs.lisp.se/Body/f_symb_5.htm). (In this tutorial, we do not study the possible difference between, on the one hand, the simple *evaluation* of the symbol `x` asked for by the expression `'x'` and the *evaluation* of `(symbol-value 'x)` on the other. In our elementary examples there is no difference between the two. Possible differences might come from different variable scopes, such as *lexical* or *dynamic*, but this subject is not studied here.) " + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4 5 6)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4 5 6)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-value 'x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Don't forget to quote the symbol! See the following error:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "TYPE-ERROR: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-value x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A symbol can also have a *functional value*. This does not mean that the value we just discussed could be a function (which it could be!). It means that *besides* the previous value, a symbol can also have an *additional* value, a function, which can be used independently of the \"ordinary\" value of this symbol. For example the function $n \\mapsto 10−n$ could be recorded as the *functional* value of the same symbol, and this function could then be used as shown below." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "X" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defun x (n) (- 10 n))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Both values coexist in the symbol without any interference:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4 5 6)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-value 'x)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-function 'x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and can be used jointly and independently:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x (first x))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You might already suspect that the position of `x` just after a left parenthesis `(x ...)` makes Lisp extract the *functional* value and not the \"ordinary\" value. This is correct, but not the subject of this tutorial. Finally, we mention only in passing the following:\n", + "\n", + "1. A symbol can have a functional value while not having an ordinary value.\n", + "2. An ordinary value can be a functional object, usually then called via `funcall` or `apply`.\n", + "3. A symbol also contains a property list or `plist`, with many possible uses, but not at all studied here.\n", + "\n", + "The internal structure of the [symbol](http://clhs.lisp.se/Body/t_symbol.htm#symbol) `x` might be visualized thus:\n", + "\n", + "```\n", + "+-----------------+\n", + "| Name o-|-----> \"x\"\n", + "+-----------------+\n", + "| Value o-|-----> (4 5 6)\n", + "+-----------------+\n", + "| Function o-|-----> (lambda (n) (- 10 n))\n", + "+-----------------+\n", + "| Package o-|-----> #\n", + "+ ----------------+\n", + "| Property list o-|-----> NIL\n", + "+-----------------+\n", + "```\n", + "\n", + "## But what the hell is a package?\n", + "\n", + "Now we attack the heart of our subject.\n", + "\n", + "> **Fact 1:** A package $p\\in P$ maintains four fundamental lists:\n", + "- A list $InS(p)$ of its internal symbols.\n", + "- A list $ExS(p)$ of its external symbols.\n", + "- A list $ShgS(p)$ of its shadowing symbols.\n", + "- A list $U(p)$ of its used packages.\n", + "\n", + "The first important fact toward understanding the subject is this: the first three lists are lists of *symbols* or, more precisely, lists of pointers (machine addresses) aimed at the symbols. Every symbol has a *unique* \"existence\": a symbol is a unique machine object in the environment, but *several* packages may *see* the same symbol, that is, include the machine address of this symbol. In particular these lists are not lists of symbol names (strings). There is another mechanism that allows Lisp to reach a symbol given its name. Every symbol can be accessed directly via a suitable package, and several packages can in general play this role for the same symbol at a given moment in a Lisp session. For example, all the [standard Lisp symbols](http://clhs.lisp.se/Front/X_Symbol.htm) in the `\"COMMON-LISP\"` package typically are accessible via any package.\n", + "\n", + "No duplicate symbol (more precisely, no duplicate pointer) is to be found in each of these lists. Also the relations $InS(p)\\cap ExS(p)=\\emptyset$ and $ShgS(p)\\subset InS(p)\\cup ExS(p)$ must be satisfied, for reasons to be studied later.\n", + "\n", + "> **Fact 2:** Given the current set of packages, assumed *coherent*, and given the current state of all the fundamental lists of these packages, the function `find-symbol` can unambiguously determine *whether* a symbol of a given name (some string) is *accessible* via some package; if so, this symbol is unique.\n", + "\n", + "The importance of the package argument of `find-symbol` could make the reader believe the function `symbol-package` will be useful for various tests. This is not the case: All this function returns is the \"birth place\" of the symbol, technically called its *home package*, which says almost nothing about the current status of this symbol with respect to any package. All it says is that at least it is *interned* in this package. In particular, a symbol can be accessible via several packages, possibly different from its home package. Here is a simple illustration:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package 'nil)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL :EXTERNAL)" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"NIL\" \"COMMON-LISP\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL :INHERITED)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"NIL\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq (find-symbol \"NIL\" \"COMMON-LISP\")\n", + " (find-symbol \"NIL\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The *home package* of `nil` is `\"COMMON-LISP\"`, and this symbol of name `\"NIL\"` is accessible via `\"COMMON-LISP\"`, but also via `\"COMMON-LISP-USER\"`, though not with the same status. It is *external* in the first case and *inherited* in the second case, points to be examined later. The value (`T`) of the last expression *proves* that both symbols, determined by `find-symbol` via different packages, in fact are the same. Fact 2 can be made a little more precise: At any given time during a Lisp session, a set $P$ of packages and a set $Sm$ of symbols are defined. The various lists of symbols $InS(p)$, $ExS(p)$ and $ShgS(p)$, and the list $U(p)$ of packages used by a package $p\\in P$ together define without any ambiguity which symbols are accessible via the package $p$. In other words, there is a well defined function $\\rho$:\n", + "\n", + "$$\n", + "\\rho: P \\longrightarrow [St \\longrightarrow Sm \\cup \\{\\mathtt{nil}\\}]\n", + "$$\n", + "\n", + "\n", + "We chose the letter $\\rho$ for reader, because this function is mainly used by the reader. Our \"mathematical\" function $\\rho$ is nothing but an avatar of the Lisp function `find-symbol`. Let $p\\in P$ be a package and $st\\in St$ be a string. Then:\n", + "\n", + "$$\n", + "\\rho(p)(st) = (\\mathtt{find-symbol}\\; st\\; p)\n", + "$$\n", + "\n", + "The left-hand side uses the standard mathematical functional notation, and the right-hand one uses the Lisp notation. If no symbol is found for a pair $(st, p)$, the symbol (!) `nil` is returned and the second value of `find-symbol`, the symbol `nil` again, tells the user that the symbol search did not return anything. A package $p$ is nothing but a way of defining the function $\\rho(p):St\\longrightarrow Sm\\cup\\{\\mathtt{nil}\\}$. In other words, the four fundamental lists of a package determine what character strings appear as the names of the symbols accessible via this package. This approach is described in detail now.\n", + "\n", + "## Internal symbols.\n", + "\n", + "The first fundamental list of a package is the list of its *internal symbols*. It is a pity that no standard Lisp function can produce this list, but here is a simple solution to the problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PACKAGE-INTERNAL-SYMBOLS" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defun package-internal-symbols (package)\n", + " (let ((rslt))\n", + " (do-symbols (s package)\n", + " (when (eq (second\n", + " (multiple-value-list\n", + " (find-symbol (symbol-name s) package)))\n", + " :internal)\n", + " (push s rslt)))\n", + " rslt))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can list now all the internal symbols of a package:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(X QUICKLISP-INIT DEF-DIR CMD-ARGS N PACKAGE-INTERNAL-SYMBOLS S RSLT)" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"COMMON-LISP-USER\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In fact, only the first ten internal symbols are displayed. We see in particular the symbols `package-internal-symbols`, `s` and `rslt` used in the definition of our function. Some are rather esoteric, strictly speaking illegal: at the beginning of a Lisp session, the `\"COMMON-LISP-USER\"` package should in principle be void of symbols. To make our experiments more understandable it is better to allocate and use toy packages. The [`defpackage`](http://clhs.lisp.se/Body/m_defpkg.htm) function (a macro) allocates a package of a given name." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defpackage \"P1\" (:use \"CL\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A package `\"P1\"` is now present in our environment, without any internal symbols." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(The symbol `nil` also represents the empty list.) Let's allocate a few symbols and make them internal in the package `\"P1\"`! The current default package is `\"COMMON-LISP-USER\"` and we switch it to `\"P1\"` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(in-package \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "*package*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From now on, the default package is `\"P1\"`. If we write down a symbol then it will be understood by the Lisp reader as accessed via the package `\"P1\"`, that is, unless we use a special syntax. Let us create a list of three symbols and assign it to the symbol `list`, and then examine the internal symbols of the package `\"P1\"`:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB1 SYMB2 SYMB3)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf (symbol-value 'list) '(symb1 symb2 symb3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The reader probably would prefer `(defvar list)` followed by `(setf list ...)`, which is rejected by some Lisp implementations, in particular SBCL, because in principle the symbol `list`, owned by the package `\"COMMON-LISP\"`, is \"locked\". It is possible to unlock such a symbol, which is a little technical, but using `(symbol-value 'list)` as above gets the job done." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-STYLE-WARNING: \n", + " undefined function: PACKAGE-INTERNAL-SYMBOLS\n", + "\n", + "UNDEFINED-FUNCTION: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Oops! What happened? We've successfully used the function `package-internal-symbols` earlier and suddenly it seems to have disappeared from the environment. The point is that it was *defined when* `\"COMMON-LISP-USER\"` was the default package. Let us examine the situation!" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(COMMON-LISP-USER::PACKAGE-INTERNAL-SYMBOLS :INTERNAL)" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-INTERNAL-SYMBOLS :INTERNAL)" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A symbol with the given name is found in both packages. Before encountering the undefined functional value error, Lisp ( more precisely, the Lisp reader) had interned a *new* symbol with the name `\"PACKAGE-INTERNAL-SYMBOLS\"` in the current default package, `\"P1\"`, for the sole reason that it was present in the source text. Let us verify that both symbols are different!" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"COMMON-LISP-USER\")\n", + " (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The symbols are different. Compare that with the status of `nil`, where the same symbol `nil` was accessible via `\"COMMON-LISP\"` and via `\"COMMON-LISP-USER\"`. In our present situation both symbols are *different*, and so are their respective properties. For example, the first symbol does have a functional value and the second does not, which can be tested with the function [`fboundp`](http://clhs.lisp.se/Body/f_fbound.htm):" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(fboundp (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"COMMON-LISP-USER\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(fboundp (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is the first time we have seen the double-colon `‘::’` notation: When the symbol `package-internal-symbols` was searched via `\"COMMON-LISP-USER\"`, the answer was positive but the returned symbol was a little lengthy: `COMMON-LISP-USER::PACKAGE-INTERNAL-SYMBOLS`. The current default package is `\"P1\"`, via which *this* symbol is not accessible. However, we may access it using the double-colon notation:\n", + "\n", + "> `package-name::symbol-name`\n", + "\n", + "This is one of the methods which allows the user or Lisp to refer to a symbol not accessible via the default package. The Lisp reader is told: \"We mean the symbol `symbol-name` via the package `package-name`.\" In contrast, for the symbol with the same name but interned in the different `\"P1\"` package, because `\"P1\"` is the current default package, the double colon notation is not necessary. The longer notation lets us access a symbol interned in the `\"COMMON-LISP-USER\"` package and its functional value as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-INTERNAL-SYMBOLS SYMB2 SYMB1 SYMB3)" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(cl-user::package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `cl-user::` prefix tells Lisp to look for the symbol via the `\"CL-USER\"` package (Remember that `\"CL-USER\"` is a nickname for `\"COMMON-LISP-USER\"`?) and, because of its position just after a left parenthesis, to use its functional value. Currently, there are four internal symbols in the package `\"P1\"` and they are returned in an arbitrary order. By the way, what about the symbol `list`, to which we assigned a list (!) of symbols?" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(LIST :INHERITED)" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"LIST\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The symbol is accessible via the package `\"P1\"` but with a different status, the status *inherited*. We will examine this situation later. In fact, the symbol `list`, one of the most important symbols in Lisp, is already present in the package `\"COMMON-LISP\"` at the beginning of a session, and by default accessible from any package.\n", + "\n", + "We must mention also that our function $\\rho$, or the function `find-symbol` if you prefer, is (almost) injective *with respect to* $st$. Let $p_1$ and $p_2$ be two packages, $st_1$ and $st_2$ two strings. Then if $\\rho(p_1, st_1) = \\rho(p_2, st_2)\\neq\\mathtt{nil}$, then $st_1 = st_2$. Pay attention to the fact that the equality is an equality between *symbols*. This is a consequence of $\\mathtt{symbol-name}(\\rho(p, st)) = st$; in other words, there is no possibility of nicknames for a symbol name. In particular, there is a 1-1 correspondence between the internal symbols of a package and their respective names.\n", + "\n", + "## Importing a symbol.\n", + "\n", + "Instead of using the lengthy notation `cl-user::xxx` to make an internal symbol in `\"CL-USER\"` accessible from `\"P1\"`, we may opt to *import* this symbol in the package `\"P1\"`. However, in doing so there is another problem due to a name conflict, a point which will be examined in detail later:" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'cl-user::package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The problem is a \"collision\" between two *different* symbols, which cannot coexist as internal symbols in the *same* package `\"P1\"` with the *same* name. Importing the symbol internal in `\"CL-USER\"` into the package `\"P1\"` is not compatible with the current presence of *another* symbol in `\"P1\"` with the same `symbol-name`. We must first [`unintern`](http://clhs.lisp.se/Body/f_uninte.htm) the accidental, pre-existing symbol in `\"P1\"` to \"make room\" for the symbol from `\"CL-USER\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unintern 'package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'cl-user::package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-INTERNAL-SYMBOLS SYMB2 SYMB1 SYMB3)" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Lisp terminology is rather misleading: in fact, it is not at all the symbol which is imported, but the connection *string* $\\mapsto$ *symbol*. More precisely, let $p_1$ and $p_2$ be two packages, $st$ a string and $sm$ a symbol. If $\\rho(p_1)(st) = sm$ holds before the importation, then, after the importation, both relations $\\rho(p_1)(st) = sm$ *and* $\\rho(p_2)(st) = sm$ are satisfied. In particular, the first relation remains valid. It is not really an importation; it is a *copy* in the package $p_2$ of the connection $st \\mapsto sm$, available in the package $p_1$.\n", + "\n", + "Furthermore, nothing is modified about the symbol itself, except *possibly*, but rarely, its home package, as will be examined later. It is tempting for a beginner to think the home-package of the symbol is being modified. Not at all! Another erroneous interpretation would be to think a *copy* of the symbol to be imported is made \"inside\" the target package, with all the corresponding ingredients of the symbol, value, functional value, and so on. Wrong again! A copy is made, but only of a *pointer* toward our symbol found in $InS(p_1)$ and copied in $InS(p_2)$.\n", + "\n", + "The [`import`](http://clhs.lisp.se/Body/f_import.htm) function has two arguments, the *symbol* to be imported and the *target package*. The symbol must be well defined, that is, taking account of the current state of the environment. Here, before the import, the symbol is accessible only via `\"CL-USER\"`, and it is mandatory to use the notation `cl-user::...` (and the symbol must also be quoted to preclude its evaluation). After the import, as can be seen in the example, the symbol can be used functionally from the package `\"P1\"` without the prefix notation. Although the imported symbol is in the list of the internal symbols of the package `\"P1\"`, it is not quite the same as before. The next mandatory exercise consists in comparing the original symbol and the imported one." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'cl-user::package-internal-symbols\n", + " 'package-internal-symbols)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The function `eq` proves that both notations name the same symbol. Here's another proof:" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"CL-USER\")\n", + " (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you remember the `symbol-package` function, the next test is revealing:" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"CL-USER\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The *unique* symbol accessed by two different paths has of course a unique home package, namely its *birth place*, the `\"CL-USER\"` package. Please, do not pay too much attention to this symbol package; it is just an artifact of the session's history and does not have any real interest. You must focus on the chain $\\mathit{package}\\rightarrow[\\mathit{string}\\rightarrow \\mathit{symbol}]$ and not much else. There is no real reason to be concerned about the map `symbol-package`: $\\mathit{symbol}\\rightarrow\\mathit{package}$.\n", + "\n", + "## Name conflicts.\n", + "\n", + "If poorly understood, the introduction of Lisp packages to a Lisp project will sooner or later lead to *name conflicts*. There is nothing mysterious about them and there are standard \"home remedies\" for dealing with them.\n", + "\n", + "**Warning.** A name conflict can only be generated if two *different* symbols $sm$, $sm' \\in Sm$ end up with the same name: $sn(sm) = sn(sm')$. A name conflict occurs if these symbols $sm$ and $sm'$ become accessible via the same package $p$, which is a forbidden situation, since it would introduce ambiguity to the `find-symbol` function. On the other hand, the fact that some symbol (singular) is accessible via *different* packages never generates a name conflict by itself.\n", + "\n", + "Lisp constantly is on the lookout for possible name conflicts and stops with an error if the evaluation of an expression causes a name conflict. Some errors are *continuable*, which means that Lisp is able to propose a conflict resolution via `unintern` or `shadowing-import`. Remember the use of `unintern` in the previous section?\n", + "\n", + "What happens if an imported symbol has the same *name* as a symbol already accessible in the target package? In mathematical language, let $p_1$ and $p_2$ be two different packages, $st$ some string, and $sm_1$ and $sm_2$ two different symbols, internal in the respective packages $p_1$ and $p_2$, with the same name $st$. In other words, assume $\\rho(p_1)(st) = sm_1$ and $\\rho(p_2)(st) = sm_2$, $sm_1 \\in InS(p_1)$ and $sm_2 \\in InS(p_2)$, and $sm_1\\neq sm_2$. A situation of this sort could be the following (Remember the current default package is `\"P1\"`!):" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "COMMON-LISP-USER::SYMB4" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar cl-user::symb4)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "111" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf cl-user::symb4 111)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "SYMB4" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar symb4)" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "111" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf symb4 111)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'cl-user::symb4 'symb4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The evaluation of the first expression interns a symbol of name `\"SYMB4\"` in the package `\"CL-USER\"` and gives it the value 111, while the second expression interns a *different* symbol, again of name `\"SYMB4\"`, in the package `\"P1\"`, the current default package, and also gives it the value 111. As a result two symbols have been allocated, installed somewhere in the environment, with the same value of 111. Also the connections $\\rho(\\mathtt{\"CL-USER\"}): \\mathtt{\"SYMB4\"}\\mapsto \\mathtt{cl-user::symb4}$ and $\\rho(\\mathtt{\"P1\"}): \\mathtt{\"SYMB4\"}\\mapsto\\mathtt{p1::symb4}$ have been installed, that is, the addresses of both symbols have been pushed to the *respective* lists of *internal* symbols of the packages `\"CL-USER\"` and `\"P1\"`. The `eq` comparison confirms that the symbols just allocated are different. Two symbols with the same name `\"SYMB4\"` are now present in our environment. This is possible because these symbols are interned in different packages. Each one is accessible via the appropriate package. Both symbols are live and can be freely used:" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "222" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf cl-user::symb4 (+ symb4 cl-user::symb4))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What happens if we import `cl-user::symb4` into the package `\"P1\"`? Let's try!" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'cl-user::symb4 \"P1\") " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The importation is rejected, for it would not be possible anymore to coherently define $\\rho(\\mathtt{\"P1\"})(\\mathtt{\"SYMB4\"})$. We will see later that another process called *shadowing* allows the user to suppress the symbol `symb4` already present in `\"P1\"` and to authorize the importation of the symbol present in `\"CL-USER\"`.\n", + "\n", + "## The function `intern`.\n", + "\n", + "**Fact 3:** Let $p$ be a package and $(sm_1,...,sm_n)$ the list of its internal symbols. If $p$ is the default package, any of these symbols can be directly used without the double colon notation `'::'`. If $p$ is not the default package, these symbols can be used thanks to the notation $p::sm_i$. All these internal symbols have different names. Nothing prevents several packages from having the same symbols in their lists of internal symbols. This is possible thanks to the `import` function which can work only if no name conflict is generated.\n", + "\n", + "To be complete on this matter, we now to consider the Lisp function [`intern`](http://clhs.lisp.se/Body/f_intern.htm). It is used almost exactly as the function `find-symbol` with just a difference: if no symbol is found, then a symbol is allocated and interned in the package referred to in its second argument. Compare the follwing:" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL NIL)" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB5\" \"P1\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB5 NIL)" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (intern \"SYMB5\" \"P1\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB5 :INTERNAL)" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (intern \"SYMB5\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We start without any `symb5` symbol in `\"P1\"`, which is confirmed by the double negative answer of `find-symbol`. If instead we use the function `intern`, Lisp notices that such a symbol is absent from `\"P1\"` and therefore allocates a symbol of name `\"SYMB5\"` and *interns* it in the package `\"P1\"` as an internal symbol. The second value `nil` is an indicator of the allocation just made. If we repeat the same expression, which is not an error, then Lisp signals that such a symbol is already present and returns its status, which also informs us of its previous presence. A little weird! This definition of the function `intern` guarantees the coherence of the set of internal symbols of packages. A symbol can be allocated only through this function `intern`. It requires two arguments, a string $st$ and a package $p$. In a sense, the function `intern` first calls `(find-symbol st p)` with the same arguments. If a symbol is found it is returned without modification and no allocation of a new symbol takes place, which otherwise would create a name conflict. On the other hand, if a corresponding symbol is not found, it is allocated and interned in the package explicitly or implicitly quoted, and this new symbol certainly does not generate a name conflict. In this way, the uniqueness of the names of internal symbols of a package is enforced. This does not prevent two different symbols of the environment from having the same name, but the packaging system is organized such that they cannot be accessed via the same package." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-INTERNAL-SYMBOLS SYMB2 SYMB1 SYMB4 SYMB5 SYMB3)" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In practice, the Lisp reader constantly uses the `intern` function, for example when reading an expression. If it detects the presence of a symbol, the colon notation allows it to decide if a package is explicitly given, otherwise it must use the default package. The name of the symbol, alone or package-qualified using the colon notation, then unambiguously determines the symbol. If present, it is used, otherwise it is allocated and then used. This is exactly the role of the `intern` function.\n", + "\n", + "## Moving a symbol between two packages.\n", + "\n", + "Up until now, we have seen no means of modifying the home package of a symbol. In fact, it is possible but may seem a little contorted. It is almost without any practical interest except as an opportunity to use this as a vehicle to discover other facts. First, it is possible to *unintern* a symbol. For example, we can unintern the symbol `\"SYMB1\"` from the package `\"P1\"`. What happens is precisely that this symbol is removed from the list of the internal symbols of `\"P1\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unintern 'symb1 \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-INTERNAL-SYMBOLS SYMB2 SYMB4 SYMB5 SYMB3)" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What is modified is the *list* of internal symbols in the package `\"P1\"`. The symbol `symb1` itself has not been modified at all, except possibly its home package. In particular the symbol is not deallocated or destroyed, and therefore always present somewhere in the environment.\n", + "\n", + "But it's become inaccessible as such, for a symbol most often is accessible only via a package, the default one or some other explicitly given. The symbol `symb1` was interned only in the package `\"P1\"`, so that it appears now to be unreachable! If a symbol is truly unreachable, the Lisp garbage collector, always silently working in the background, will establish that fact and free the corresponding part of memory for reuse. Except that our symbol remains reachable, for it was the first element of the list assigned to the symbol `list`. Let's have a look at its value!" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(#:SYMB1 SYMB2 SYMB3)" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our symbol is still present in the list but in a little different form. To be sure, it is still a symbol:" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "SYMBOL" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(type-of (first list))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But what about its home package?" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package (first list))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "No home package, i.e., our symbol is \"homeless\"! The prefix `'#:'` indicates that this symbol is homeless. The `unintern` function had this effect because the package argument of `unintern` was the package which *owned* the symbol. Otherwise the home package would not be modified. Although our symbol has become homeless, it continues to be an ordinary symbol, for example, we can assign it a value:" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "444" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(set (first list) 444)" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "444" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-value (first list))" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "444" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eval (first list))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unlike `setf` (macro), the `set` function evaluates its arguments, so that before the assignment, the expression `(first list)` is evaluated, which returns our mysterious symbol `#:symb1`. Once that's done, the `set` function assigns (the value of) the second argument to it, as is confirmed by the other expressions. It is even possible to *import* a homeless symbol in a package, but then the target package becomes the home package of the imported symbol. The importing package can be different from the original home package. As a result our symbol is \"moved\" into another package. This explains the new form of the value of `list`." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import (first list) \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package 'cl-user::symb1)" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(COMMON-LISP-USER::SYMB1 SYMB2 SYMB3)" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our symbol could also have been imported in other packages before being uninterned from its home package. The status of our symbol with respect to other packages would not have been modified." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'symb3 \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'cl-user::symb3 'symb3)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unintern 'symb3 \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'cl-user::symb3 (third list))" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package 'cl-user::symb3)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'cl-user::symb3 \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(symbol-package 'cl-user::symb3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we import `symb3` in the package `\"CL-USER\"` before uninterning it from the package `\"P1\"`. The symbol is then accessible via the package `\"CL-USER\"` or via the list `list`. The evaluation of the expression `(eq ...)` proves that both paths lead to the same address. When the first `(import ...)` expression is evaluated, the symbol does have a home package and that it is not changed. When it is uninterned from the package `\"P1\"`, it becomes homeless. Lisp does not take account of the fact it is internal in other packages. In theory, this would allow it to select one of these packages as an \"emergency home\" package, but the choice would be quite arbitrary? Furthermore this would force Lisp to examine the list of all other packages and all their accessible symbols, which would be very expensive! Here, despite the symbol being internal and therefore interned in the package `\"CL-USER\"`, it becomes homeless after evaluating the `(unintern ...)` expression. Finally we import it in the package `\"CL-USER\"`, where, in fact, it is already present. According to the ANSI standard (see the following note), in such a situation, its status remains unchanged. In particular its home package remains `nil`. A little esoteric, huh? Don’t worry: the home package does not have any real role for the Lisp user.\n", + "\n", + "---\n", + "\n", + " **Note:** The [passage](http://clhs.lisp.se/Body/f_import.htm#import) in the [Common Lisp HyperSpec](http://clhs.lisp.se/Front/index.htm) reads like this: *\"If the symbol is already present in the importing package, import has no effect.\"* It appears that the behavior of SBCL and some other compilers is different: `Out[85]` is `#` not `NIL`.\n", + "\n", + "---\n", + "\n", + "We will prove later that if a symbol $sm$ has a home package $p$, then $sm$ is certainly present in $p$, that is, internal or external in this package. The \"converse\" is false: a symbol may be present in several packages $p_1,\\ldots , p_k$ while not having a home package. This might sound a little weird, but you shouldn't lose much sleep over it: except for the internal efficiency of the Lisp interpeter or compiler, the notion of a home-package is almost useless for the programmer.\n", + "\n", + "## External symbols and used packages.\n", + "\n", + "The analysis of this notion of an internal symbol was a little lengthy, but, in this way, the general structure of the packaging system is now very clear, and it will be a lot easier to understand the other concepts. Two fundamental lists maintained in the packages play a \"dual\" role in the packaging system, the list of *external* symbols and the list of *used* packages. The following function gives the list of the external symbols of a package." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "*package*" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PACKAGE-EXTERNAL-SYMBOLS" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defun package-external-symbols (package)\n", + " (let ((rslt nil))\n", + " (do-external-symbols (s package)\n", + " (push s rslt))\n", + " rslt))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The evaluation of the first expression is just to confirm that the default package remains `\"P1\"`. Let us see what the lists of external symbols of the packages `\"P1\"`, `\"CL-USER\"` and `\"COMMON-LISP\"` are:" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-external-symbols \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-external-symbols \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SIMPLE-ERROR DEBUG BOOLE-EQV MULTIPLE-VALUES-LIMIT ALPHA-CHAR-P WRITE-TO-STRING DEFSETF SHORT-FLOAT NOTINLINE CLASS-NAME ...)" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-external-symbols \"COMMON-LISP\")" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "978" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(length *)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are no external symbols in `\"P1\"` and `\"CL-USER\"`, but 978 such symbols in the package `\"COMMON-LISP\"`. (Only the first ten are being displayed.) A point of concern might be the following: these symbols are accessible via the package `\"COMMON-LISP\"` and yet the double-colon notation `common-lisp::simple-error` was not used. The explanation has two \"dual\" parts:\n", + "1. The symbol `simple-error` is external in the package `\"COMMON-LISP\"`.\n", + "2. The package `\"COMMON-LISP\"` is in the `package-use-list` of the package `\"P1\"`:" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SIMPLE-ERROR :EXTERNAL)" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SIMPLE-ERROR\" \"COMMON-LISP\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(#)" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-use-list \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(\"CL\")" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-nicknames (find-package \"COMMON-LISP\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`\"CL\"` is the nickname of the package whose official name is `\"COMMON-LISP\"`, and which is not to be confused with `\"COMMON-LISP-USER\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq (find-package \"CL\")\n", + " (find-package \"COMMON-LISP\")) " + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "# \n", + "# " + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq (print (find-package \"CL\"))\n", + " (print (find-package \"CL-USER\")))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Applied to a package $p$, the Lisp function [`package-use-list`](http://clhs.lisp.se/Body/f_pkg_us.htm) returns the list of all the packages used by $p$. If $p_1$ uses $p_2$, then it is said that $p_2$ is used by $p_1$. Whether this relation is reflexive can be doubted (What does \"$p$ uses $p$\" mean?), and it is neither symmetric (\"$p_1$ uses $p_2$\" does not imply \"$p_2$ uses $p_1$\") nor transitive (\"$p_1$ uses $p_2$\" and \"$p_2$ uses $p_3$” does not imply \"$p_1$ uses $p_3$\"). The oriented graph of the *use* relation is totally arbitrary.\n", + "\n", + "## First toy examples.\n", + "\n", + "Let's start over with two \"fresh\" packages `\"P1\"` and `\"P2\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(in-package \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(delete-package (find-package \"P1\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defpackage \"P1\" (:use \"CL\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defpackage \"P2\" (:use \"CL\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We switch the default package to `\"P1\"`:" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(in-package \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and we assume that, *in this environment*, we define the functions `package-internal-symbols` and `package-external-symbols` as explained before." + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PACKAGE-INTERNAL-SYMBOLS" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defun package-internal-symbols (package)\n", + " (let ((rslt))\n", + " (do-symbols (s package)\n", + " (when (eq (second\n", + " (multiple-value-list\n", + " (find-symbol (symbol-name s) package)))\n", + " :internal)\n", + " (push s rslt)))\n", + " rslt))" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PACKAGE-EXTERNAL-SYMBOLS" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defun package-external-symbols (package)\n", + " (let ((rslt))\n", + " (do-external-symbols (s package)\n", + " (push s rslt))\n", + " rslt))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What about the internal symbols of `\"P1\"`?" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-EXTERNAL-SYMBOLS PACKAGE-INTERNAL-SYMBOLS S RSLT)" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see the symbols naming our functions and also the symbols `rslt` and `s` used to define them. All the other symbols used in their definitions are in fact external in `\"COMMON-LISP\"` and absent here. We move now to the package `\"P2\"` from where we cannot access the symbol `package-internal-symbols` directly." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(in-package \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL NIL)" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"PACKAGE-INTERNAL-SYMBOLS\" \"P2\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have already seen the use of the `'::'` notation to make reference to the symbol at once. We can also *import* the symbol, in which case it becomes directly accessible from `\"P2\"` permanently:" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'p1::package-internal-symbols \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-INTERNAL-SYMBOLS)" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another solution requires *two* steps:\n", + "\n", + "1. Make the symbol `external` in some package, which is the role of the Lisp function [`export`](http://clhs.lisp.se/Body/f_export.htm).\n", + "2. Declare that the current package *uses* the package where the symbol is external.\n", + "\n", + "Let us use this method to make the symbol `package-external-symbols` directly visible from `\"P2\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export 'p1::package-external-symbols \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"P1\" \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-external-symbols \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PACKAGE-EXTERNAL-SYMBOLS)" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-external-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An `export` expression has the same form as an `import` one: `(export` $sm$ $st$ `)`, where $sm$ is a symbol and $st$ is a string naming a package. The symbol $sm$ must be readable from the current default package, for us `\"P2\"`, which explains the (required) prefix `'p1::'`. The argument $st$ is a string naming the package where the symbol $sm$ is to become external. Again, the terminology is somewhat misleading: the function `export` does not export anything. All it does is to make some connection $st \\rightarrow sm$ \"exportable\" to other packages via `use-package` expressions. The 978 external symbols of `\"COMMON-LISP\"` contain all the standard Lisp constants, variables and functions. Because an allocated package is always initialized with the package `\"COMMON-LISP\"` in its `package-use-list`, these basic Lisp objects will be accessible from any package. If we remove `\"COMMON-LISP\"` from the `use-package-list` of the current default package, via `unuse-package`, the situation becomes a little tricky.\n", + "\n", + "---\n", + "\n", + "**Note:** *We can't let go of* `progn`. *Otherwise* `cl-jupyter` *will stop functioning.*" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(common-lisp::import 'common-lisp::progn \"P2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "COMMON-LISP:T" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unuse-package \"COMMON-LISP\" \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-STYLE-WARNING: \n", + " undefined function: +\n", + "\n", + "UNDEFINED-FUNCTION: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "COMMON-LISP:NIL" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(+ 2 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The notation for the external symbol `t` in the package `\"COMMON-LISP\"` has become `common-lisp:t`, for `\"COMMON-LISP\"` is no longer used by `\"P2\"`. Since the symbol `t` is external in `\"COMMON-LISP\"`, the simple colon notation is sufficient. However, the most elementary operations are not easily accessible anymore, for example, the addition function `'+'`. This is not a good position to be in. Let us go back to the previous environment!" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "SIMPLE-STYLE-WARNING: \n", + " undefined function: USE-PACKAGE\n", + "\n", + "UNDEFINED-FUNCTION: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "COMMON-LISP:NIL" + ] + }, + "execution_count": 116, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"COMMON-LISP\" \"P2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This does not work since the symbol `use-package` can't be reached easily either, and we *must* use the prefix notation." + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "COMMON-LISP:NIL" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(common-lisp:use-package \"COMMON-LISP\" \"P2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But here is a new problem: earlier, when when we tried to use the symbol `'+'` from `\"P2\"`, Lisp detected that this symbol is not accessible from `\"P2\"` and therefore interned a fresh symbol of name `\"+\"` in `\"P2\"`. This was legitimate then, but it is now incompatible with `\"COMMON-LISP\"` having a different symbol with the same name `\"+\"`, if `\"P2\"` were to use `\"COMMON-LISP\"`. The same goes for `use-package`. We must unintern these symbols from `\"P2\"` to resolve the conflict. Let's not forget to use the colon notation for `unintern`, which is otherwise unreachable!" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "COMMON-LISP:T" + ] + }, + "execution_count": 118, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(common-lisp:unintern '+ \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "COMMON-LISP:T" + ] + }, + "execution_count": 119, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(common-lisp:unintern 'use-package \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(common-lisp:use-package \"COMMON-LISP\" \"P2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Phew, we are back in a state where Lisp is usable!\n", + "\n", + "## Importing an external symbol.\n", + "\n", + "Recall that a symbol $sm$ is *accessible* from a package $p$ if $\\rho(p)(st) = sm$ for $st = \\mathtt{symbol-name}(sm)$. In that case, the possible return values from `find-symbol` are as follows:\n", + "\n", + "1. If $sm$ is one of the internal symbols of $p$ the second value returned by `find-symbol` is `:internal`.\n", + "2. If $sm$ is one of the external symbols of $p$ the second value returned by `find-symbol` is `:external`.\n", + "3. If $sm$ is an external symbol of a package $p'$ used by the package $p$ the second value returned by `find-symbol` is `:inherited`.\n", + "\n", + "We have seen how to import a symbol from a package $p'$ into a package $p$. The example given used a symbol $sm$ internal in the package $p'$, which then became also an internal symbol of the package $p$ . This also works if $sm$ is external in the package $p'$, regardless of $p$ using or not using $p'$. (... assuming that no name conflict is generated.)\n", + "\n", + "Let's continue our journey in `\"P1\"`!" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(in-package \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB1\" \"P2\") \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(P2:SYMB1 :EXTERNAL)" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB1\" \"P2\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(NIL NIL)" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB1\" \"P1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We intern a symbol `p2::symb1` in `\"P2\"` and make it external immediately. As it stands this symbol is not accessible via `\"P1\"`. The fact that it is external in `\"P2\"` does not prevent us from importing it in `\"P1\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'p2:symb1 \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB1 :INTERNAL)" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB1\" \"P1\") )" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB1 :EXTERNAL)" + ] + }, + "execution_count": 127, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB1\" \"P2\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once the importation is done, the symbol is accessible via `\"P1\"` with the status *internal*. The *external* status with respect to `\"P2\"` is unchanged, which underlines that the status of a symbol does not depend on anything in the symbol itself. It is the *package* which must be examined to determine the status of a symbol *with respect to this package*.\n", + "\n", + "Curiously, the last `find-symbol` returns the symbol `symb1`, without the prefix `'p2:'`. Lisp prints a symbol according to the following rule:\n", + "\n", + "> First, Lisp determines whether a symbol is accessible via the current default package, in which case it is printed without using the colon notation. Otherwise, Lisp examines the home package, where the symbol is certainly internal or external, and it is printed with the double or simple colon notation. The only exception to this rule appears to be a *keyword*, that is, a symbol of the package `\"KEYWORD\"`. (Though the ANSI definition is somewhat incomplete for the `\"KEYWORD\"` package, we can assume that it is forbidden to change anything about the scope of keywords.) Keywords are printed simply as `':xxx'`, without any prefix. Finally, if the symbol is homeless, it is printed as `#:xxx`.\n", + "\n", + "The take-home message here is that an importation does not change anything for the relevant symbol; it is just a matter of updating a list of internal symbols in some package, that is, a list of respective pointers toward these symbols. Installing a new signpost in San Francisco that reads \"New York\" does not change anything in New York. Once more:" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 128, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq (find-symbol \"SYMB1\" \"P1\") (find-symbol \"SYMB1\" \"P2\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A novice could believe that adding `\"P2\"` to the `package-use-list` of `\"P1\"` could generate a name conflict, because the symbols `p1::symb1` and `p2::symb1` would become both accessible via `\"P1\"`. This is not the case: both notations represent one and the same symbol, and there is no ambiguity." + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 129, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"P2\" \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB1 :INTERNAL)" + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB1\" \"P1\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB1 :EXTERNAL)" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB1\" \"P2\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that because our symbol is internal in `\"P1\"`, the function `find-symbol` reports its status as `:internal`. This is in a sense preferred to the status `:inherited`, which after all would be possible. A name conflict occurs only if two *different* symbols with the same name could be accessible via the same package, possibly via another package used by the first one. Two symmetrical illustrations:" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB2\" \"P2\") \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB2 :INHERITED)" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (intern \"SYMB2\" \"P1\") )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is not an error: The second `intern` *does not* allocate a new symbol, a symbol of name `\"SYMB2\"` being already accessible via `\"P1\"`, which uses `\"P2\"`. In particular the second `intern` does not import the pre-existing symbol.\n", + "\n", + "We have to come up with something else to really allocate a new symbol." + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#:SYMB2" + ] + }, + "execution_count": 134, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(make-symbol \"SYMB2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 135, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import * \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(The `'*'` denotes a symbol which is always bound to the value of the last evaluation, here, our homeless symbol.)\n", + "\n", + "The function [`make-symbol`](http://clhs.lisp.se/Body/f_mk_sym.htm) allocates a *homeless* symbol of a given name, that's its definition. A homeless symbol cannot cause a name conflict, because it is *not* accessible via any package. Nothing prevents us from trying to `import` this symbol in `\"P1\"`, but this generates a name conflict between two different symbols: the freshly allocated `#:symb2` and the symbol external in `\"P2\"` accessible via `\"P1\"` which uses `\"P2\"`.\n", + "\n", + "Here's another way to cause a name conflict:" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB3 NIL)" + ] + }, + "execution_count": 136, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (intern \"SYMB3\" \"P1\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(P2::SYMB3 NIL)" + ] + }, + "execution_count": 137, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (intern \"SYMB3\" \"P2\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 138, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export * \"P2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The package `\"P1\"` uses the package `\"P2\"`, but this does not prevent them from having two different internal symbols with the same name `\"SYMB3\"`, for `\"P1\"` does not see the symbol internal in `\"P2\"`. If we try to make this symbol external in `\"P2\"`, a name conflict arises. \n", + "\n", + "Another sort of name conflict can occur between two used packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 139, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB4\" \"P2\") \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 140, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB4\" (defpackage :p3)) \"P3\")" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"P3\" \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compare that with:" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 142, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB5\" \"P2\") \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 143, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (import 'p2:symb5 \"P3\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 144, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unexport 'p3:symb4 \"P3\")" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 145, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"P3\" \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Two symbols of name `\"SYMB5\"` are external in `\"P2\"` and `\"P3\"`, but the second one is in fact the same as the first one: it has been imported and immediately made external in `\"P3\"`.\n", + "\n", + "This time the `use-package`, which makes the external symbols of `\"P2\"` *and* `\"P3\"` visible from `\"P1\"`, does not generate a name conflict, since we are dealing with two different paths which lead to the same *symbol*. This works as long as we do not forget to first `unexport` the symbol `\"SYMB4\"` of `\"P3\"`, i.e., to downgrade its status from *external* to *internal*.\n", + "\n", + "## A symbol can shadow another symbol.\n", + "\n", + "In the matter of name conflicts, the Lisp creators could have adopted another strategy and decided that the symbols *present* in a package, that is, *internal* or *external* in this package, have precedence over *inherited*, possibly different, symbols. However, they reasoned that this could easily generate true programming errors because of possible perverse collisions of identifiers, and turn into a source of obscure bugs. Nevertheless, in line with the general spirit of Lisp, if a programmer really wants to use such a strategy, she can do it on a case-by-case basis and explicitly describe the intended exemptions. This is the role of the [`package-shadowing-symbols`](http://clhs.lisp.se/Body/f_pkg_sh.htm) list, which is maintained in every package $p$ and denoted by $ShgS(p)$. This list is empty initially. $ShgS(p)$ can contain symbols present in the concerned package, internal or external. These symbols will automatically *shadow* any other competing symbol, such as symbols exported in other used packages and with the same `symbol-name`.\n", + "\n", + "The function [`shadow`](http://clhs.lisp.se/Body/f_shadow.htm) takes a symbol name (or a list of symbol names) and a package name, allocates a symbol with this name if it is not *present* in this package, and records it in the shadowing list of the package." + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 146, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(shadow \"SYMB6\" \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB6)" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-shadowing-symbols \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 148, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB6\" \"P2\") \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'symb6 'p2:symb6)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once the symbol `p1::symb6` is on the shadowing-list of `\"P1\"` the existence of a different external symbol with the same name in the used package `\"P2\"` does not generate a name conflict. The last `eq`-test proves both symbols are really different. Along the same lines, the function [`shadowing-import`](http://clhs.lisp.se/Body/f_shdw_i.htm), as clearly indicated in its name, imports a symbol and immediately adds it to the shadowing list, which could resolve a possible name conflict. For example the *different* symbols `p2:symb4` (external in `\"P2\"`) and `p3::symb4` (internal in `\"P3\"`) are in our environment; the package `\"P1\"` uses both packages `\"P2\"` and `\"P3\"`, but `p3::symb4` is internal in `\"P3\"` and therefore not in conflict with `p2::symb4`. Remember also the current default package is `\"P1\"`. A simple importation of `p3::symb4` in `\"P1\"` generates a conflict, while a `shadowing-import` adds the imported symbol to the shadowing list, shadowing the symbol `p2:symb4` and resolving the name conflict." + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB4 :EXTERNAL)" + ] + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB4\" \"P2\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(P3::SYMB4 :INTERNAL)" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB4\" \"P3\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'p3::symb4 \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(shadowing-import 'p3::symb4 \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB4 SYMB6)" + ] + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-shadowing-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If necessary, `p2:symb4`, shadowed by the internal `symb4` in `\"P1\"`, remains accessible via the colon notation.\n", + "The evaluations of the following expressions assign the same symbol value to both symbols as is confirmed by the subsequent '=' form. Evaluating the next `eq` expression proves that both *symbols* are different. In contrast, because `p1::symb4` was imported from `\"P3\"`, the last `eq`-test is affirmative." + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "SYMB4" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar symb4)" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "P2:SYMB4" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defvar p2:symb4)" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "111" + ] + }, + "execution_count": 157, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf symb4 (setf p2:symb4 111))" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 158, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(= symb4 p2:symb4)" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 159, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'symb4 'p2:symb4)" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 160, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'symb4 'p3::symb4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is even possible to add an *inherited* symbol to the shadowing list, maybe to declare a precedence with respect to another future competing symbol in another package intended to be used later. This experiment assumes three different packages, where one of them is not yet used. We first \"detach\" for example the package `\"P3\"` from `\"P1\"`, leaving `\"P2\"` used by `\"P1\"`. (Remember `\"P1\"` is the current default package.)" + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 161, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unuse-package \"P3\" \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then install two new external symbols with the same name in `\"P2\"` and `\"P3\"`:" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 162, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB7\" \"P2\") \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 163, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 163, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export (intern \"SYMB7\" \"P3\") \"P3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we have seen already with essentially the same example, if `\"P1\"` now decides to use `\"P3\"`, the two `\"SYMB7\"` symbols collide:" + ] + }, + { + "cell_type": "code", + "execution_count": 164, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "NAME-CONFLICT: \n", + " #\n" + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 164, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"P3\" \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In some context, it could happen that `\"P1\"` has no reason to use `p3:symb7` directly, and instead would like to give precedence to `p2:symb7`. This can be achieved with a shadowing import." + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB7 :INHERITED)" + ] + }, + "execution_count": 165, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB7\" \"P1\") )" + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 166, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(shadowing-import 'symb7 \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 167, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"P3\" \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `shadowing-import` refers to `p2:symb7` implicitly, which is external in a package used by `\"P1\"` and therefore inherited by `\"P1\"`. We see how the use of `shadowing-import` solves the name conflict, which otherwise would be raised by `use-package`. Let us review our symbols as seen now from our three packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB7 :INTERNAL)" + ] + }, + "execution_count": 168, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB7\" \"P1\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(SYMB7 :EXTERNAL)" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB7\" \"P2\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'symb7 'p2:symb7)" + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(P3:SYMB7 :EXTERNAL)" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(multiple-value-list (find-symbol \"SYMB7\" \"P3\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(# # #)" + ] + }, + "execution_count": 172, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-use-list \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The statuses of `symb7` have not changed in `\"P2\"` and `\"P3\"`, but seen from `\"P1\"`, the symbol is now *internal*. This is to satisfy the rule that any shadowing symbol in a package must be internal or external in this package, and also a consequence of the implied importation.\n", + "\n", + "## A mathematical definition of the packaging system.\n", + "\n", + "We recall that there are three general sets we must study:\n", + "\n", + "- The set $St$ of *strings*\n", + "- The set $P$ of *packages*\n", + "- The set $Sm$ of *symbols*\n", + "\n", + "There is one important difference: the set $St$ is the *infinite* set of all the possible strings, while $P$ and $Sm$ are *finite* sets made of the packages and symbols previously *allocated* in the current Lisp session.\n", + "\n", + "We use the notations $pn$ to denote the function `package-name` $pn: P \\rightarrow St$ and $sn$ to denote the function `symbol-name` $sn: Sm\\rightarrow St$.\n", + "\n", + "The map $pn$ is *injective* for the packages; the function $pn: P \\rightarrow pn(P)$ defines a 1-1 correspondence between the packages and their respective names. Here, we ignore the potential nicknames of a package, which is not essential for our discussion.\n", + "\n", + "Every package $P$ contains four *fundamental* lists:\n", + "\n", + "- The list $InS(P)$ of its internal symbols\n", + "- The list $ExS(P)$ of its external symbols\n", + "- The list $ShgS(P)$ of its shadowing symbols\n", + "- The list $U(P)$ of its used packages\n", + "\n", + "More precisely, these lists are lists of *pointers* to the mentioned objects, or, if you prefer, of the machine addresses of these objects. It is important to understand these lists are made of (pointers to) *symbols*, not of their names. Each of these lists is *without any duplicates*.\n", + "\n", + "The fundamental lists defined in a package are the source of a relatively extensive terminology, which is detailed now. The set of symbols *present* in a package $p$, denoted by $PrS(p)$, is defined as the union $PrS(p) := InS(p) \\cup ExS(p)$: a symbol is present in a package if it is internal or external in this package. This union is *disjoint*, the intersection $InS(p) \\cap ExS(p)$ must be empty. Also the relation $ShgS(P) \\subset Pr(P)$ is required. A shadowing symbol of the package $p$ must be internal or external in this package, i.e., a shadowing symbol is present.\n", + "\n", + "Let $q$ be a package used by the package $p$. The symbols of $q$ *shadowed* by a symbol of $p$ are:\n", + "$$\n", + "ShdS(q, p) = \\{sm \\in ExS(q)\\;\\mathbf{\\underline{st}}\\;\\exists sm' \\in ShgS(p)\\;\\mathbf{\\underline{st}}\\; sn(sm) = sn(sm')\\}\n", + "$$\n", + "\n", + "In other words, a *precedence* relation is defined by the list $ShgS(p)$: if ever a symbol $sm' \\in ShgS(p)$ and a symbol $sm\\in ExS(q)$ have the same name, then, with respect to the package $p$, $sm'$ takes precedence over $sm$, which means $sm$ is *shadowed*. If $sm'$ is present in $p$ but not shadowing, with $sm'\\neq sm$ and $sn(sm') = sn(sm)$, then a name conflict would be raised, which is forbidden.\n", + "\n", + "The inherited symbols of a package $p$, denoted by $InhS(p)$, are defined as:\n", + "$$\n", + "InhS(p) := \\Big\\{ \\bigcup_{q\\in U(P)}(ExS(q) − ShdS(q,p)) \\Big\\} − PrS(P)\n", + "$$\n", + "\n", + "A symbol is inherited in a package $p$ if it is external in a *used* package $q$, not shadowed by a shadowing symbol of $p$, and not present in $p$. The union in the preceding formula is not necessarily disjoint: a symbol can be external in several packages simultaneously, and without causing a name conflict. A symbol could be simultaneously present in $p$ and external in a package $q$ used by $p$. That is why the the last term, $−PrS(P)$, is necessary to define unambiguously what an inherited symbol is.\n", + "\n", + "Finally the accessible symbols $AccS(p) := PrS(p) \\cup InhS(p)$ are the symbols which are present or inherited. The very definition of $InhS(p)$ implies that this union is disjoint. In this way, the *status* of a symbol with respect to a package $p$ via which it is accessible is unambiguously defined as a keyword `:internal`, `:external` or `:inherited`. For a package $q\\neq p$ there is no relation whatsoever between the status of some symbol $sm$ with respect to $p$ and its status with respect to $q$. **Exercise:** Show that the sixteen pairs of possible status combinations can be realized!\n", + "\n", + "It is important to be clear about possible intersections (overlaps) between the lists defined above. Let $p$ be some package and $q$ another package used by $p.$ If $sm\\in ExS(q)$ is an external symbol of $q$, the symbol $sm$ *may* also be present in $p$, in which case its status is internal or external, but not inherited. In particular, this does not generate a name conflict, because it is the same symbol which is visible in two different packages via the same name, and a symbol has only *one* name! Nevertheless its status *with respect to* $p$ is not inherited. The status of a symbol is so defined deterministically, even if it is present in two interacting packages by one using the other. Of course the same symbol has status external in $q$.\n", + "\n", + "Following the same logic, if $q,q'\\in U(p)$, the intersection $ExS(q)\\cap ExS(q')$ is not necessarily empty.\n", + "\n", + "The Lisp system maintains the *coherence* of these lists, which is defined as follows:\n", + "\n", + "**Definition.** A set of packages is *coherent* if, for every package $p$, the function `symbol-name` defines a 1-1 correspondence $sn: AccS(p) \\rightarrow sn(AccS(p))$.\n", + "\n", + "This map is by definition surjective; it is injective if two different symbols accessible via $p$ have different names. In this way the function `find-symbol` is free of ambiguity. It searches first in the present symbols, and finally in the external symbols of the used packages. It is not necessary to compute the list of shadowed symbols since a shadow*ing* symbol, which is also a present symbol, is found first. The main purpose of the coherence rule is to prevent users from designing too contorted situations in name scopes, wich are frequently the source of subtle and difficult to find bugs.\n", + "\n", + "Several symbols with the same name may coexist in an environment. The most common way to access a symbol consists in using its *name*, but *different* symbols may have the *same* name. In fact, its symbol name is not sufficient to access a symbol, a package must also be specified. This can be the default package, in which case the direct notation without any colons is possible, or the package name can be explicitly used to prefix the symbol name using the one- or two-colon notation. Of course, the symbol so accessed must be defined in a *deterministic* manner. The definition just given says that a *unique* symbol of some name is accessible from some package.\n", + "\n", + "The main function in the packaging organization is a map:\n", + "$$\n", + "\\rho:P \\rightarrow [St \\rightarrow ((Sm\\cup\\mathtt{nil})\\times Stt)]\n", + "$$\n", + "where $Stt$ is a constant set of four symbols, the Lisp symbol `nil`, and three keywords, the possible statuses of an accessible symbol:\n", + "$$\n", + "Stt = \\{\\mathtt{nil}, \\mathtt{:internal}, \\mathtt{:external}, \\mathtt{:inherited}\\}\n", + "$$\n", + "Because of the assumed coherence of the package configuration, the function $\\rho$, an avatar of the function `find-symbol` of a package $p$ and a string $st$, determines a *unique* symbol named $st$ and accessible via $p$, and also its status. It is possible that the search fails, in which case the function returns the pair `(nil,nil)`. (More precisely `(values nil nil)`.) The second `nil` is a kind of a null status: \"There is nothing accessible via $p$ under the name $st$.\" The relation $\\rho(p)(s) = \\mathtt{(find-symbol}\\;p\\;st\\mathtt{)}$ is always satisfied.\n", + "\n", + "It is now relatively simple to understand the various Lisp functions for handling packages and symbols, in particular what name conflicts might be generated.\n", + "\n", + "### (intern $st\\;p$)\n", + "\n", + "This function first evaluates `(find-symbol` $st\\; p$`)`. If a symbol is found, *both* values of `(find-symbol ...)` are returned.\n", + "\n", + "If no symbol is found, then Lisp *allocates* (creates in memory) a new symbol without any value or function value. The `symbol-package` is initialized to $p$ and this symbol is added to $InS(p)$. In this case, `(values` $sm$ `nil)` is returned, the second value `nil` being a code for saying a new symbol has just been allocated.\n", + "\n", + "No name conflict can be created this way.\n", + "\n", + "This function is used implicitly by the Lisp reader when it encounters a symbol in an expression. If the colon syntax is not used, the default package is used for $p.$ Otherwise the (capitalized) package name of $p$ preceding the colon(s) is read, followed by the (capitalized) `symbol-name` $st$ after the colon(s). Finally, `(intern` $st\\; p$`)` is evaluated, which yields the symbol so *read* by the Lisp reader. In this way, the symbol can be either a pre-existing one or one freshly allocated as a consequence of this reading.\n", + "\n", + "\n", + "### (unintern $sm\\;p$)\n", + "\n", + "A symbol $sm$ is *interned* in a package $p$ if it is present in this package. It is *present* in package $p$ if $sm\\in PrS(p) = InS(p) \\cup ExS(p).$ Uninterning a symbol asks for this membership to be \"revoked.\"\n", + "\n", + "If $sm\\notin PrS(p)$, nothing is changed and `nil` is returned. Otherwise $sm$ is removed from $InS(p)$ or $ExS(p)$, and also from $Shg(p)$ if $sm$ was shadowing with respect to $p$; in this case, `unintern` returns `t`. In the last case, when uninterning a shadowing symbol, a name conflict can be generated: it is possible for symbols $sm'\\in ExS(q)$ and $sm''\\in ExS(q')$ to have the same name while being *different* symbols in the respective packages $q$ and $q'$ used by $p$, and that this collision was resolved by the symbol $sm$ shadowing both. If this happens, the `unintern` request is rejected.\n", + "\n", + "Also, if a symbol $sm$ is uninterned from a package $p$ and $p$ is the home package of $sm$, then the home package of $sm$ is modified and becomes `nil`, that is, the symbol becomes homeless. This is the only case where the symbol itself is modified by `unintern`. The same symbol could be external in another package used by $p$, in which case the symbol *remains* accessible via $p$. Otherwise, if the home package was `nil` or another package, it is not modified.\n", + "\n", + "### (import $sm\\; p$)\n", + "\n", + "This is a request for the symbol $sm$ to be added to $InS(p)$. Lisp first looks for a possible name conflict. This happens only if a *different* symbol $sm'\\neq sm$ with the same name ($sn(sm) = sn(sm')$) is already accessible via $p$. If so, the importation is rejected.\n", + "\n", + "If no name conflict is detected, Lisp proceeds as follows: If the symbol is already *present*, it is left *unchanged* in $InS(p)$ or $ExS(p)$. Else, if the symbol is *inherited* because of a package $q$ used by $p$, $ExS(q)$ is left unchanged and $sm$ is added to $InS(p)$ (not $ExS(p)$!). If $sm$ is not accessible via $p$, it is simply added to $InS(p)$.\n", + "\n", + "In the specific case of an imported homeless symbol, the `symbol-package` of the symbol is updated to $p$. This is the only case where a symbol object is modified by an importation. If the imported symbol is already owned by a package, whatever its home package, it is not modified.\n", + "\n", + "If the importation succeeds, the symbol will always be *present* in the target package, even if it was previously accessible only via a used package.\n", + "\n", + "### (export $sm\\; p$)\n", + "\n", + "The terminology \"export\" is not really appropriate, \"externalize\" would be more precise. The user of an `export` expression asks Lisp to record the symbol $sm$ in $ExS(p).$ This is allowed only if $sm$ is accessible from $p.$ In other words, the `export` function cannot *add* an accessible symbol to $p.$ In view of the rule $InS(p)\\cap ExS(p) = \\emptyset$, if present in $InS(p)$, the symbol $sm$ is also erased from that list. If $sm$ is external in a package $q$ used by $p$, its status in $q$ remains unchanged, but $sm$ is nevertheless added to $ExS(p)$.\n", + "\n", + "Remember that the `use-package` relation is not transitive. The fact that $sm\\in ExS(q)$ makes $sm$ accessible via $p$ using $q$ but not via $p'$ using $p$. If it is desired that $sm$ be accessible via $p'$, it is possible to make it external in $p$ by an `export` expression. Another solution would be to import $sm$ in $p'$, an operation which doesn't involve $p$.\n", + "\n", + "After exporting it, the symbol $sm$ becomes accessible from any package $q$ using $p$, that is, if $p\\in U(q)$. This can generate a name conflict with a possible *different* symbol $sm'$ of the same name, which is present in $q$ or accessible via $q$ in another package $p'\\in U(p)$, that is, if $sm'\\in ExS(p')$. If this happens, the *export* request is rejected.\n", + "\n", + "### (unexport $sm\\; p$)\n", + "\n", + "The symbol $sm$ must be accesible via the package $p.$ If the status of $sm$ is *external*, it is downgraded to *internal*. Otherwise it remains unchanged, even if it is *inherited*.\n", + "\n", + "### (shadow {$sm$ or $st$} $p$)\n", + "\n", + "It is intended to *import* the symbol $sm$ in $p$, to record it in the shadowing list $Shg(p)$ of the package $p$, and also to *resolve* any name conflict possibly raised by the importation.\n", + "\n", + "This time the first argument must be a *symbol*, a pre-existing one or a symbol just allocated because of its presence in the source statement. The symbol is imported (See the function `import`.) and added to the shadowing list $Shg(p)$ of $p$ if not yet present in this list. This addition to the shadowing list solves the possible name conflicts with symbols inherited from packages used by $p.$ It is possible that a *different* symbol *sm'* with the same name was *present* in $p$ beforehand. If so, such a symbol is *uninterned* from $p$, that is, erased from the lists $InS(p)$, $ExS(p)$ and $Shg(p)$.\n", + "\n", + "The difference between `shadow` and `shadowing-import` is clarified by the following toy example. We start a new Lisp session, allocate a package `\"P\"` and experiment with symbols named `\"A\"` and `\"B\"` in the packages `\"CL-USER\"` and `\"P\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 173, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(in-package \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 174, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defpackage \"P\" (:use :cl))" + ] + }, + { + "cell_type": "code", + "execution_count": 175, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(A B P::A P::B)" + ] + }, + "execution_count": 175, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(setf (symbol-value 'list) '(a b p::a p::b))" + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 176, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(shadow 'p::a \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 177, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 177, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(shadowing-import 'p::b \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(B A)" + ] + }, + "execution_count": 178, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-shadowing-symbols \"CL-USER\")" + ] + }, + { + "cell_type": "code", + "execution_count": 179, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'a 'p::a)" + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 180, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq 'b 'p::b)" + ] + }, + { + "cell_type": "code", + "execution_count": 181, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(A #:B P::A B)" + ] + }, + "execution_count": 181, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we `shadow` the symbol `p::a` in `\"CL-USER\"`, the symbol `a` remains different from the symbol `p::a`. In contrast, if we `shadowing-import` the symbol `p::b` in `\"CL-USER\"`, then the symbols `b` and `p::b` are equal. In a sense, the expression `(shadow ’p::a \"CL-USER\")` is cheating because the presence of `'p::'` has no role whatsoever, for only the name of the symbol `p::a` is used and this name is the same as the name `\"A\"` of `a`, that is, because of the default package, `cl-user::a`. The symbol defined by `\"A\"` via the package `\"CL-USER\"` remains unchanged, in particular, it remains different from the symbol `p::a`.\n", + "\n", + "In contrast, if `shadowing-import` is used, an `import` operation is induced, so that the symbol `p::b` becomes directly accessible via `\"CL-USER\"`. The previous symbol $\\rho:($`\"CL-USER\"`$)($`\"B\"`$)$ has therefore been *uninterned* and is now unreachable by `find-symbol`. However, it is still visible in the list named `list` as the homeless symbol `#:B`.\n", + "\n", + "The conclusion of our test is this:\n", + "\n", + "* The symbol `cl-user::a` is unchanged but added to $ShgS($ `\"USER\"` $)$.\n", + "* The symbol `cl-user::b` is almost unchanged, but no longer accessible via `\"CL-USER\"`. The only change is its `symbol-package` becomes `nil`, because the symbol has been uninterned from its home-package.\n", + "* The symbol `p::a` remains unchanged; only its name has been used for something which does not really concern it.\n", + "* The symbol `p::b` remains unchanged, except that it is now directly accessible via `\"CL-USER\"` and now printed as `B` in the value of `list`.\n", + "\n", + "\n", + "### (use-package $q$ $p$)\n", + "\n", + "Unless $q$ is already in $U(p)$, the package $q$ is added to the `package-use-list` of $p.$\n", + "\n", + "Lisp checks beforehand for possible name conflicts. A symbol $sm \\in PrS(p)$ could collide with a *different* symbol $sm'\\in ExS(q)$ if they have the same name $sn(sm) =sn(sm')$. If $sm = sm'$, no name conflict is generated. One cannot resolve such a name conflict by adding $sm'$ to $ShgS(q)$, hoping to give the precedence to the new inherited symbol: shadowing operates in a uni-directional manner, from a using package toward a used package. If one does so by a `shadowing-import` of $sm'$ into $p$, the symbol $sm$ would be coherently uninterned, erased from $PrS(p)$, and in a sense replaced by $sm'$. One can prepare the precedence of $sm$ over $sm'$ before the `use-package` by previously `shadow`ing $sm$ in $p.$ Also Lisp on its own can propose to *unintern* the symbol $sm$ in a *continuable* error in order to resolve such an observed name conflict.\n", + "\n", + "Another type of name conflict could be raised if another package $q'$ is already used by $p.$ It is then possible that two *different* symbols $sm \\neq sm'$ with the same name $st$ are external, respectively in $q$ and $q'$, in which case $\\rho(p)(st)$ is ambiguous. If so, evaluating the `use-package` expression would generate an error. On the other hand, the intersection $ExS(q) \\cap ExS(q')$ may be non-empty: a symbol external in both packages does not generate any ambiguity via $p$. As usual, it is always important to remember the lists such as $ExS(q)$ and $ExS(q')$ are lists of *symbols*, not strings. A name conflict can be generated only by *different* symbols having the same `symbol-name`.\n", + "\n", + "If no name conflict is detected, the package $q$ is added to $U(p)$ so that the new set of accessible symbols via $p$ is:\n", + "\n", + "$$\n", + "\\mathrm{new-}AccS(p) = \\mathrm{old-}AccS(p) \\cup (ExS(q) − ShdS(q, p))\n", + "$$\n", + "\n", + "### (unuse-package $q$ $p$)\n", + "\n", + "If the package $q$ is in $U(p)$, it is removed from this list.\n", + "\n", + "### A bug in package-internal-symbols?\n", + "\n", + "While preparing these notes, the author thought for a moment to have encountered a mysterious bug. The minimal model for this bug goes as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 182, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 182, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unuse-package \"P2\" \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 183, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(unuse-package \"P1\" \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 184, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(delete-package \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 185, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(delete-package \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 186, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defpackage \"P1\" (:use :cl))" + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "#" + ] + }, + "execution_count": 187, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defpackage \"P2\" (:use :cl))" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 188, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(use-package \"P2\" \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(export 'p2::a \"P2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "T" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(import 'p2::a \"P1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(P2:A P2:A)" + ] + }, + "execution_count": 191, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The point is that the standard Lisp function `do-symbols` runs through all the symbols *accessible* via `\"P1\"`, but it does not check for possible duplicates. Really? It seems possible that a symbol can appear more than once in $AccS($`\"P1\"`$)$!? This does not seem compatible with the exclusion of duplicates in general in $InS(p)$ and $ExS(p)$, and also the rule $InS(p) \\cap ExS(p) = \\emptyset$. Yes, but a – one !– symbol can be simultaneously in $InS(p)$ and $ExS(q)$ for a package $q$ used by $p$, which is the case here for our symbol `p1::a` = `p2:a`.\n", + "\n", + "The evaluation of `(do-symbols (s p) ...)` makes `s` run through all the internal symbols of $p$, then through all the external symbols of $p$, and finally through all the external symbols of $q$ for every package $q$ used by $p$. It happens that our symbol is in $InS($`\"P1\"`$)$ because it was imported there, and also in $ExS($`\"P2\"`$)$ where it was initially defined. Didn't the programmer of `package-internal-symbols` check the status of the symbol, so that the (fake) second symbol should have been rejected? No, not really, and it is interesting to see the path followed by Lisp: only the symbol-name and the examined package are used, so that when Lisp computes `(find-symbol (symbol-name s) package)`, it obtains *the* symbol, in fact the only one in this story, together with its status with respect to the `package` argument. In our case that is the package `\"P1\"` and the symbol's status (with respect to `\"P1\"`) is `:internal`, even if at the same time $s \\in ExS($ `\"P2\"` $)$.\n", + "\n", + "This illustrates some of the hidden traps and pitfalls that one might encounter as a result of different packages seeing the same symbol in various ways. Remember: no name conflict can be caused by one symbol alone. A name conflict happens only if *two* different symbols with the same name are \"visible\" by the same package. Nothing of this sort happened here, where *one* symbol is visible by several packages.\n", + "\n", + "To overcome this weakness in the implementation of `package-internal-symbols`, using the Lisp function `delete-duplicates` is sufficient." + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PACKAGE-INTERNAL-SYMBOLS-DEDUP" + ] + }, + "execution_count": 192, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defun package-internal-symbols-dedup (package)\n", + " (let ((rslt))\n", + " (do-symbols (s package)\n", + " (when (eq (second\n", + " (multiple-value-list\n", + " (find-symbol (symbol-name s) package)))\n", + " :internal)\n", + " (push s rslt)))\n", + " (delete-duplicates rslt)))" + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(P2:A)" + ] + }, + "execution_count": 193, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(package-internal-symbols-dedup \"P1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Symbol-package revisited.\n", + "\n", + "Throughout this tutorial, we kept insisting that this function is not really interesting. To be complete, we prove that a symbol $sm \\in PrS($ `(symbol-package ` $sm$ `)` $).$ In other words, if the symbol-package of the symbol $sm$ is $p$, then $sm$ is present in $p$, either internal or external. When a symbol is allocated (necessarily via `intern`), it is in particular interned in the package which is the second argument of `intern` so that it becomes a member of $InS(p)$. It may be exported in this package, which would make it a member of $ExS(p)$, but it remains present. The symbol can be added to $InS(q)$ for other packages $q$ via `import` or `shadowing-import`. It can also then be \"upgraded\" to $ExS(q)$ in those packages, but the membership in the initial package $PrS(p)$ remains unaffected.\n", + "\n", + "The only way to change this situation is using `unintern` with the package argument being $p.$ Then the symbol becomes homeless.\n", + "In turn, this can be changed with `import` and `shadowing-import`, in which case the symbol is interned in a possibly new package, and the story continues along similar lines. Finally, unless a symbol is homeless, it is always present in its home-package.\n", + "\n", + "This also shows that, as was mentioned in the introduction, the `symbol-package` qualifier of a symbol does divide the set of *interned* symbols into packages. It is impossible to have two different interned symbols with the same symbol-name and also the same symbol-package, for they would be interned in the same package, with the same name, and then the set of packages would not be *coherent*.\n", + "\n", + "An interned symbol is internal or external in its symbol-package, and certainly visible via this package. However, it may be visible from many other packages, and this information is not stored in the internal representation of the symbol. If it is external, it is accessible via the packages which use its package. The list of these packages can be determined by the standard Lisp function `package-used-by-list`. But a symbol can also be imported by other packages, which in turn may decide to make (the pointer to) this symbol external, and so visible from other packages.\n", + "\n", + "**Exercise:** Try to define a function `symbol-accessible-from` with one argument, a symbol, that returns the list of all packages where this symbol is accessible. Do not forget the contorted particular case of `common-lisp:nil`! Hint: Have a look at the standard Lisp function [`list-all-packages`](http://clhs.lisp.se/Body/f_list_a.htm#list-all-packages).\n", + "\n", + "An arbitrary number of homeless symbols may have the same name, as illustrated by:" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "#:Z \n", + "#:Z " + ] + }, + { + "data": { + "text/plain": [ + "NIL" + ] + }, + "execution_count": 194, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(eq (print (make-symbol \"Z\")) (print (make-symbol \"Z\")))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When `make-symbol` is called, Lisp allocates a *new* homeless symbol with the prescribed name, even if another one with the same name has been already allocated. You see *both* homeless symbols allocated above are printed in the same way, yet they are *different*.\n", + "\n", + "### Practical uses of the packaging system.\n", + "\n", + "Many of the somewhat esoteric technichalities detailed in this tutorial have no direct use in concrete programming. On the other hand, they can help to understand and demystify the behaviour of your Lisp system.\n", + "\n", + "A particularly irritating fact exhibited by some systems is the following: When you merely install, not load, a source file in the editor of your Lisp IDE, these systems immediately scan for symbols appearing in these files and intern them in a separate package. Sometimes this is the package indicated in an inital `(in-package \"XXX\")` of the file, or still more inappropriately, the standard `\"CL-USER\"` package, or also the current default package, even if the initial `in-package` references another package. Often this later leads to symbol collisions which are sometimes hard to identify. A developer who meets this kind of mysterious symbol collisions is advised to search first in this direction. In this case, you should make independent micro-experiments to study the actual behavior of your IDE when you install a file in your development environment. Probably in the early days of Lisp, this was done to improve the efficiency, but on modern systems, it is difficult to see compelling reasons to justify such a strategy.\n", + "\n", + "Most symbols of a Lisp file in fact are symbols with lexical scope, a scope limited to a small part of the code, typically *inside* a function, a `let` form, a `do` form and so on. These references are converted by the compiler into raw machine addresses, and these symbols have no real life at runtime.\n", + "\n", + "The most important symbols that have a real life at runtime are those that name functions, those defined through a `defun` form and the like. If a large program is divided in several files, then some decisions must be taken about the status of such a symbol.\n", + "\n", + "Usually, a correspondance \"file $\\leftrightarrow$ package\" is maintained. Several Lisp systems require that a file begins with an appropriate `(in-package ...)` expression, declaring that all the symbols used in this file are to be understood as present in this package, except when something else is a consequence of decisions taken outside this file or even in this file.\n", + "\n", + "In the ANSI definition of [`in-package`](http://clhs.lisp.se/Body/m_in_pkg.htm), the referenced package must already exist in the environment. Beginning a file by:\n", + "```cl\n", + "(defpackage \"MY-PACKAGE\")\n", + "(in-package \"MY-PACKAGE\")\n", + "```\n", + "is tempting but does not obey the requirement about the first instruction of a file being the appropriate `(in-package ...)`.\n", + "\n", + "The solution is to have a separate file in the project which is called aptly `packages.lisp` and which contains all the necessary `(defpackage ...)` expressions for the project. The `defpackage` form is flexible and can contain also all the main information about a package, in particular the list of its external symbols and its used packages. (See the ANSI definition of [`defpackage`](http://clhs.lisp.se/Body/m_defpkg.htm#defpackage).) This file is a good foundation for structuring the \"public\" documentation, explaining the role of these packages and their external symbols.\n", + "\n", + "Along the same lines, it might be worthwhile to have an `\"EXTERNAL\"` package which contains all the external symbols of the application. In this way, the other packages may use this package systematically, giving to this package a status similar to the status of the `\"COMMON-LISP\"` package. This does not prevent another package to import some symbols of other packages, symbols whose role is too specific to justify to make them \"globally\" external.\n", + "\n", + "### Shortcuts.\n", + "\n", + "There are several ways for the developer to shorten the Lisp expressions dealing with the packaging system.\n", + "\n", + "To be rigorous in our explanations and close to the deep structure of Lisp, in this turorial, we used a string identifier everywhere for a package. A symbol could be used instead, its name then being used as the intended string. For example, `(defpackage :my-package)` is equivalent to `(defpackage \"MY-PACKAGE\")`, for the name of the keyword `:my-package` is used, and because of the capitalization rule of the symbols applied by the Lisp reader. The name of `:my-package` is `\"MY-PACKAGE\"`, which through the `defpackage` expression becomes a package-name by following the standard rule which asks for an uppercase name.\n", + "\n", + "Also, each time a package argument is needed, frequently the second one, it can be omitted if this package is the current default package, which justifies this terminology. When using these shortcuts, do not forget about maintaining maximum readability of the source code!" + ] + } + ], + "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": 0 +}