|
|
|
|
home / intro / faq / tutorial / manual / models / download / people / about |
||
| defining concepts with defcon |
|
The DEFCON macro will be useful to you if you need to specify new classes of concepts. This is a technique more suitable to an advanced user who has some experience with programming and object-oriented programming techniques. You may wish to skip to the next section if you just want to get a flavor of how b works.
required arguments DEFCON takes three required arguments: a name, options, and the field lambda list. The name must be a symbol (which does not contain the "." character, for reasons that will become clear later). The options are given as a list. This is a list of Lisp keywords (symbols in the keyword package written either KEYWORD:NAME or just :NAME, followed by the names of one or more concept classes from which the current class derives. These are said to be the superclasses of the class being defined. We will ignore keyword options for now. In the example above, to define a class Y which inherits the behavior of X, we would write (defcon y (x) (a)). Notice that we must redeclare the field A in y's lambda list. There are other optional arguments to DEFCON, but we won't treat them here.
| the defcon macro | |
| (DEFCON name
(option* superclass*) (ordered-field-def* [&OPTIONAL optional-field-def*] [&REST optional-field-def*] [&KEY optional-field-def*] [&REST optional-field-def*] [&PROPERTY property-field-def*] [&METHOD method-field-def*]) [validation-form*] [=> implication-form*] [WHERE where-pattern]) | |
| option | ::= :ABSTRACT | :TRACEABLE | :MATCHABLE |
| superclass | ::= non-keyword-symbol |
| ordered-field-def | ::= non-keyword-symbol | (non-keyword-symbol FIELD-TYPE) |
| optional-field-def | ::= non-keyword-symbol | (non-keyword-symbol [field-type] [[:= default-value-form] |   [:#= default-value-form]]   [:RELEVANCE default-relevance-form]) |
| validation‑forms | ::= code which checks or modifies the arguments provided by the object constructor. |
| implication‑forms | code which runs once after an object instance has been created |
| where‑pattern | a pattern matching expression which governs when an instance of the object should be created. |
identity fields Identity fields are specified in the third argument to DEFCON, called the concept lambda list. The third argument to DEFCON, "(A)", is called the concept lambda list. It has two roles: it defines the fields available in the concept, and it defines a protocol for mapping values (provided in the object constructor) to fields. The structure of a concept lambda list is similar to ordinary Lisp lambda lists. That is, the lambda list keywords (i.e., &optional, &key, &rest) may appear in the list, and control the protocol by which the value for the field is specified.
lambda list keywords Note that the phrase "lambda list keywords" refers specifically to symbols beginning with the ampersand (&) which govern the interpretation of a lambda list. Usually when referring to "keywords", we mean symbols in the special "KEYWORD" package. These are written with a shorthand notation, :NAME, but could also be written KEYWORD:NAME. Symbols in the keyword package are treated specially in Lisp: they are constants which self-evaluate (evaluating returns the same symbol object), and are externally accessible from the KEYWORD package, and are generated automatically even when referred to from outside the keyword package (not true of symbols in other packages). They are used throughout Lisp as convenient symbolic constants.
Lambda list keywords control the protocol for assigning object constructor arguments to fields. For instance, we could have written, (defcon x () (&optional a)), in which case the value for A is optional, with a default value of NIL. In this case, evaluating [x] would produce [x NIL], and evaluating [x 1] would still produce [x 1]. The &key lambda list keyword indicates that keyword-calling style is used. So, (defcon x () (&key a)) would require us to use [x :a 1] (which would print out [x :A 1]) to specify a value of 1 for the A field. Keyword arguments, just like for ordinary Lisp functions, are optional with a default value (if not otherwise specified) of NIL. So evaluating [x] produces [x :A NIL].
The other lambda list keyword that is available is &rest, which collects an indeterminate number of remaining arguments into a list. Writing (defcon x () (&rest a)) would also allow us to write [x], which would evaluate x with the field A being NIL (which is also known as the empty list, sometimes written ()). Defined this way, the concept would print out as [x], not [x NIL], which has the meaning of A being the list containing NIL. Given this definition, we could write [x 1 2], and the field A would be a list containing the numbers 1 and 2: (1 2). This concept object would print as [x 1 2].
concept lambda list keywords may be combined in the same way as ordinary Lisp lambda list keywords. &Optional fields must precede any &key and &rest fields. The table below summarizes some of the above discussion, and shows examples of combining lambda list keywords. Notice that the lambda list keywords govern not only how arguments are mapped to fields, but also how the concept prints.
| class definition | example object ctor | comments | printed object |
| (defcon x () (a)) | [x 1] | Single non-optional identity field | [x 1] |
| (defcon x () (&optional (a := 1))) | [x 1] or [x] | Optional identity field, with default value 1. | [x 1] |
| (defcon x () (&key (a := 1))) | [x :a 1] or [x] | Optional keyword identity field, with default value 1. | [x :A 1] |
| (defcon x () (a &key (b := 1))) | [x 1 :b 2] or [x 1] | Two identity fields, one required, and one optional keyword field. | [x 1 :B 2] |
| (defcon x () (&rest (c := '(1 2 3)))) | [x 1 2 3] or [x] | Identity field C contains all the arguments are collected into a list. | [x 1 2 3] |
| (defcon x () (a &optional (b := 2) &rest (c := '(3 4)))) | [x 1] or [x 1 2] or [x 1 2 3 4] | A complex concept lambda list with optional and rest arguments. | [x 1 2 3 4] |
optional arguments DEFCON has several optional arguments: validation-formss, implication-forms and where-patterns. The validation-form area is where you can enter code which will run when an object constructor is encountered. It is intended for checking and modifying object constructor arguments before the object is constructed.
| using validation |
| (defcon x () (&rest args) (unless (every #'numberp args) (error "Expecting every argument to be a number.")) {.args := (sort args #'<)}) ; sort the numbers in ascending order ;; these tests will return true: {[x 1 2 3] = [x 2 3 1]} {[x 1 2 3] = [x 3 1 2]} |
| end of tutorial - content is under development |