| 1 | Notes on Lisp style |
| 2 | |
| 3 | * Language subset and extensions |
| 4 | |
| 5 | None of ANSI Common Lisp is off-limits. |
| 6 | |
| 7 | I think my Lisp style is rather more imperative in flavour than most |
| 8 | modern Lisp programmers. It's probably closer to historical Lisp |
| 9 | practice in that regard, even though I wasn't writing Lisp back then. |
| 10 | |
| 11 | I make extensive use of CLOS, and macros. On a couple of occasions I've |
| 12 | made macros which use CLOS generic function dispatch to compute their |
| 13 | expansions. The parser language is probably the best example of this in |
| 14 | the codebase. |
| 15 | |
| 16 | I like hairy ~format~ strings. |
| 17 | |
| 18 | I've avoided hairy ~loop~ for the most part, not because I dislike it |
| 19 | strongly but because others do and I don't find that it wins big enough |
| 20 | for the fight to be worthwhile. |
| 21 | |
| 22 | I only use ~&aux~ lambda-list parameters in ~defstruct~ BOA |
| 23 | constructors, for special effects. |
| 24 | |
| 25 | I use ~car~, not ~first~, and ~cdr~, not ~rest~. Similarly, I use |
| 26 | ~cadr~, not ~second~, and I'm not afraid to use ~cddr~ or ~cadar~. |
| 27 | |
| 28 | Similarly, I've not used ~elt~, preferring to know what kind of sequence |
| 29 | I'm dealing with, or using the built-in sequence functions. |
| 30 | |
| 31 | I'm happy to use ~1+~, and I like the brevity of ~1-~ enough to use it |
| 32 | despite its terrible name. |
| 33 | |
| 34 | There are no reader syntax extensions in the code. This is because I |
| 35 | couldn't think of any way they'd be especially helpful, and not because |
| 36 | I'm in any way opposed to them. |
| 37 | |
| 38 | The main translator, in the ~SOD~ package, tries to assume very little |
| 39 | beyond ANSI Common Lisp and what's included in just about every serious |
| 40 | implementation: specifically, MOP introspection, and Gray streams. |
| 41 | There's intentionally no MOP intercession. |
| 42 | |
| 43 | The frontend additionally makes use of ~cl-launch~, but the dependency |
| 44 | is actually quite weak, and it could be replaced with a different, maybe |
| 45 | implementation-specific, mechanism fairly easily. I'm keen to take |
| 46 | patches which improve frontend portability. |
| 47 | |
| 48 | I'm more tolerant of extensions and external dependencies in the test |
| 49 | suite, which makes additional use of ~xlunit~. Running the test suite |
| 50 | isn't essential to getting the translator built, so this isn't as much |
| 51 | of a problem. |
| 52 | |
| 53 | |
| 54 | * Layout |
| 55 | |
| 56 | I pretty much let Emacs indent my code for me, based on information |
| 57 | collected by SLIME. Some exceptions: |
| 58 | |
| 59 | + DSLs (e.g., the parser language) have their own space of macros |
| 60 | which Emacs doesn't understand and for the most part I haven't |
| 61 | bothered to teach it. |
| 62 | |
| 63 | + Emacs sometimes does a bad job with hairy ~loop~ and requires manual |
| 64 | fixing. Since I don't use hairy ~loop~ much, this isn't a major |
| 65 | problem. |
| 66 | |
| 67 | Lines are 77 characters at most, except for strange special effects. |
| 68 | Don't ask. This is not negotiable, though. Don't try to tell me that |
| 69 | your monitor is very wide so you can read longer lines. My monitor is |
| 70 | likely at least as wide. On the other hand, most lines are easily short |
| 71 | enough to fit in my narrow columns, so the right hand side of a wide |
| 72 | window would be mostly blank. This seems wasteful to me, when I could |
| 73 | fill that space with more code. |
| 74 | |
| 75 | Lisp code does have a tendency to march across to the right quite |
| 76 | rapidly given a chance. I have a number of strategies for dealing with |
| 77 | this. |
| 78 | |
| 79 | + Break a long nested calculation into pieces, giving names to the |
| 80 | intermediate results, in a ~let*~ form. |
| 81 | |
| 82 | + Hoist deeply nested complex computations out into ~flet~ or |
| 83 | ~labels~, and then invoke them from inside whatever complicated |
| 84 | conditional mess was needed to decide what to do. |
| 85 | |
| 86 | + Shrug my shoulders and let code dribble down the right hand side for |
| 87 | a bit. |
| 88 | |
| 89 | |
| 90 | * Packages and exporting |
| 91 | |
| 92 | A package collects symbols which are given meanings in one or more |
| 93 | source files. If a package's code is all in one file, then the package |
| 94 | definition can be put in that file too; otherwise I put it in its own |
| 95 | file. |
| 96 | |
| 97 | I don't put ~:export~ in package definitions. Instead, I scatter calls |
| 98 | to the ~export~ function throughout the code, right next to where the |
| 99 | relevant symbol is defined. This has three important advantages. |
| 100 | |
| 101 | + You can tell, when you're reading the code which defines ~foo~, |
| 102 | whether ~foo~ is exported and therefore a defined part of the |
| 103 | package interface. |
| 104 | |
| 105 | + When you know that you're writing a thing which will form part of |
| 106 | the package interface, you don't have to go off and edit some other |
| 107 | file to export it. |
| 108 | |
| 109 | + A master list of exported symbols becomes a merge hazard: if two |
| 110 | different branches add symbols to nearby pieces of the master list |
| 111 | then you get a merge conflict for no especially good reason. |
| 112 | |
| 113 | There's an apparent disadvantage: there's no immediately visible master |
| 114 | list of exported symbols. But that's not a big problem: |
| 115 | |
| 116 | : (loop for s being the external-symbols of pkg collect s) |
| 117 | |
| 118 | See ~doc/list-symbols.lisp~ for more sophisticated reporting. (In |
| 119 | particular, this identifies what kind of thing(s) each external symbol |
| 120 | names.) |
| 121 | |
| 122 | |
| 123 | * Comments and file structuring |
| 124 | |
| 125 | A file starts with a big ~;;;~ comment bearing the Emacs ~-*-lisp-*-~ |
| 126 | marker, a quick description, and copyright and licensing boilerplate. I |
| 127 | don't use four-semicolon comments, and I only use ~#|~ ... ~|#~ for |
| 128 | special effects. |
| 129 | |
| 130 | Then there's package stuff. There may be a ~cl:defpackage~ form (with |
| 131 | explicit package qualifier) if the relevant package doesn't have its own |
| 132 | package definition file. |
| 133 | |
| 134 | Then there's ~cl:in-package~. Like ~defpackage~, I use a gensym to name |
| 135 | the package. I can't think offhand of a good reason to have a file with |
| 136 | sections `in' more than one package. So, the ~in-package~ form goes at |
| 137 | the top of the file, before the first section header. If sections are |
| 138 | going to end up in separate packages, I think I'd put a ~cl:in-package~ |
| 139 | at the top of each section in case I wanted to reorder them. |
| 140 | |
| 141 | The rest of the file consists of Lisp code. I don't use page boundaries |
| 142 | ~^L~ to split files up. Instead, I use big banner comments for this: |
| 143 | |
| 144 | : ;;;-------------------------------------------------------------------------- |
| 145 | : ;;; Section title. |
| 146 | |
| 147 | Sections don't usually have internal comments, but if they did they'd |
| 148 | also be ~;;;~ comments. |
| 149 | |
| 150 | Almost all definitions get documentation strings. I've tried to be |
| 151 | consistent about formatting. |
| 152 | |
| 153 | + Docstring lines are 77 characters or less. |
| 154 | |
| 155 | + The first line gives a summary of what the thing does. The summary, |
| 156 | together with the SLIME-generated synopsis, is likely enough to |
| 157 | remind you what the thing does. |
| 158 | |
| 159 | + The rest of the lines are indented by three spaces, and explain |
| 160 | carefully what the thing does and what all the parameters mean. |
| 161 | |
| 162 | Smallish functions and macros don't usually need any further |
| 163 | commentary. Big functions often need to be split into bitesize pieces |
| 164 | with their own internal ~;;~ comments. The idea is that these comments |
| 165 | should explain the code's overall strategy to the reader, and help them |
| 166 | figure out how a piece fits into that strategy. |
| 167 | |
| 168 | Winged, single ~;~ comments are very rare. |
| 169 | |
| 170 | Files end, as a result of long tradition, with a comment |
| 171 | |
| 172 | : ;;;----- That's all, folks -------------------------------------------------- |
| 173 | |
| 174 | |
| 175 | * Macro style |
| 176 | |
| 177 | I don't mind complicated macros if they're doing something worthwhile. |
| 178 | They need to have good documentation strings, though. |
| 179 | |
| 180 | That said, where possible I've tried to factor macros into an actual |
| 181 | macro providing the syntactic sugar, and a function which receives the |
| 182 | parameters and $\eta$-expanded forms, and does the actual work. |
| 183 | |
| 184 | It's extremely bad taste for a macro to evaluate its evaluable |
| 185 | parameters in any order other than strictly left to right, or to |
| 186 | evaluate them more than once. |
| 187 | |
| 188 | |
| 189 | * Data structures |
| 190 | |
| 191 | I've tended to be happy with plain lists for homogeneous-ish |
| 192 | collections. Strongly heterogeneous collections (other than input |
| 193 | syntax, destructured using ~defmacro~ or ~destructuring-bind~) I've |
| 194 | tended to make a proper data type for. |
| 195 | |
| 196 | My first instinct when defining a new structure is to use ~defclass~. |
| 197 | While it's annoyingly verbose, it has the immense benefit over |
| 198 | ~defstruct~ that it's safe to redefine CLOS classes in a running image |
| 199 | without the world breaking, and I usually find it necessary to add or |
| 200 | change slots while I'm working on new code. Once a piece of code has |
| 201 | settled down and I have a good feel for what my structure is actually |
| 202 | doing, I might switch the ~defclass~ for a ~defstruct~. Several |
| 203 | questions influence my decision. |
| 204 | |
| 205 | + Do slot accesses need to be really fast? My usual Lisp |
| 206 | implementations aggressively optimize ~defstruct~ accessor |
| 207 | functions. |
| 208 | |
| 209 | + Have I subclassed my class? While I can move over a |
| 210 | single-inheritance tree using ~:include~, it seems wrong to do this |
| 211 | most of the time. Also, I'd be precluding subclasses from multiple |
| 212 | inheritance, and I'd either have to prohibit subclassing by |
| 213 | extensions or have to commit to ~defstruct~ in the documentation. |
| 214 | In general, I'm much happier committing to ~defclass~. |
| 215 | |
| 216 | + Are there methods specialized on my class? Again, structure classes |
| 217 | make fine method specializers, but it doesn't seem right. |
| 218 | |
| 219 | Apart from being hard to redefine, ~defstruct~ does a pretty good job of |
| 220 | making a new structure type. I tend to tidy up a few rough edges. |
| 221 | |
| 222 | + The default predicate always has ~-p~ appended. If the class name |
| 223 | is a single word, then I'll explicitly name the predicate with a |
| 224 | simple ~p~ suffix. For example, ~ship~ would have the predicate |
| 225 | ~shipp~, rather than ~ship-p~. |
| 226 | |
| 227 | + If there are slots I can't default then I'll usually provide a BOA |
| 228 | constructor which sets them from required parameters; other slots |
| 229 | I'll set from optional or keyword parameters according to my taste |
| 230 | and judgement. |
| 231 | |
| 232 | + Slots mustn't be given names which are external in any package. |
| 233 | Unfortunately, slot names are used in constructing accessor names, |
| 234 | and sometimes the right accessor name involves a prohibited symbol. |
| 235 | I've mostly addressed this by naming the slot ~%foo~, and then |
| 236 | providing inline reader and writer functions. (CLOS class |
| 237 | definitions don't have this problem because you get to set the |
| 238 | accessor function names independently of the slot names.) |
| 239 | |
| 240 | + BOA constructors are strange. You can set the initial slots based |
| 241 | on an arbitrary computation on the provided parameters, but you have |
| 242 | to roll up your sleeves and mess with ~&aux~ parameters to pull it |
| 243 | off. |
| 244 | |
| 245 | |
| 246 | * Naming |
| 247 | |
| 248 | I'm a traditionalist in some ways, and one of the reasons I like Lisp is |
| 249 | the richness of its history and tradition. |
| 250 | |
| 251 | In other languages, I tend to use single- or two-letter names for |
| 252 | variables and structure slots; not so much in Lisp. Other languages |
| 253 | express more using punctuation, so the names stand out easily; I find |
| 254 | that short names can be lost more easily in Lisp. |
| 255 | |
| 256 | I've also tended to go for fairly prosaic names, taking my inspiration |
| 257 | from the CLOS MOP. While I mourn the loss of whimsical names like |
| 258 | ~haulong~ and ~haipart~, I've tried to avoid inventing more of them. |
| 259 | |
| 260 | There's a convention, which I think comes from ML, of using ~_~ in a |
| 261 | where a binding occurrence of a variable name is expected, to signify |
| 262 | that that the corresponding value is to be discarded. Common Lisp, |
| 263 | alas, doesn't have such a convention. Instead, there's a sequence of |
| 264 | silly names used with the same intention, and the bindings are then |
| 265 | explicitly ignored with a declaration. The names begin ~hunoz~, |
| 266 | ~hukairz~, and (I think) ~huaskt~. |
| 267 | |
| 268 | |
| 269 | * Declarations |
| 270 | |
| 271 | The code is light on declarations, other than ~ignore~ and similar used |
| 272 | to muffle warnings. The macros try to do sensible things with |
| 273 | declarations, and I think they succeed fairly well, but there might be |
| 274 | bugs and rough edges. I know that some are just broken because, for |
| 275 | actual correctness, declarations provided by the caller need to be split |
| 276 | up into a number of different parts of the expansion, which in turn |
| 277 | requires figuring out what the declarations mean and which bindings |
| 278 | they're referring to. That's not completely impossible, assuming that |
| 279 | there aren't implementation-specific declarations which crazy syntax |
| 280 | mixed in there, but it's more work than seems worthwhile. |
| 281 | |
| 282 | |
| 283 | * COMMENT Emacs cruft |
| 284 | |
| 285 | #+LATEX_CLASS: strayman |
| 286 | |
| 287 | ## LocalWords: CLOS ish destructure destructured accessor specializers |
| 288 | ## LocalWords: accessors DSLs gensym |
| 289 | |
| 290 | ## Local variables: |
| 291 | ## mode: org |
| 292 | ## End: |