diff options
Diffstat (limited to 'doc/propspec.rst.in')
-rw-r--r-- | doc/propspec.rst.in | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/doc/propspec.rst.in b/doc/propspec.rst.in new file mode 100644 index 0000000..bec4e6e --- /dev/null +++ b/doc/propspec.rst.in @@ -0,0 +1,175 @@ +Property application specifications ("propspecs") +================================================= + +If you understand the difference between propspecs and unevaluated propspecs +well enough to be able to read and understand most of the uses of +``DEFPROPLIST`` in Consfigurator's own source, then you can probably get away +without working through this section of the manual. You can build your own +propspecs by taking uses of ``DEFPROPLIST`` in Consfigurator's own source as +models. What follows is mainly for those who want to implement new property +combinators, though normal usage of Consfigurator should not require doing +that. + +Purposes +-------- + +Property application specifications are a domain-specific language which +enables + +1. combining properties to make new properties, e.g. with ``DEFPROPLIST``; and + +2. representing the properties which are to be applied to a host in a form + which can be serialised using the Lisp printer. + +Definitions +----------- + +An *atomic property application* ("propapp") is a list satisfying the lambda +list ``(PROPERTY &rest ARGS)`` where ``PROPERTY`` is a symbol naming a +property and ``ARGS`` satisfies that property's lambda list, or the empty +list, which means a no-op property. A *property combinator* is a function +which takes at least one propapp as one of its arguments and returns a single +propapp, or a macro which takes at least propapp as one of its arguments and +returns an expression which yields a single propapp (in the return values of +property combinators, often ``PROPERTY`` will be a gensym). A *property +application specification* is + +i. a readably printable Lisp expression, **P**, known as a *property + application specification expression*, which contains + + a. propapps combined using property combinators; but + + b. no free variables except as bound by macro property combinators, i.e., + such that the evaluation of **P** would not be affected by any dynamic + or lexical context; where + + c. evaluating **P** means an operation equivalent to quoting each propapp + occurring in **P**, and then passing the whole thing as the only + argument to ``EVAL``; and + +ii. a list of ASDF systems **S**; such that + +iii. evaluating **P** yields a propapp ``(PROPERTY &rest ARGS)``; such that + +iv. when each of **S** are loaded, calling ``PROPERTY`` with ``ARGS`` will + apply each of the propapps occurring in **P** in accordance with the + combinators occuring in **P**. + +A property application specification should not contain any binding forms +except as will be eliminated after macroexpansion. + +We do not always cleanly distinguish between property application +specifications and property application specification expressions, and the +abbreviation "propspec" refers to both. It is usually clear from the context +which is meant. Within Lisp they are easy to distinguish because property +application specifications are CLOS objects, whereas property application +specification expressions are just conses. + +An *unevaluated property application specification expression* ("unevaluated +propspec") is a list of Lisp forms which can be converted into a property +application specification expression by + +i. replacing each argument in each propapp with the result of evaluating that + argument in the dynamic and lexical contexts in which the unevaluated + propspec occurs; and + +ii. wrapping a single property combinator which takes a variable number of + arguments around the resulting list of Lisp forms, usually ``SEQPROPS`` or + ``ESEQPROPS``. + +That is, the arguments to propapps in unevaluated propspecs are forms which +will produce the arguments to the properties, rather than those arguments +themselves. There is one special case: if the symbol naming the property ends +with the character ``.``, then the propapp is replaced with a new one +according to the following rules (the "dotted propapp rules"): + +i. the property to be applied is the property named by the symbol in the same + package and with the same name as the first element of the propapp, but + with the trailing period removed from the name; + +ii. the first argument is not evaluated if it is a list whose first element is + a keyword, or a if it is a list of lists where the first element of the + first list is a keyword; and + +iii. the last required or optional argument to the property to be applied is + converted to a ``&rest`` parameter and its elements are treated as an + embedded unevaluated propspec, which is recursively converted into a + propspec according to the usual evaluation rules, where the surrounding + combinator is ``ESEQPROPS``. + +The dotted propapp rules are implemented by ``DEFPROP`` and ``DEFPROPLIST``, +which define a macro with the dotted name which performs the replacement. + +Available combinators +--------------------- + +``SEQPROPS`` +~~~~~~~~~~~~~ + +Function. Applies each of the propapps passed as arguments without stopping +if any of them signal a failed change. Semantically, the propapps are ordered +with respect to their ``:HOSTATTRS`` subroutines, but not with respect to +their ``:APPLY`` subroutines. + +``ESEQPROPS`` +~~~~~~~~~~~~~ + +Function. Applies each of the propapps passed as arguments, stopping and +signalling a failed change if any of the propapps signal a failed change. +Semantically, each propapp implicitly depends upon the preceding propapps. + +``UNAPPLIED`` +~~~~~~~~~~~~~ + +Function. Unapplies a single propapp. + +``ON-CHANGE`` +~~~~~~~~~~~~~ + +Macro. Applies properties when attempting to apply the first did not return +``:NO-CHANGE``. + +Remarks +------- + +The conversion of an unevaluated propspec into a propspec must resolve any +free variable references, except where those will be resolved by macro +property combinators. + +The ``PROPS`` macro converts an unevaluated propspec into a propspec. + +The evaluation of arguments to propapps in unevaluated propspecs appearing in +calls to ``DEFHOST``, ``DEPLOYS.`` and ``DEPLOYS-THESE.`` cannot retrieve +hostattrs, because these propspecs will be evaluated as part of the initial +definitions of hosts, before they have any hostattrs. By contrast, the +unevaluated propspecs in calls to ``DEFPROPLIST``, ``DEPLOY``, and +``DEPLOY-THESE``, and the code which produces propspecs in ``DEFPROPSPEC``, +may retrieve hostattrs set by other properties, because that code is run in +the context of a host which has already been defined. You cannot retrieve +hostattrs set by properties in the propspec resulting from evaluating the +evaluated propspec, however, since that propspec has not yet been applied to +the host. New hostattrs should not be pushed outside of the definitions of +``:HOSTATTRS`` subroutines. + +The elements of unevaluated propspecs are typically arguments to macros, such +that the context of evaluation for forms which produce the arguments to the +propapps is the context in which the call to the containing macro appears. +The single property combinator which will be wrapped around the list of forms +depends on the macro. ``DEFHOST`` uses ``SEQPROPS``, while ``DEFPROPLIST`` +uses ``ESEQPROPS``. + +The dotted propapp rules are intended to make applications of properties like +``DEPLOYS``, ``DEPLOYS-THESE`` and ``CHROOT:DEBOOTSTRAPPED``, which take +property application specifications as arguments, easier to read and write in +the most common cases. For example, you can write:: + + (deploys. (:ssh (:sudo :as "spwhitton@athena.example.com")) athena.example.com + (a-further-property val1) + (additional-property val2)) + +instead of:: + + (deploys '(:ssh (:sudo :as "spwhitton@athena.example.com")) athena.example.com + (make-propspec :props `((a-further-property ,val1) + (additional-property ,val2)))) + |