summaryrefslogtreecommitdiff
path: root/README.md
blob: d6010a2cf86fe466da97b6f1e12f09f971d399f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# `def`

A stupid project to dull the pain of using a few of CL's built-in `def*` forms.

## `def:var`

Isn't it annoying that, if you want to leave a `defvar`-defined
special variable uninitialized, then you're not allowed to give that
variable a docstring?

Sure, you can always 

    (setf (documentation *my-speical* 'variable) "oh good, docs")
    
but that's irritating too.

Enter `def:var`


    (def:var *oh-so-special* :doc "It's oh-so-special")
    
Of course, you may still initialize the variable:

    (def:var *oh-so-special* :init (random 10) :doc "It's oh-so-special")
    
## `def:const` 

Even more obnoxious is the behavior of `defconst` whenever you `C-c
C-c` in SLIME. You nearly always find yourself invoking a `CONTINUE`
restart when, according to common sense, you should not have had to.

Well no more! 

    (def:const +its-ten+ 10 "It's just the number 10")
    
If you re-evaluate that form, then nothing annoying happens. Of
course, the form still expands out to `defconstant` so that a
condition signals whenever you try to `setf` the name. 

## `def:class`

Admittedly, this one is less justfifed. There's already the extremely
elaborate `defclass/std` in the `defclass-std` system available
through quicklisp.  But I think `defclass/std` is a little *too*
thorough and ends up getting in its own way.

The case for using a macro like this is that, well, most classes are
defined in a very repetitive way.  This macro just saves you some
work:

    (def:class pt () 
      (x y z :prefix :type real :initform 0)
      (uuid :ro :type integer :initform (make-uuid) :documentation "A unique ID")
      :documentation "A point in real 3d space")
      
The above would expand out into

    (DEFCLASS PT NIL
      ((X :ACCESSOR PT-X :INITARG :X :TYPE REAL :INITFORM 0)
       (Y :ACCESSOR PT-Y :INITARG :Y :TYPE REAL :INITFORM 0)
       (Z :ACCESSOR PT-Z :INITARG :Z :TYPE REAL :INITFORM 0)
       (UUID :READER UUID :TYPE INTEGER :INITFORM (MAKE-UUID)
             :DOCUMENTATION "A unique ID"))
      (:DOCUMENTATION "A point in real 3d space"))
      

The syntax of the macro is:

    (def:class class-name (supers ...) body-and-slots...)
    
The `CLASS-NAME` and `SUPERS` follow `DEFCLASS` syntax.

Each member of `BODY-AND-SLOTS` is a slot definition list, a keyword
naming a class option, or a value bound to a class option. 

The class options are the same as those accepted by `DEFCLASS`, and
**Any class options must follow ALL slot definitions!**  E.g. in the
above `PT` example, the `:DOCUMENTATION` class option follows the two
slot definition lists.

Each slot definition is, in order:

- One or more slot name (with optional docstring)
- Zero or more slot flags
- Zero or more slot options

The following are all valid slot definitions:

;; define three slots
(x y z) 

;; define three read-only slots initialized to zero whose readers are
;; prefixed with the class name
(x y z :prefix :ro :initform 0)

;; define two string-valued required slots
((first-name "First name") 
 (last-name "Last name")
 :ro :required 
 :type string)

The following flags are accepted:

- `:prefix` prefix the accessor by the class name
- `:ro` only define a reader 
- `:wo` only define a writer 
- `:noarg` means no initarg
- `:required` the slot MUST have an initial value


## `def:typed` 

Common Lisp compilers like SBCL can produce extremely fast code
... if, that is, you help them out a little.  

Declaring types and `optimize` declare expressions can be a little
verbose and irritating to add to every function that you want to be
fast.  

`def:fast` lightens some of the typing load by embedding types inside
the lambda list, and by declaring the return type of functions.  E.g.:


    (defun list-of-fixnum-p (xs)
      (and (listp xs) (every #'sb-int:fixnump xs)))
    
    (deftype list-of-fixnum ()
      '(satisfies list-of-fixnum-p))
    
    (def:fast sum-fixnum ((x fixnum) (y fixnum) &rest (xs list-of-fixnum)) -> fixnum
      "Sums fixnums"
      (+ x y (reduce #'+ xs)))
    

The above expands into

    (DEFUN SUM-FIXNUM (X Y &REST XS)
      "Sums fixnums"
      (DECLARE (TYPE LIST-OF-FIXNUM XS)
               (TYPE FIXNUM Y X)
               (VALUES FIXNUM)
               (OPTIMIZE (SPEED 3) (SAFETY 0)))
      (+ x y (REDUCE #'+ XS)))