Fix missing `without-special-symbol-access' in `funcall-with-attribute-context'
[clinton/lisp-on-lines.git] / doc / tutorial.txt
1 \modeline{-*- mode: outline; transient-mark-mode: t -*-}
2
3 * Lisp on Lines : An Tutorial.
4
5 **. intro
6
7 Lisp on Lines is a framework for rapid development of data-driven applications, with a particular focus on web-based applications. It builds on the UncommonWeb engine and Contextl, and uses CLOS and the MOP extensively. Most of LoL can be used both at the REPL and through the browser, offering many options for development and testing.
8
9 While the target audience for LoL is developers experienced with both web technologies and common lisp, a good programmer with a little experience in either should be able to pick things up fairly quickly.
10
11 ** Describing the domain with MAO.
12
13 LoL uses a protocol known as Meta-Attributed Objects, or MAO, as the basis of its display mechanism. In MAO, we create context-aware DESCRIPTIONs of objects, and those descriptions are used to generate the display of the object itself. By having these external descriptions change based on the context in which they are used, a few generic components can come together to create complex applications.
14
15 Descriptions are a similar to classes. Every Lisp object has one, and the root description that all descriptions inherit from is known as T. FIND-DESCRIPTION is used to, well, find descriptions.
16
17 \code{
18 (find-description t) \=>{#<DESCRIPTION T {B7B9861}>}}
19
20 A description is really just a collection of attributes. Each attribute describes a part of an object, and any number of attributes may or may not be active. The ATTRIBUTES function is used to find a the list attributes that are both active and applicable in the current context.
21
22 \code{ (attributes (find-description t) )
23
24 \=>{(#<ATTRIBUTE IDENTITY {BBC9691}>
25 #<ATTRIBUTE TYPE {BBC96A1}>
26 #<ATTRIBUTE CLASS {BBC96B1}>)}}
27
28 When a description is associated with an object, these attributes have properties, for example ATTRIBUTE-LABEL and ATTRIBUTE-VALUE. By simply iterating through the attributes of a described object, we can create a generic display for any lisp object. This is very similar to the technique outlined by (foo) in MEWA: A meta-blah blah.
29
30 MAO adds to MEWA the concept of dynamic context. By changing the context in which an object is described, we combine and specialize the generic displays, ultimately creating different views of our objects. LoL uses ContextL extensively. Descriptions are contextl layers, and attributes themselves are layered classes. Most of the exported functions are layered methods, and the idea of dynamic context-sensitivity is used throughout LoL. If you're not familiar with contextl, don't worry, LoL mostly stands on its own. Still, reading through the material won't hurt.
31
32 The functions DESCRIPTION-ATTRIBUTES, DESCRIPTION-ACTIVE-ATTRIBUTES and DESCRIPTION-CURRENT-ATTRIBUTES return all the descriptions attributes, Attributes that are currently active regardless of context, and attributes that exist in the current context but may or may not be active, respectively.
33
34 To access the properties of an attribute, it is neccessary to create the proper context. For the most part, LoL does this for you, but for demonstration purposes we'll do it manually. The function FUNCALL-WITH-DESCRIBED-OBJECT takes care of setting up the proper context. There is some syntax for it in the form of WITH-DESCRIBED-OBJECT.
35
36 \code{
37 (let ((description (find-description t))
38 (object "Hello World"))
39 (with-described-object (object description)
40 (dolist (a (attributes description))
41 (format t "~@[~A: ~]~A~%"
42 (attribute-label a)
43 (attribute-value a)))))
44
45 \outputs{Hello World
46 Type: (SIMPLE-ARRAY CHARACTER (11))
47 Class: #<BUILT-IN-CLASS SB-KERNEL::SIMPLE-CHARACTER-STRING>}}
48
49 FUNCALL-WITH-DESCRIBED-OBJECT binds two specials, *description* and *object*, to its arguments. Knowing this, we can shorten our code somewhat. Later on we'll be far away from the lexical bindings of description and object, so these special variables are essential.
50
51 \code{
52 (with-described-object ("Hello World" (find-description t))
53 (dolist (a (attributes *description*))
54 (format t "~@[~A: ~]~A~%"
55 (attribute-label a)
56 (attribute-value a))))}
57
58 Lets wrap that up in a function that we can re-use
59
60 \code {(defun present (object description)
61 (with-described-object (object description)
62 (dolist (a (attributes *description*))
63 (format t "~@[~A: ~]~A~%"
64 (attribute-label a)
65 (attribute-value a)))))}
66
67 ** Defining description contexts
68
69 The basics of MAO should now be clear, so lets start using it. First, lets create our very own description.
70
71 \code{(define-description hello-world ()
72 ((title :value "Lisp on Lines Demo")
73 (identity :label "Message")
74 (length :label "Length" :function #'length)
75 (active-attributes :value '(title identity length))))}
76
77 Descriptions are very much like CLOS classes, and are in fact implemented that way, so normal inheritance rules apply. Attributes can have any number of properties, (see the class STANDARD-ATTRIBUTE), but the three most important are ATTRIBUTE-LABEL, ATTRIBUTE-VALUE and ATTRIBUTE-FUNCTION, named by the :label, :value, and :function keywords. ATTRIBUTE-LABEL is simply a textual label that describes the attribute. ATTRIBUTE-VALUE is defined to return the result of calling ATTRIBUTE-FUNCTION with the object. If ATTRIBUTE-FUNCTION is NIL, the value :value property is returned directly.
78
79 In the example above, the IDENTITY and ACTIVE-ATTRIBUTES attribures are inherited from T, and we are simply overriding the default properties for our description. LENGTH and TITLE are specific to this description. A look at src/standard-descriptions/t.lisp may be instructive at this point.
80
81 Now, we can present our object using our new description. LoL include a DISPLAY function, but for now we'll use our PRESENT function, as it's a little more transparent.
82
83 \code{(present "Hello World" (find-description 'hello-world))
84 \outputs {Lisp on Lines Demo
85 Message: Hello World
86 Length: 11})
87
88 When an object is being described, the description context is also made active. One can also activate/deactivate individual description contexts, without using them to describe an object. One can also define partial descriptions that are only active when other description contexts have been activated. This is easier to show than to tell.
89
90 \code{(define-description one-line ())
91
92 (define-description hello-world ()
93 ((identity :label nil)
94 (active-attributes :value '(identity))
95 (attribute-delimiter :value ", ")
96 (label-formatter :value (curry #'format nil "~A: "))
97 (value-formatter :value (curry #'format nil "~A")))
98 (:in-description one-line))}
99
100 Here we've defined a new description, ONE-LINE, and a context-sensitive extension to our HELLO-WORLD description. This partial desription will be active only when in the context of a one-line description. One can have attributes that only exist in certain description contexts, and attributes can have different properties.
101
102 \code{
103 (let ((message "Hello World!")
104 (description (find-description 'hello-world)))
105 (print :normal)(terpri)
106 (present message description)
107 (print :one-line)(terpri)
108 (with-active-descriptions (one-line)
109 (present message description)))
110 \outputs{:NORMAL
111 Lisp on Lines Demo
112 Message: Hello World!
113 Length: 12
114
115 :ONE-LINE
116 Hello World!}
117
118
119 By activating the description ONE-LINE, we've changed the context in which our object is displayed. We can create any number of descriptions and contexts and activate/deactivate them in any order.
120
121 Because all descriptions inherit from T, we can describe contexts for T and they will apply to every description. LoL includes a standard description, INLINE, which is almost exactly like our ONE-LINE definition above. It can be found in standard-descriptions/inline.lisp, and should look something like the following:
122
123 \code{;; Defined by LoL:
124 (define-description t ()
125 ((identity :label nil)
126 (active-attributes :value '(identity))
127 (attribute-delimiter :value ", ")
128 (label-formatter :value (curry #'format nil "~A: "))
129 (value-formatter :value (curry #'format nil "~A")))
130 (:in-description inline))}
131
132
133 The DISPLAY protocol using this internally to display attribute values. This allows infinitely deep nesting of descriptions and contexts, so that one can describe how one object looks when displayed within another, and it's turtles all the way down. We can demonstrate this by modifying our PRESENT function.
134
135 \code{
136 (defun present (object description)
137 (with-described-object (object description)
138 (dolist (a (attributes *description*))
139 (format t (concatenate
140 'string "~@[~A: ~]~A"
141 (attribute-value
142 (FIND-ATTRIBUTE *description* 'attribute-delimiter)))
143 (attribute-label a)
144 (let ((value (attribute-value a)))
145 ;; prevent circles
146 (if (eq object value)
147 value
148 (with-output-to-string (*standard-output*)
149 (with-active-descriptions (inline)
150 (present value (DESCRIPTION-OF value))))))))))
151
152 \outputs }
153
154 This is not perfect, but it serves to demonstrate the idea.
155
156
157
158
159
160
161
162
163
164
165
166
167
168