diff --git a/lispguide.xml b/lispguide.xml index 3d828a7..32ee868 100644 --- a/lispguide.xml +++ b/lispguide.xml @@ -2,9 +2,10 @@ +

-Revision 1.8 +Revision 1.10

@@ -12,9 +13,8 @@ Revision 1.8 Robert Brown -
-François-René Rideau + François-René Rideau
@@ -490,8 +490,7 @@ François-René Rideau before committing code.
  • - We should/will/must incorporate code coverage - into our testing process. + You should incorporate code coverage into your testing process. Tests are not sufficient if they do not cover all new and updated code; code that for whatever reason cannot be included in coverage results @@ -531,7 +530,7 @@ François-René Rideau

    If you're not sure, consult a dictionary, Google for alternative spellings, - or ask a local grammar nazi. + or ask a local expert.

    Here are examples of choosing the correct spelling: @@ -616,7 +615,7 @@ François-René Rideau

    - Indent your code the way GNU Emacs does. + Indent your code the way a properly configured GNU Emacs does.

    Indent carefully to make the code easier to understand. @@ -738,10 +737,10 @@ François-René Rideau (defconstant +golden-ratio64+ #xe08c1d668b756f82 "more digits of the golden ratio") (defmacro incf32 (x y) - "like incf, but for integers modulo 2**32" + "Like INCF, but for integers modulo 2**32" `(setf ,x (logand (+ ,x ,y) #xffffffff))) (defmacro incf64 (x y) - "like incf, but for integers modulo 2**64" + "Like INCF, but for integers modulo 2**64" `(setf ,x (logand (+ ,x ,y) #xffffffffffffffff)))

    @@ -1343,7 +1342,8 @@ François-René Rideau It is possible to fake global lexical variables with a differently named global variable and a DEFINE-SYMBOL-MACRO. - You should not use this trick. + You should not use this trick, + unless you first publish a library that abstracts it away.

    (defconstant +hash-results+ #xbd49d10d10cbee50) @@ -1358,26 +1358,31 @@ François-René Rideau

    - Name boolean-valued functions with a trailing + You should name boolean-valued functions with a trailing "P" or "-P", to indicate they are predicates. Generally, you should use "P" when the rest of the function name is one word and "-P" when it is more than one word.

    +

    + A rationale for this convention is given in + the CLtL2 chapter on predicates. +

    For uniformity, you should follow the convention above, and not one of the alternatives below.

    - Alternative rules used in some existing packages - is to always use "-P", - or to always use "?". + An alternative rule used in some existing packages + is to always use "-P". + Another alternative rule used in some existing packages + is to always use "?". When you develop such a package, you must be consistent with the rest of the package. When you start a new package, you should not use such an alternative rule - without a very good reason. + without a very good documented reason.

    @@ -1832,6 +1837,24 @@ François-René Rideau Sure, use it for "helper" functions, but not API functions.

    +

    + You should, however, use appropriate declarations + in internal low-level functions + where these declarations are used for optimization. + In addition to providing more speed in production, + declarations are more helpful than assertions + to find bugs at compile-time; + they can still help find dynamic errors + by setting optimization settings low while debugging. + You should not use such declarations + outside internal functions + where it is well-documented how unsafe it is + to use such functions with the wrong arguments; + and you must not call these functions in a way + that could possibly lead to their being passed wrong arguments. + Use check-type to sanitize any input being passed + to such function from uncontrolled sources. +

    @@ -1966,8 +1989,22 @@ François-René Rideau could have figure out all by itself, when the compiler isn't sufficiently-clever and the difference matters. - Consider using a DEFCONSTANT and its variants, - which would give the value a name explaining what it means. +

    +

    + Whenever you are going to use #., + you should consider using DEFCONSTANT and its variants, + possibly in an EVAL-WHEN, + to give the value a name explaining what it means. +

    +

    + You should use normal computations inside an EVAL-WHEN + rather than #. whenever they are enough for the job. +

    +

    + If you don't need the computation to happen at compile-time, + but only at some point before runtime, + you should use LOAD-TIME-VALUE + instead of read-time or compile-time computations.

    @@ -1990,12 +2027,12 @@ François-René Rideau

    It is usually an error to omit the :execute, - for it prevents LOADing the source rather than the fasl. + because it prevents LOADing the source rather than the fasl. It is usually an error to omit the :load-toplevel (except to modify e.g. readtables and compile-time settings), - for it prevents LOADing future files + because it prevents LOADing future files or interactively compiling code - that depend on the effects that happen at compile-time + that depends on the effects that happen at compile-time, unless the current file was COMPILE-FILEd within the same Lisp session.

    @@ -2038,8 +2075,8 @@ François-René Rideau any sort of method combination that might be in effect for the slot. Rare exceptions include INITIALIZE-INSTANCE and PRINT-OBJECT methods and - the initialization of Quake volatile slots - in INITIALIZE-RECORD methods. + accessing normally hidden slots in the low-level implementation of + methods that provide user-visible abstractions. Otherwise, you should use accessors, WITH-ACCESSORS

    @@ -2047,15 +2084,23 @@ François-René Rideau

    Accessor names generally follow a convention of <protocol-name>-<slot-name>, - where an "protocol" in this case loosely indicates - a set of functions with well-defined behavior; - a class can implement all or part of an interface - by defining some methods for (generic) functions in the protocol, - including readers and writers. - No implication of a formal "protocol" concept is intended. + where a "protocol" in this case loosely indicates + a set of functions with well-defined behavior.

    - For example, if there were a "notional" protocol called + No implication of a formal "protocol" concept is necessarily intended, + much less first-class "protocol" objects. + However, there may indeed be an abstract CLOS class + or an + Interface-Passing Style interface + that embodies the protocol. + Further (sub)classes or (sub)interfaces may then implement + all or part of a protocol by defining + some methods for (generic) functions in the protocol, + including readers and writers. +

    +

    + For example, if there were a notional protocol called is pnr with accessors pnr-segments and pnr-passengers, then the classes air-pnr, hotel-pnr and @@ -2085,7 +2130,7 @@ François-René Rideau some specific class(es).

    - You must not use generic functions where there is no "notional" protocol. + You must not use generic functions where there is no notional protocol. To put it more concretely, if you have more than one generic function that specializes its Nth argument, the specializing classes should all be descendants of a single class. @@ -2324,7 +2369,7 @@ François-René Rideau instead of using a combination of several list accessor functions. In this context, using CAR and CDR instead of FIRST and REST also makes sense. - However, mind in such cases that it might be more appropriate + However, keep in mind that it might be more appropriate in such cases to use higher-level constructs such as DESTRUCTURING-BIND or FARE-MATCHER:MATCH.

    @@ -2518,6 +2563,28 @@ François-René Rideau (define-constant +google-url+ "http://www.google.com/" :test #'string=) (define-constant +valid-colors+ '(red green blue)) +

    + Note that with optimizing implementations, such as SBCL or CMUCL, + defining constants this way precludes any later redefinition + short of UNINTERNing the symbol + and recompiling all its clients. + This may make it "interesting" to debug things at the REPL + or to deploy live code upgrades. + If there is a chance that your "constants" are not going to be constant + over the lifetime of your server processes + after taking into consideration scheduled and unscheduled code patches, + you should consider using + DEFPARAMETER or DEFVAR instead, + or possibly a variant of DEFINE-CONSTANT + that builds upon some future library implementing global lexicals + rather than DEFCONSTANT. + You may keep the +plus+ convention in these cases + to document the intent of the parameter as a constant. +

    +

    + Also note that LOAD-TIME-VALUE may help you + avoid the need for defined constants. +

    @@ -2720,9 +2787,10 @@ François-René Rideau

    You must use = to compare numbers, - unless it's really okay for 0, - 0.0 and -0.0 to compare unequal! - But then again, you must not usually use exact comparison + unless you really mean for 0, + 0.0 and -0.0 to compare unequal, + in which case you should use EQL. + Then again, you must not usually use exact comparison on floating point numbers.

    @@ -3036,7 +3104,7 @@ François-René Rideau

  • It is quite unlikely that the code will be changed - in ways that cause the declaration not to be true anymore. + in ways that cause the declaration to become false.
  • @@ -3120,7 +3188,7 @@ François-René Rideau is also O(n^2) unless you specify :FROM-END T. In such cases, you must use proper abstractions that cover those cases instead of calling REDUCE, - first defining them in a suitable library if needs be. + first defining them in a suitable library if need be.

    @@ -3198,7 +3266,7 @@ François-René Rideau

    -Revision 1.8 +Revision 1.10

    @@ -3206,9 +3274,8 @@ Revision 1.8 Robert Brown
    -
    -François-René Rideau + François-René Rideau