diff --git a/lispguide.xml b/lispguide.xml index 32ee868..5b4de9c 100644 --- a/lispguide.xml +++ b/lispguide.xml @@ -5,14 +5,13 @@
-Revision 1.10 +Revision 1.11
Robert Brown - François-René Rideau @@ -21,6 +20,11 @@ Robert Brown In memoriam Dan Weinreb ++Patterns mean "I have run out of language." — Rich Hickey +
+ ++ Some of these guidelines are motivated by universal principles of good programming. + Some guidelines are motivated by technical peculiarities of Common Lisp. + Some guidelines where once motivated by a technical reason, + but the guideline remained after the reason subsided. + Some guidelines, such those about as comments and indentation, + are based purely on convention, rather than on clear technical merit. + Whatever the case may be, you must still follow these guidelines, + as well as other conventional guidelines + that have not been formalized in this document. + You MUST follow conventions, because they are important for readability. +
++ Conventional guidelines are indoctrination. + Their purpose is to make you follow the mores of the community, + + so you can more effectively cooperate with existing members. + It is still useful to distinguish the parts that are technically motivated + from the parts that are mere conventions, + so you know when best to defy conventions for good effect, + and when not to fall into the pits that the conventions are there to help avoid. +
+@@ -603,13 +628,10 @@ Robert Brown
Some line length restriction is better than none at all. - Google Java developers have adopted - a 100-column limitation on source code lines - and C++ developers limit themselves to 80 columns. - Common Lispers at ITA have long adopted the 100-column limit. Allowing 100 columns seems better, since good style encourages the use of descriptive variables and function names.
+@@ -1032,7 +1058,7 @@ Robert Brown
---
+ You must follow the convention of using ---
for comments requiring special attention,
including unobvious tricks, TODO items, questions, breakage, danger.
- You should, however, use appropriate declarations
+ You should, of course, 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.
+ When you do, however, see our recommendations for
+ Unsafe Operations.
Places where it is actually appropriate to use EVAL
- are so few and far between that you must get permission;
+ are so few and far between that you must consult with your reviewers;
it's easily misused.
@@ -2989,7 +3073,15 @@ Robert Brown
testing frameworks and code that is ONLY used for testing;
the build infrastructure; and
inside macros when there isn't any reasonable way
- to avoid using EVAL
(there almost always is).
+ to avoid using EVAL
+ (there almost always is, and where there isn't,
+ consider that unless you can also dump
+ code to reproduce the side-effects being evaluated
+ in the macro expansion,
+ you may actually have to use
+ ASDF-FINALIZERS:EVAL-AT-TOPLEVEL
+ so the side-effects are present when loading the fasl
+ without compiling).
Other uses need to be checked.
We do have a few special cases where EVAL
is allowed.
- Some systems define unsafe numerical comparators, + Common Lisp implementations often provide backdoors + to compute some operations faster in an unsafe way. + For instance, some libraries provide arithmetic operations that are designed to be used with fixnums only, - and are faster in that case, - but incorrect in case of overflow, and - have undefined behavior when called with anything but a fixnum. - You must not use these functions without both - profiling results indicating the need for this optimization, - and careful documentation explaining why it is safe to use them. + and yield the correct result faster if provided proper arguments. + The downside is that the result of such operations + is incorrect in case of overflow, and can + have undefined behavior when called with anything but fixnums.
++ More generally, unsafe operations + will yield the correct result faster + than would the equivalent safe operation + if the arguments to satisfy some invariant such as + being of the correct type and small enough; + however if the arguments fail to satisfy the required invariants, + then the operation may have undefined behavior, + such as crashing the software, or, + which is sometimes worse, silently giving wrong answers. + Depending on whether the software is piloting an aircraft + or other life-critical device, + or whether it is accounting for large amounts money, + such undefined behavior can kill or bankrupt people. + Yet proper speed can sometimes make the difference between + software that's unusably slow and software that does its job; + between software that is a net loss + and software that can yield a profit. +
+
+ You must not define or use unsafe operations without both
+ profiling results indicating the need for this optimization,
+ and careful documentation explaining why it is safe to use them.
+ Unsafe operations should be restricted to internal functions;
+ you should carefully documented how unsafe it is
+ to use these functions with the wrong arguments.
+ You should only use unsafe operations
+ inside functions internal to a package and
+ you should document the use of the declarations,
+ since calling the functions with arguments of the wrong type
+ can lead to undefined behavior.
+ Use check-type
in functions exported from a package
+ to sanitize input arguments,
+ so that internal functions are never passed illegal values.
+
+ On some compilers,
+ new unsafe operations
+ can usually be defined by combining
+ type declarations with an OPTIMIZE
declaration
+ that has sufficiently high SPEED
and low SAFETY
.
+ In addition to providing more speed for production code,
+ such declarations may more helpful
+ than check-type
assertions
+ for finding bugs at compile-time,
+ on compilers that have type inference.
+ These compilers may interpret those declarations as assertions
+ if you switch to safer and slower optimize settings;
+ this is good to locate a dynamic error in your code during development,
+ but is not to be used for production code since
+ it defeats the purpose of declarations as a performance trick.
+
SATISFIES
clause in a type specifier.
+
+ Most Common Lisp implementations can't optimize
+ based on a SATISFIES
type,
+ but many of them offer simple optimizations
+ based on a type of the form
+ (AND FOO (SATISFIES BAR-P))
+ where the first term of the AND
clause
+ describes the structure of the object
+ without any SATISFIES
+ and the second term is the SATISFIES
.
+
+ However, AND
in the DEFTYPE
language
+ isn't a left-to-right short-circuit operator
+ as in the expression language;
+ it is a symmetrical connector that allows for reordering subterms
+ and doesn't guarantee short-circuiting.
+ Therefore, in the above example,
+ you cannot rely on the test for INTEGER
ness
+ to protect the function PRIME-NUMBER-P
+ from being supplied non-integer arguments
+ to test for being of instances of the type.
+ Implementations may, and some will,
+ invoke SATISFIES
-specified function
+ at compile-time to test various relevant objects.
+
+ That is why any function specified in a SATISFIES
clause
+ MUST accept objects of any type as argument to the function,
+ and MUST be defined within an EVAL-WHEN
.
+
+ In particular, the above means that the
+ example
+ used in the Common Lisp Standard is erroneous:
+ (and integer (satisfies evenp))
+ is not a safe, conformant type specifier to use,
+ because EVENP
will throw an error
+ rather than return NIL
+ when passed a non-integer as an argument.
+
+ Finally, there is a catch when your DEFTYPE
code expands
+ to a SATISFIES
with a dynamically generated function:
+
DEFTYPE
.
+
+ Therefore, you cannot merely create the function
+ as a side-effect of expansion
+ using EVAL
at type-expansion time.
+ The solution is to use EVAL-AT-TOPLEVEL
+ from ASDF-FINALIZERS
.
+ It will not only EVAL
your function definition
+ at type-expansion time for immediate availability,
+ it will also save the form aside, for inclusion in a
+ (FINAL-FORMS)
at the end of the file being compiled,
+ which ASDF-FINALIZERS
ensures you include,
+ throwing an error if you omit it.
+ This way, the form is also available at load-time.
+
+ Common Lisp is hard to satisfy. +
+ +-Revision 1.10 +Revision 1.11