Fix missing `without-special-symbol-access' in `funcall-with-attribute-context'
[clinton/lisp-on-lines.git] / doc / manual.org
CommitLineData
fb50e621 1Lisp on Lines : The Missing Manual.
2
3/Abstract/: Lisp on Lines is a Common Lisp based framework for rapid
4development of data-driven applications. It is particularly useful
5for producing Web based applications, but is also useful elsewhere.
6
7
8* Introduction
9
10Lisp on Lines (LoL) is a framework for rapid development of data-driven
11applications, with a particular focus on web-based applications. It
12builds on the UncommonWeb engine and Contextl, and uses CLOS and the
13MOP extensively. Most of LoL can be used both at the REPL and through
14the browser, offering many options for development and testing.
15
16While the target audience for LoL is developers experienced with both
17web technologies and common lisp, a good programmer with a little
18experience in either should be able to pick things up fairly quickly.
19
20* Installation
21
22LoL has a load of dependencies, which themselves depend on others,
23etc. The best way to deal with this is to use [[http://common-lisp.net/project/clbuild/][clbuild]], a library
24management tool.
25
26If you'd prefer to manage your libraries manually, the dependencies,
27according to clbuild, are :
28
29alexandria arnesi bordeaux-threads cl-base64 cl-fad cl-mime cl-ppcre
30cl-qprint closer-mop contextl iterate lift local-time lw-compat
31net-telent-date parenscript parse-number portable-threads puri rfc2109
32slime split-sequence trivial-garbage ucw usocket yaclml
33
34All libraries should be installed from version control where available.
35
36* Describing the domain with the MAO protocol.
37
38 LoL uses a protocol it calls Meta-Attributed Objects, or MAO, as the
39 basis of its display mechanism. In MAO, we create context-aware
40 DESCRIPTIONs of objects, and those descriptions are used to generate
41 the display of the object itself. By having these external
42 descriptions change based on the context in which they are used, a few
43 generic components can come together to create complex interfaces.
44
45** Descriptions
46 Descriptions are a similar conceptually to classes. Every Lisp object
47 has one, and the root description that all descriptions inherit from
48 is known as T. FIND-DESCRIPTION is used to, well, find descriptions.
49
50#+BEGIN_SRC lisp
51 (find-description t)
52 => #<DESCRIPTION T {B7B9861}>
53#+END_SRC
54
55** Attributes and Properties
56 A description is a collection of ATTRIBUTEs, among other things. Each
57 attribute describes a part of an object, and any number of attributes
58 may or may not be active. The ATTRIBUTES function is used to find a
59 the list attributes that are both active and applicable in the current
60 context.
61
62#+BEGIN_SRC lisp
63(attributes (find-description t))
64=>(#<ATTRIBUTE IDENTITY {BBC9691}>
65 #<ATTRIBUTE TYPE {BBC96A1}>
66 #<ATTRIBUTE CLASS {BBC96B1}>)
67#+END_SRC
68
69The functions DESCRIPTION-ATTRIBUTES, DESCRIPTION-ACTIVE-ATTRIBUTES
70and DESCRIPTION-CURRENT-ATTRIBUTES return all the descriptions
71attributes, Attributes that are currently active regardless of
72context, and attributes that exist in the current context but may or
73may not be active, respectively.
74
75Attributes have properties, for example ATTRIBUTE-LABEL and
76ATTRIBUTE-VALUE. By simply iterating through the attributes of a
77described object, we can create a generic display for any lisp
78object. This is very similar, and was inspired by the technique
79outlined by Adrian Lienhard in [[http://www.adrian-lienhard.ch/files/mewa.pdf][MEWA: A Meta-level Architecture for
80Generic Web-Application Construction_]].
81
82
83For attribute properties to be useful, the description must be
84associated with the object it is meant to describe.
85
86The function FUNCALL-WITH-DESCRIBED-OBJECT takes care of setting up
87the proper context. There is some syntax for it in the form of
88WITH-DESCRIBED-OBJECT :
89
90#+BEGIN_SRC lisp
91
92(let ((description (find-description t))
93 (object "Hello World"))
94 (with-described-object (object description)
95 (dolist (a (attributes description))
96 (format t "~@[~A: ~]~A~%"
97 (attribute-label a)
98 (attribute-value a)))))
99=>
100Hello World
101Type: (SIMPLE-ARRAY CHARACTER (11))
102Class: #<BUILT-IN-CLASS SB-KERNEL::SIMPLE-CHARACTER-STRING>
103
104NIL
105#+END_SRC
106
107FUNCALL-WITH-DESCRIBED-OBJECT binds two specials, *DESCRIPTION* and
108*OBJECT*, to its arguments. Knowing this, we can shorten our code
109somewhat. Later on we'll be far away from the lexical bindings of
110description and object, so these special variables are essential.
111
112Another reason for the *description* variable is that
113WITH-DESCRIBED-OBJECT will use DESCRIPTION-OF to determine the
114description if the DESCRIPTION argument is NIL
115
116#+BEGIN_SRC lisp
117(with-described-object ("Hello World" nil)
118 (dolist (a (attributes *description*))
119 (format t "~@[~A: ~]~A~%"
120 (attribute-label a)
121 (attribute-value a))))
122
123Lets wrap that up in a function that we can re-use. LoL includes an
124entire DISPLAY mechanism that is slightly more involved, but this
125serves as an excellent example with not bogging us down in details.
126
127#+BEGIN_SRC lisp
128(defun present (object &optional description)
129 (with-described-object (object description)
130 (dolist (a (attributes *description*))
131 (format t "~@[~A: ~]~A~%"
132 (attribute-label a)
133 (attribute-value a)))))
134#+END_SRC
135
136** Contexts
137
138MAO adds to MEWA the concept of dynamic context. By changing the
139context in which an object is described, we combine and specialize the
140generic displays, ultimately creating different views of our
141objects. LoL uses ContextL extensively. Descriptions are contextl
142layers, and attributes themselves are layered classes. Most of the
143exported functions are layered methods, and the idea of dynamic
144context-sensitivity is used throughout LoL. If you're not familiar
145with contextl, don't worry, LoL mostly stands on its own. Still,
146reading through the material on contextl won't hurt.
147
148Descriptions may have different attributes dependant on what
149description contexts (or layers) are currently active. Attributes
150themselves might have different properties.
151
152When an object is being described (using WITH-DESCRIBED-OBJECT), it is
153also activated as a layer context. One can also activate/deactivate
154contexts manually, using WITH-ACTIVE-DESCRIPTIONS and
155WITH-INACTIVE-DESCRIPTIONS.
156
157Hopefully a little code will make this more clear :
158
159#+BEGIN_SRC lisp
160(present "Hello World")
161=>
162Hello World
163Type: (SIMPLE-ARRAY CHARACTER (11))
164Class: #<BUILT-IN-CLASS SB-KERNEL::SIMPLE-CHARACTER-STRING>
165Simple character string
166
167;; Now we'll activate a built-in description, INLINE.
168
169(with-active-descriptions (inline)
170 (present "Hello World"))
171=>
172Hello World
173#+END_SRC
174
175You can see that the behavior of PRESENT changed when the INLINE
176context was activated. This is the key innovation that makes LoL so
177useful. In the next chapter we'll create our own descriptions and
178demonstrate this further.
179
180* Defining and Using Descriptions
181
182** Defining a simple description
183The basics of the MAO should now (hopefully) be clear, so lets start
184using it. First, we'll create our very own description.
185
186#+BEGIN_SRC lisp
187(define-description hello-world ()
188 ((title :value "Lisp on Lines Demo")
189 (identity :label "Message")
190 (length :label "Length" :function #'length)
191 (active-attributes :value '(title identity length))))
192#+END_SRC
193
194Descriptions are defined very much like CLOS classes, and are in fact
195implemented that way, inheritance rules apply. The object returned
196from FIND-DESCRIPTION is best described as a prototype-based
197singleton. In other words, there is only one instance, and it inherits
198attributes and properties from further up its hierarchy unless
199specifically overridden.
200
201Attributes can have any number of properties, (see the class
202STANDARD-ATTRIBUTE), but the three most important are accessed via the
203methods ATTRIBUTE-LABEL, ATTRIBUTE-VALUE and ATTRIBUTE-FUNCTION,and
204named (in DEFINE-DESCRIPTION forms and elsewhere)
205by the :label, :value, and :function keywords.
206
207ATTRIBUTE-LABEL is simply a textual label that describes the
208attribute. ATTRIBUTE-VALUE is defined to return the result of calling
209ATTRIBUTE-FUNCTION with the object as its argument. If
210ATTRIBUTE-FUNCTION is NIL, the value of the :value property is returned
211directly.
212
213In the example above, the IDENTITY and ACTIVE-ATTRIBUTES attributes
214are inherited from T, and we are simply overriding the default
215properties for our description. LENGTH and TITLE are specific to this
216description. A look at src/standard-descriptions/t.lisp may be
217instructive at this point.
218
219Now, we can present our object using our new description.
220
221#+BEGIN_SRC lisp
222(present "Hello World" (find-description 'hello-world))
223=>
224Lisp on Lines Demo
225Message: Hello World
226Length: 11
227
228NIL
229#+END_SRC
230
231** Using descriptions as and with contexts.
232
233A we mentioned earlier, when an object is being described, the
234'description context' is also made active. On top of that, one can
235define partial descriptions that are only active when other
236description contexts have been activated.
237
238We'll make a ONE-LINE description similar to the INLINE description
239demonstrated earlier.
240
241#+BEGIN_SRC lisp
242(define-description one-line ())
243
244(define-description hello-world ()
245 ((identity :label nil)
246 (active-attributes :value '(identity)))
247 (:in-description one-line))
248
249#+END_SRC
250
251Here we've defined a new description, ONE-LINE, and a
252context-sensitive extension to our HELLO-WORLD description. This
253partial desription will be active only when in the context of a
254one-line description. One can have attributes that only exist in
255certain description contexts, and attributes can have different
256properties.
257
258#+BEGIN_SRC lisp
259(let ((message "Hello World!")
260 (description (find-description 'hello-world)))
261 (print :normal)(terpri)
262 (present message description)
263 (print :one-line)(terpri)
264 (with-active-descriptions (one-line)
265 (present message description)))
266=>
267:NORMAL
268Lisp on Lines Demo
269Message: Hello World!
270Length: 12
271
272:ONE-LINE
273Hello World!
274
275NIL
276#+END_SRC
277
278By activating the description ONE-LINE, we've changed the context in
279which our object is displayed. We can create any number of
280descriptions and contexts and activate/deactivate them in any order.
281
282Descriptions are implemented as ContextL 'layers', so if all
283this seems weird, reading the ContextL papers might help.
284
285** T : The root of all descriptions.
286
287Because all descriptions inherit from T, we can define contexts for T
288and they will apply to every description. The INLINE description can
289be found in standard-descriptions/inline.lisp, where we define
290a desription for T in the context of the INLINE description :
291
292#+BEGIN_SRC lisp
293;; Defined by LoL in inline.lisp :
294(define-description t ()
295 ((identity :label nil)
296 (active-attributes :value '(identity))
297 (attribute-delimiter :value ", ")
298 (label-formatter :value (curry #'format nil "~A: "))
299 (value-formatter :value (curry #'format nil "~A")))
300 (:in-description inline))}
301
302#+END_SRC
303
304The does for the LoL DISPLAY mechanism what ONE-LINE did for PRESENT,
305only with more magic. By exetending T in this way, it's easy to create
306contexts the redefine the behavior of LoL while still reusing the basics.
307
308** DESCRIPTION-OF : Permanently Associate a description with a class.
309
310The LAYERED-FUNCTION DESCRIPTION-OF will return the description
311associated with an object.
312
313#+BEGIN_SRC lisp
314
315(description-of nil)
316=>
317#<DESCRIPTION NULL {AA04F49}>
318
319(description-of t)
320=>
321#<DESCRIPTION SYMBOL {AA04541}>
322
323(description-of '(1 2 3))
324=>
325#<DESCRIPTION CONS {AA04C29}>
326
327;;etc
328
329#+END_SRC
330
331* The DISPLAY Protocol
332
333 Our function, PRESENT, is very basic, though pretty powerful when
334 combined with descriptions and contexts. LoL includes a superset of
335 such functionality built-in.
336
337 The main entry point into this protocol is the DISPLAY
338 function. The signature for this functions is :
339
340#+BEGIN_SRC lisp
341(display DISPLAY OBJECT &REST ARGS &KEY DEACTIVATE ACTIVATE &ALLOW-OTHER-KEYS)
342#+END_SRC
343
344 The first argument, DISPLAY, is the place where we will display
345 to/on/in/with. It could be a stream, a UCW component, a CLIM gadget,
346 or anything else you might want to use.
347
348 One can specialize on this argument (though it's better to specialize
349 DISPLAY-USING-DESCRIPTION... more on that later) to use generic
350 descriptions to display objects in different environments.
351
352 The second argument is simply the object to be displayed. Here's a
353 simple example :
354
355#+BEGIN_SRC lisp
356(display t t)
357=>
358T
359Type:BOOLEAN
360Class:#<BUILT-IN-CLASS SYMBOL>
361Symbol
362Name:T
363Value:T
364Package:#<PACKAGE "COMMON-LISP">
365Function:<UNBOUND>
366; No value
367#+END_SRC
368
369 The two arguments specified in the lambda-list, ACTIVATE and
370 DEACTIVATE, are used to activate and deactivate description contexts in
371 the scope of the display function.
372
373#+BEGIN_SRC lisp
374
375(display nil t :activate '(inline))
376=>
377"t"
378(with-active-descriptions (inline)
379 (display nil t :deactivate '(inline)))
380=>
381"T
382Type:BOOLEAN
383Class:#<BUILT-IN-CLASS SYMBOL>
384Symbol
385Name:T
386Value:T
387Package:#<PACKAGE \"COMMON-LISP\">
388Function:<UNBOUND>"
389
390#+END_SRC
391
392Any other keyword arguments passed will be used to set the value of an
393attribute with a :keyword property, in the dynamic context of the
394DISPLAY function call. Once such attribute, and a very useful one is
395ACTIVE-ATTRIBUTES with its :attributes keyword :
396
397#+BEGIN_SRC lisp
398
399(display t t :attributes '(class package))
400=>
401Class:#<BUILT-IN-CLASS SYMBOL>
402Package:#<PACKAGE "COMMON-LISP">
403
404#+END_SRC
405
406The properties of attributes that do not have a :keyword property can
407also be set dynamically. Since :attributes is the :keyword property of
408the ACTIVE-ATTRIBUTES attribute, the following form is equivalent to
409the previous :
410
411#+BEGIN_SRC lisp
412(display t t :attributes '((active-attributes
413 :value (class package))))
414=>
415Class:#<BUILT-IN-CLASS SYMBOL>
416Package:#<PACKAGE "COMMON-LISP">
417#+END_SRC
418
419Setting the attributes this way is almost like creating an anonymous
420description context... you can express just about anything you would
421in a DEFINE-DESCRIPTION. Here's a more involved example :
422
423#+BEGIN_SRC lisp
424(display t t :attributes `((identity :label "The Object")
425 (class :label "CLOS Class")
426 (package :value "COMMON LISP" :function nil)
427 (type :value-formatter
428 ,(lambda (a)
429 (format nil "Got a value? ~A" a)))))
430=>
431
432The Object:T
433CLOS Class:#<BUILT-IN-CLASS SYMBOL>
434Package:COMMON LISP
435Type:Got a value? BOOLEAN
436
437#+END_SRC
438
439I hope that serves well to demonstrate the concepts behind LoL, as
440there is no API documentation available at the moment... use the
441source luke!
442
443
444* Automatic Descriptions for CLOS classes.
445
446 Lisp-on-Lines includes a compose-able metaclass, DESCRIBED-CLASS. It
447 can be combined with _any_ other metaclass without affecting the
448 behavior of that class. DESCRIBED-CLASS has been used with the
449 metaclasses provided by CLSQL, ROFL, Rucksack and UCW simply by
450 defining a class that inherits from both metaclasses.
451
452 DESCRIBED-CLASS creates a base description for the class, named
453 DESCRIPTION-FOR-<class>, and another description with the same name
454 as the class that has the previous description as a superclass. The
455 then defines a method on DESCRIPTION-OF that returns the second
456 description.
457
458 LoL includes DESCRIBED-STANDARD-CLASS, which is subclass of
459 STANDARD-CLASS and DESCRIBED-CLASS. We'll use this to create a class
460 and its description.
461
462#+BEGIN_SRC lisp
463
464(defclass person ()
465 (first-name last-name company-name
466 date-of-birth phone fax email
467 address city province postal-code)
468 (:metaclass described-standard-class))
469=>
470#<DESCRIBED-STANDARD-CLASS PERSON>
471
472(display t (make-instance 'person))
473=>
474First name:#<UNBOUND>
475Last name:#<UNBOUND>
476Company name:#<UNBOUND>
477Date of birth:#<UNBOUND>
478Phone:#<UNBOUND>
479Fax:#<UNBOUND>
480Email:#<UNBOUND>
481Address:#<UNBOUND>
482City:#<UNBOUND>
483Province:#<UNBOUND>
484Postal code:#<UNBOUND>
485
486#+END_SRC
487
488** Described CLOS objects an the EDITABLE description
489
490 The slots of an object are SETF'able places, and LoL takes
491 advantage of that to provide EDITABLE descriptions
492 automatically. When the EDITABLE description is active, and editor
493 will be presented. The REPL based editor is pretty basic, but still
494 useful. The HTML based editor will be described later.
495
496
497#+BEGIN_SRC lisp
498(defun edit-object (object &rest args)
499 (with-active-descriptions (editable)
500 (apply #'display t object args)))
501
502(let ((object (make-instance 'person)))
503 (edit-object object)
504 (terpri)
505 (display t object))
506
507;; What follows are prompts and the information i entered
508
509First name:Drew
510
511Last name:Crampsie
512
513Company name:The Tech Co-op
514
515Date of birth:1978-07-31
516
517Phone:555-5555
518
519Fax:555-5555
520
521Email:drewc@tech.coop
522
523Address:s/v Kanu, Lower Fraser River
524
525City:Richmond
526
527Province:BC
528
529Postal code:V1V3T6
530
531;; And this is what was displayed.
532
533First name:Drew
534Last name:Crampsie
535Company name:The Tech Co-op
536Date of birth:1978-07-31
537Phone:555-5555
538Fax:555-5555
539Email:drewc@tech.coop
540Address:s/v Kanu, Lower Fraser River
541City:Richmond
542Province:BC
543Postal code:V1V3T6
544#+END_SRC
545
546** Extending the generated description
547
548We mentioned earlier that DESCRIBED-CLASS creates two descriptions :
549
550#+BEGIN_SRC lisp
551
552(find-description 'description-for-person)
553=>
554#<DESCRIPTION DESCRIPTION-FOR-PERSON {D296DE1}>
555
556(find-description 'person)
557=>
558#<DESCRIPTION PERSON {ADFEDB1}>
559
560(description-of (make-instance 'person))
561=>
562#<DESCRIPTION PERSON {ADFEDB1}>
563
564#+END_SRC
565
566
567The reason for this is so we can redefine the description PERSON while
568keeping all the generated information from DESCRIPTION-FOR-PERSON.
569
570In this case, we will add an attribute, PERSON-AGE, that calculates
571a persons age based on the data in the date-of-birth slot.
572
573
574
575
576
577
578
579
580
581
582
583
584
585* Using Lisp-on-Lines for the Web.
586
587 LoL was developed, and is primarily used, for implementing
588 data-driven web applications. As such, it comes with a host of
589 features for doing just that.
590
591 LoL, by default, implements its web portion on top of the wonderful
592 UnCommon Web meta-framework. The LISP-ON-LINES-UCW ASDF system
593 should be loaded, as it provides the features we're going to
594 discuss.
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609