Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!genrad!panda!husc6!harvard!seismo!utah-cs!shebs From: shebs@utah-cs.UUCP (Stanley Shebs) Newsgroups: net.ai,net.lang.lisp Subject: Re: Common LISP style standards. Message-ID: <3787@utah-cs.UUCP> Date: Sat, 10-May-86 13:31:52 EDT Article-I.D.: utah-cs.3787 Posted: Sat May 10 13:31:52 1986 Date-Received: Tue, 13-May-86 02:58:53 EDT References: <2784@jhunix.UUCP> Reply-To: shebs@utah-cs.UUCP (Stanley Shebs) Distribution: net Organization: University of Utah CS Dept Lines: 114 Xref: watmath net.ai:3491 net.lang.lisp:826 In article <2784@jhunix.UUCP> ins_amrh@jhunix.UUCP (Martin R. Hall) writes: > > We are doing everything in Common LISP, but are looking for >standards in regards to coding *style*. The "correct" style depends on whether your hackers are from CMU, MIT, Stanford, Utah, University of Southern ND at Hoople, ... :-) The following remarks are based on 4 years with Franz, PSL, and Common Lisp, reading as well as writing, but are nevertheless highly prejudiced. > - How do you keep track of the side effects of destructive functions > such as sort, nconc, replaca, mapcan, delete-if, etc? There are very few circumstances when it is appropriate to use destructive functions. There are two classes of exceptions: for efficiency, or the algorithm depends on it. In the first case, you only use the destructive operations on newly consed cells, and NEVER on things passed in as function arguments. In the second case, you have lots of discussion about why the algorithm needs destructive ops and how it uses them ("since our image is 1000x1000, we replace the pixels to avoid consing another image..."). > - When should you use macros vs. functions? Use macros only if you need new syntax, for instance a defining form that your program uses a lot. In a game I wrote a while ago, there was a macro called def-thing which took a bunch of numbers and symbols. If it had been a function, the "'" key would have been worn out... Sometimes macros are useful to represent a commonly appearing bit of code that you don't want to call as a function. But this usually loses on space what it gains in speed. > - How do you reference global variables? Usually you enclose it > in "*"s, but how do you differentiate between your own vars and > Common LISP vars such as *standard-input*, *print-level*, etc? Use "*"s, no differentiation. > - Documentation ideas? File headers are good, especially for programs that wander to different operating systems. The commenting style in the Common Lisp book is good. Documentation strings don't seem like a big win, but they probably make more sense in very elaborate programming environments. I always put doc strings on defvars. > - When to use DOLIST vs MAPCAR? Mapcar returns something, dolist doesn't. To return a list, mapcar must cons a lot, and dolist doesn't cons at all. Consing is bad. :-) > - DO vs LOOP? Whatever turns you on. > - Indentation/format ideas? Or do you always write it like the > pretty-printer would print it? I always write like the editor formats it. This can create problems if two people are using different editors or different customizations of the editor. What you see in the Common Lisp book is a good place to start for getting your editor to indent properly. Personally, I find it most readable to have a block of comments in front of the function, then a blank line, then the function. I also prefer to minimize the number of comments scattered about in the function body. Frequently the structure of the function tells a lot, but is obscured by comments inserted randomly. Consider, too, that a 1-page function + 2 pages of comments = 3 pages of function, which is *really* hard to read! > - NULL vs ENDP, FIRST vs CAR, etc. Some would say "FIRST" is > more mnemonic, but does that mean you need to use > (first (rest (first X))) instead of (cadar X) ?? Null vs endp is pretty clearcut, since endp may error out, where null would just return nil. No more than 1% of all Lisp programs will behave predictably if they get a dotted list instead of a normal one, but nobody seems to care... On first vs car, everybody has their favorites. I prefer c...r combos, but others hate it when I use cadr instead of second. Fortunately, such circumstances are rare. If you feel the urge to put together a data structure that has more than 2 pieces, use a defstruct. Your code will be more readable *and* more efficient (since implementors can put in all sorts of performance hacks for structures). If I were a manager, I would fire anybody who used anything but car, cdr, and cadr (and they wouldn't be saved by doing (car (car (cdr (cdr X)))) either!) > - etc, etc. Avoid cond if you only have one test, use "if" instead. Saves two pairs of parens and a "T"... (i.e. it's easier to read). Short functions are better than long ones. In any competent Lisp implementation, the cost of a function call is quite low, and shouldn't be considered. I've only written a handful of functions longer than 20 lines... Sequence functions and mapping functions are generally preferable to handwritten loops, since the Lisp wizards will probably have spent a lot of time making them both efficient and correct (watch out though; quality varies from implementation to implementation). More generally, Lisp programs usually benefit from the encouragement of a "functional programming" style, where functions do few side-effects that extend beyond the function's body. Easier to read, easier to debug, easier to maintain. Standard dictums of programming practice still apply in Lisp, i.e. always put in a default case on any mult-way conditional - the constructs ecase and ccase are useful in this respect. There are lots of others I don't remember at the moment... somebody should write a book that concentrates on Lisp programming instead of laundry listifying 400 functions... > -Marty Hall stan shebs