mirror of
https://github.com/google/styleguide.git
synced 2024-03-22 13:11:43 +08:00
Google Common Lisp Style Guide updated to 1.11.
This commit is contained in:
parent
0c584cb624
commit
8c4c5ce85d
411
lispguide.xml
411
lispguide.xml
@ -5,14 +5,13 @@
|
||||
|
||||
<p align="right">
|
||||
|
||||
Revision 1.10
|
||||
Revision 1.11
|
||||
</p>
|
||||
|
||||
|
||||
<address>
|
||||
Robert Brown
|
||||
</address>
|
||||
|
||||
<address>
|
||||
<a HREF="mailto:tunes@google.com">François-René Rideau</a>
|
||||
</address>
|
||||
@ -21,6 +20,11 @@ Robert Brown
|
||||
In memoriam Dan Weinreb
|
||||
</address>
|
||||
|
||||
<p align="center">
|
||||
<cite>Patterns mean "I have run out of language."</cite> — Rich Hickey
|
||||
</p>
|
||||
|
||||
|
||||
<OVERVIEW>
|
||||
<CATEGORY title="Important Note">
|
||||
<STYLEPOINT title="Note: Displaying Hidden Details in this Guide">
|
||||
@ -151,11 +155,32 @@ Robert Brown
|
||||
</STYLEPOINT>
|
||||
<STYLEPOINT title="Conventions">
|
||||
<SUMMARY>
|
||||
Some guidelines, such those about as comments and indentation,
|
||||
are based purely on convention, rather than on clear technical merit.
|
||||
However, conventions are important for readability,
|
||||
thus most conventions are MUSTs.
|
||||
You MUST follow conventions. They are not optional.
|
||||
</SUMMARY>
|
||||
<BODY>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
Conventional guidelines <em>are</em> 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.
|
||||
</p>
|
||||
</BODY>
|
||||
</STYLEPOINT>
|
||||
<STYLEPOINT title="Old Code">
|
||||
<p>
|
||||
@ -603,13 +628,10 @@ Robert Brown
|
||||
<BODY>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
</BODY>
|
||||
</STYLEPOINT>
|
||||
<STYLEPOINT title="Indentation">
|
||||
@ -826,6 +848,7 @@ Robert Brown
|
||||
c))
|
||||
</BAD_CODE_SNIPPET>
|
||||
<CODE_SNIPPET>
|
||||
;; Better
|
||||
(defun munge (a b c)
|
||||
(* (+ a b)
|
||||
c))
|
||||
@ -899,7 +922,7 @@ Robert Brown
|
||||
For example, "The value of LENGTH should be an integer."
|
||||
</p>
|
||||
<CODE_SNIPPET>
|
||||
(defun primep (n)
|
||||
(defun small-prime-number-p (n)
|
||||
"Return T if N, an integer, is a prime number. Otherwise, return NIL."
|
||||
(cond ((or (< n 2))
|
||||
nil)
|
||||
@ -910,7 +933,7 @@ Robert Brown
|
||||
(t
|
||||
(loop for i from 3 upto (sqrt n) by 2
|
||||
do (when (divisorp i n)
|
||||
(return-from primep nil)))
|
||||
(return-from small-prime-number-p nil)))
|
||||
t)))
|
||||
</CODE_SNIPPET>
|
||||
<CODE_SNIPPET>
|
||||
@ -990,7 +1013,7 @@ Robert Brown
|
||||
;;; Divisibility
|
||||
;;; Comments that describe a group of definitions.
|
||||
|
||||
(defun divisible-by (n d)
|
||||
(defun divisorp (d n)
|
||||
(zerop (mod n d)))
|
||||
|
||||
(defun proper-divisors (n)
|
||||
@ -1001,15 +1024,18 @@ Robert Brown
|
||||
|
||||
;;; Prime numbers
|
||||
|
||||
(defun prime-p (n)
|
||||
(cond ((or (< n 2)) nil)
|
||||
((= n 2) t) ; parenthetical remark here
|
||||
; continuation of the remark
|
||||
((divisible-by n 2) nil) ; different remark
|
||||
;; Comment that applies to a secion of code.
|
||||
(t (loop for i from 3 upto (sqrt n) by 2
|
||||
do (when (divisible-by n i)
|
||||
(return-from prime-p nil)))
|
||||
(defun small-prime-number-p (n)
|
||||
(cond ((or (< n 2))
|
||||
nil)
|
||||
((= n 2) ; parenthetical remark here
|
||||
t) ; continuation of the remark
|
||||
((divisible-by n 2)
|
||||
nil) ; different remark
|
||||
;; Comment that applies to a section of code.
|
||||
(t
|
||||
(loop for i from 3 upto (sqrt n) by 2
|
||||
do (when (divisorp i n)
|
||||
(return-from small-prime-number-p nil)))
|
||||
t)))
|
||||
</CODE_SNIPPET>
|
||||
<p>
|
||||
@ -1032,7 +1058,7 @@ Robert Brown
|
||||
</STYLEPOINT>
|
||||
<STYLEPOINT title="Attention Required">
|
||||
<SUMMARY>
|
||||
You must follow the ITA convention of using <code>---</code>
|
||||
You must follow the convention of using <code>---</code>
|
||||
for comments requiring special attention,
|
||||
including unobvious tricks, TODO items, questions, breakage, danger.
|
||||
</SUMMARY>
|
||||
@ -1838,22 +1864,11 @@ Robert Brown
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>
|
||||
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 <code>check-type</code> to sanitize any input being passed
|
||||
to such function from uncontrolled sources.
|
||||
When you do, however, see our recommendations for
|
||||
<a href="#Unsafe_Operations">Unsafe Operations</a>.
|
||||
</p>
|
||||
</BODY>
|
||||
</STYLEPOINT>
|
||||
@ -1914,30 +1929,46 @@ Robert Brown
|
||||
One way to write a macro is the so-called "call-with" style,
|
||||
explained at length in
|
||||
<a href="http://random-state.net/log/3390120648.html">http://random-state.net/log/3390120648.html</a>.
|
||||
The idea is to keep the macro very simple,
|
||||
generating a call to an auxiliary function,
|
||||
which often takes a functional argument
|
||||
consisting of code in the original macro call.
|
||||
Advantages: during development,
|
||||
you can modify the function instead of recompiling all macro call sites;
|
||||
during debugging, you can see the function in the stack trace;
|
||||
there is less generated code so smaller memory usage.
|
||||
You should use this style unless
|
||||
the macro body is simple, rarely subject to change,
|
||||
<em>and</em> the macro is used in tight loops where performance matters.
|
||||
Think about whether the extra stack frames are helpful or just clutter.
|
||||
The general principle is that the macro is strictly limited to processing the syntax,
|
||||
and as much of the semantics as possible is kept in normal functions.
|
||||
Therefore, a macro <code>WITH-<em>FOO</em></code> is often limited to
|
||||
generating a call to an auxiliary function
|
||||
<code>CALL-WITH-<em>FOO</em></code>
|
||||
with arguments deduced from the macro arguments.
|
||||
Macro <code>&body</code> arguments are typically
|
||||
wrapped into a lambda expression of which they become the body,
|
||||
which is passed as one of the arguments of the auxiliary function.
|
||||
</p>
|
||||
<p>
|
||||
The advantages are many.
|
||||
By keeping semantics outside the macro,
|
||||
the macro is made simpler, easier to get right, and less subject to change,
|
||||
which makes it easier to develop and maintain.
|
||||
The semantics is written in a simpler language — one without staging —
|
||||
which also makes it easier to develop and maintain.
|
||||
It becomes possible to debug and update the semantic function
|
||||
without having to recompile all clients of the macro.
|
||||
The semantic function appears in the stack trace
|
||||
which also helps debug client functions.
|
||||
The macro expansion is made shorter and
|
||||
each expansion shares more code with other expansions,
|
||||
which reduces memory pressure which in turn usually makes things faster.
|
||||
You should use this style unless the macro is used
|
||||
in tight loops where performance matters;
|
||||
and even then, see our rules regarding optimization.
|
||||
</p>
|
||||
<p>
|
||||
Any functions (closures) created by the macro should be named:
|
||||
either use <code>FLET</code> or <code>NAMED-LAMBDA</code>.
|
||||
Using <code>FLET</code> is also good
|
||||
because you can declare the function to be of dynamic
|
||||
extent (if it is — and usually it is).
|
||||
extent (if it is — and often it is).
|
||||
</p>
|
||||
<p>
|
||||
If a macro call contains a form,
|
||||
and the macro expansion includes more than one copy of that form,
|
||||
the form can be evaluated more than once.
|
||||
the form can be evaluated more than once,
|
||||
and code it contains macro-expanded and compiled more than once.
|
||||
If someone uses the macro and calls it
|
||||
with a form that has side effects or that takes a long time to compute,
|
||||
the behavior will be undesirable
|
||||
@ -1950,6 +1981,11 @@ Robert Brown
|
||||
that generates code to do this.
|
||||
See also <code>ALEXANDRIA:WITH-GENSYMS</code>,
|
||||
to make some temporary variables in the generated code.
|
||||
Note that if you follow our <code>CALL-WITH-</code> style,
|
||||
you typically expand the code only once, as either
|
||||
an argument to the auxiliary function, or
|
||||
the body of a lambda passed as argument to it;
|
||||
you therefore avoid the above complexity.
|
||||
</p>
|
||||
<p>
|
||||
When you write a macro with a body,
|
||||
@ -2010,11 +2046,13 @@ Robert Brown
|
||||
</STYLEPOINT>
|
||||
<STYLEPOINT title="EVAL-WHEN">
|
||||
<SUMMARY>
|
||||
<code>EVAL-WHEN</code> is tricky. Be aware.
|
||||
When using <code>EVAL-WHEN</code>, you should almost always use all of
|
||||
<code>(:compile-toplevel :load-toplevel :execute)</code>.
|
||||
</SUMMARY>
|
||||
<BODY>
|
||||
<p>
|
||||
Lisp evaluation happens at several "times".
|
||||
Lisp evaluation happens at several times,
|
||||
some of them interleaved.
|
||||
Be aware of them when writing macros.
|
||||
<a href="http://fare.livejournal.com/146698.html">EVAL-WHEN considered harmful to your mental health</a>.
|
||||
</p>
|
||||
@ -2037,8 +2075,8 @@ Robert Brown
|
||||
within the same Lisp session.
|
||||
</p>
|
||||
<p>
|
||||
In some odd cases, you may want to evaluate things from within
|
||||
the expansion of a <code>DEFTYPE</code>
|
||||
In some odd cases, you may want to evaluate top-level definitions
|
||||
from within the expansion of a <code>DEFTYPE</code>
|
||||
or of a non-top-level <code>DEFMACRO</code>.
|
||||
In these cases, you should use <code>ASDF-FINALIZERS</code>
|
||||
and its <code>ASDF-FINALIZERS:EVAL-AT-TOPLEVEL</code> form.
|
||||
@ -2453,11 +2491,19 @@ Robert Brown
|
||||
<p>
|
||||
You must consistently use either <code>#'(lambda ...)</code>
|
||||
or <code>(lambda ...)</code> without <code>#'</code> everywhere.
|
||||
You should only use the former style if your code is intended as a library
|
||||
with maximal compatibility to all Common Lisp implementations.
|
||||
Unlike the case of <code>#'symbol</code> vs <code>'symbol</code>,
|
||||
it is only a syntactic difference with no semantic impact,
|
||||
except that the former works on Genera and the latter doesn't.
|
||||
You must use the former style if your code is intended as a library
|
||||
with maximal compatibility to all Common Lisp implementations;
|
||||
otherwise, it is optional which style you use.
|
||||
<code>#'</code> may be seen as a hint
|
||||
that you're introducing a function in expression context;
|
||||
but the <code>lambda</code> itself is usually sufficient hint,
|
||||
and concision is good.
|
||||
Choose wisely, but above all,
|
||||
consistently with yourself and other developers,
|
||||
within a same file, package, system, project, etc.
|
||||
</p>
|
||||
<p>
|
||||
Note that if you start writing a new system
|
||||
@ -2670,20 +2716,40 @@ Robert Brown
|
||||
</p>
|
||||
<p>
|
||||
You should only use <code>CASE</code> and <code>ECASE</code>
|
||||
to compare integers, characters or symbols
|
||||
to compare numbers, characters or symbols
|
||||
(including booleans and keywords).
|
||||
Indeed, <code>CASE</code> uses <code>EQL</code> for comparisons,
|
||||
so strings and other numbers may not compare the way you expect.
|
||||
so strings, pathnames and structures may not compare the way you expect,
|
||||
and <code>1</code> will differ from <code>1.0</code>.
|
||||
</p>
|
||||
<p>
|
||||
You should use <code>ECASE</code> and <code>ETYPECASE</code>
|
||||
in preference to <code>CASE</code> and <code>TYPECASE</code>.
|
||||
It is better to catch erroneous values early.
|
||||
</p>
|
||||
<p>
|
||||
You should not use <code>CCASE</code> or <code>CTYPECASE</code> at all.
|
||||
At least, you should not use them in server processes,
|
||||
unless you have quite robust error handling infrastructure
|
||||
and make sure not to leak sensitive data this way.
|
||||
These are meant for interactive use,
|
||||
and can cause interesting damage
|
||||
if they cause data or control leak to attackers.
|
||||
</p>
|
||||
<p>
|
||||
You must not use gratuitous single quotes in <code>CASE</code> forms.
|
||||
This is a common error:
|
||||
</p>
|
||||
<BAD_CODE_SNIPPET>
|
||||
(case x
|
||||
('bar :bar)
|
||||
('quux :quux))
|
||||
(case x ; Bad: silently returns NIL on mismatch
|
||||
('bar :bar) ; Bad: catches QUOTE
|
||||
('baz :baz)) ; Bad: also would catch QUOTE
|
||||
</BAD_CODE_SNIPPET>
|
||||
<CODE_SNIPPET>
|
||||
(ecase x ; Better: will error on mismatch
|
||||
((bar) :bar) ; Better: won't match QUOTE
|
||||
((baz) :baz)) ; Better: same reason
|
||||
</CODE_SNIPPET>
|
||||
<p>
|
||||
<code>'BAR</code> there is <code>(QUOTE BAR)</code>,
|
||||
meaning this leg of the case will be executed
|
||||
@ -2696,14 +2762,26 @@ Robert Brown
|
||||
In <code>CASE</code> forms,
|
||||
you must use <code>otherwise</code> instead of <code>t</code>
|
||||
when you mean "execute this clause if the others fail".
|
||||
And you must use <code>((t) ...)</code>
|
||||
when you mean "match the symbol T".
|
||||
You must use <code>((t) ...)</code>
|
||||
when you mean "match the symbol T" rather than "match anything".
|
||||
You must also use <code>((nil) ...)</code>
|
||||
when you mean "match the symbol NIL" rather than "match nothing".
|
||||
</p>
|
||||
<p>
|
||||
You should use <code>ECASE</code> and <code>ETYPECASE</code>
|
||||
in preference to <code>CASE</code> and <code>TYPECASE</code>.
|
||||
You should not use <code>CCASE</code> or <code>CTYPECASE</code> at all.
|
||||
Therefore, if you want to map booleans <code>NIL</code> and <code>T</code>
|
||||
to respective symbols <code>:BAR</code> and <code>:QUUX</code>,
|
||||
you should avoid the former way and do it the latter way:
|
||||
</p>
|
||||
<BAD_CODE_SNIPPET>
|
||||
(ecase x ; Bad: has no actual error case!
|
||||
(nil :bar)) ; Bad: matches nothing
|
||||
(t :quux)) ; Bad: matches anything
|
||||
</BAD_CODE_SNIPPET>
|
||||
<CODE_SNIPPET>
|
||||
(ecase x ; Better: will actually catch non-booleans
|
||||
((nil) :bar)) ; Better: matches NIL
|
||||
((t) :quux)) ; Better: matches T
|
||||
</CODE_SNIPPET>
|
||||
</BODY>
|
||||
</STYLEPOINT>
|
||||
<STYLEPOINT title="Identity, Equality and Comparisons">
|
||||
@ -2797,6 +2875,12 @@ Robert Brown
|
||||
Monetary amounts should be using decimal (rational) numbers
|
||||
to avoid the complexities and rounding errors
|
||||
of floating-point arithmetic.
|
||||
Libraries such as
|
||||
<a href="http://wukix.com/lisp-decimals">wu-decimal</a>
|
||||
can help you;
|
||||
if this library is not satisfactory, see above about
|
||||
<a href="#Using_Libraries">Using Libraries</a> and
|
||||
<a href="#Open-Sourcing_Code">Open-Sourcing Code</a>.
|
||||
</p>
|
||||
|
||||
</BODY>
|
||||
@ -2941,10 +3025,10 @@ Robert Brown
|
||||
use <code>STRING=</code> or <code>STRING-EQUAL</code>.
|
||||
</p>
|
||||
<BAD_CODE_SNIPPET>
|
||||
(member (intern str :keyword) $keys) ; BAD
|
||||
(member (intern str :keyword) $keys) ; Bad
|
||||
</BAD_CODE_SNIPPET>
|
||||
<CODE_SNIPPET>
|
||||
(member str $keys :test #'string-equal) ; GOOD
|
||||
(member str $keys :test #'string-equal) ; Better
|
||||
</CODE_SNIPPET>
|
||||
<p>
|
||||
You must not use <code>UNINTERN</code> at runtime.
|
||||
@ -2972,7 +3056,7 @@ Robert Brown
|
||||
<BODY>
|
||||
<p>
|
||||
Places where it is actually appropriate to use <code>EVAL</code>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
@ -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 <code>EVAL</code> (there almost always is).
|
||||
to avoid using <code>EVAL</code>
|
||||
(there almost always is, and where there isn't,
|
||||
consider that unless you can <em>also</em> dump
|
||||
code to reproduce the side-effects being evaluated
|
||||
in the macro expansion,
|
||||
you may actually have to use
|
||||
<code>ASDF-FINALIZERS:EVAL-AT-TOPLEVEL</code>
|
||||
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 <code>EVAL</code> is allowed.
|
||||
</p>
|
||||
@ -3141,16 +3233,68 @@ Robert Brown
|
||||
</SUMMARY>
|
||||
<BODY>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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 <code>check-type</code> in functions exported from a package
|
||||
to sanitize input arguments,
|
||||
so that internal functions are never passed illegal values.
|
||||
</p>
|
||||
<p>
|
||||
On some compilers,
|
||||
new unsafe operations
|
||||
can usually be defined by combining
|
||||
type declarations with an <code>OPTIMIZE</code> declaration
|
||||
that has sufficiently high <code>SPEED</code> and low <code>SAFETY</code>.
|
||||
In addition to providing more speed for production code,
|
||||
such declarations may more helpful
|
||||
than <code>check-type</code> 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.
|
||||
</p>
|
||||
</BODY>
|
||||
</STYLEPOINT>
|
||||
<STYLEPOINT title="REDUCE vs APPLY">
|
||||
@ -3254,6 +3398,115 @@ Robert Brown
|
||||
</STYLEPOINT>
|
||||
</CATEGORY>
|
||||
|
||||
<CATEGORY title="Pitfalls">
|
||||
<STYLEPOINT title="SATISFIES">
|
||||
<SUMMARY>
|
||||
You must be careful when using a <code>SATISFIES</code> clause in a type specifier.
|
||||
</SUMMARY>
|
||||
<BODY>
|
||||
<p>
|
||||
Most Common Lisp implementations can't optimize
|
||||
based on a <code>SATISFIES</code> type,
|
||||
but many of them offer simple optimizations
|
||||
based on a type of the form
|
||||
<code>(AND FOO (SATISFIES BAR-P))</code>
|
||||
where the first term of the <code>AND</code> clause
|
||||
describes the structure of the object
|
||||
without any <code>SATISFIES</code>
|
||||
and the second term is the <code>SATISFIES</code>.
|
||||
</p>
|
||||
<BAD_CODE_SNIPPET>
|
||||
(deftype prime-number () (satisfies prime-number-p)) ; Bad
|
||||
</BAD_CODE_SNIPPET>
|
||||
<CODE_SNIPPET>
|
||||
(deftype prime-number () (and integer (satisfies prime-number-p)) ; Better
|
||||
</CODE_SNIPPET>
|
||||
<p>
|
||||
However, <code>AND</code> in the <code>DEFTYPE</code> 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 <code>INTEGER</code>ness
|
||||
to protect the function <code>PRIME-NUMBER-P</code>
|
||||
from being supplied non-integer arguments
|
||||
to test for being of instances of the type.
|
||||
Implementations may, and some <em>will</em>,
|
||||
invoke <code>SATISFIES</code>-specified function
|
||||
at compile-time to test various relevant objects.
|
||||
</p>
|
||||
<p>
|
||||
That is why any function specified in a <code>SATISFIES</code> clause
|
||||
MUST accept objects of any type as argument to the function,
|
||||
and MUST be defined within an <code>EVAL-WHEN</code>.
|
||||
</p>
|
||||
<BAD_CODE_SNIPPET>
|
||||
(defun prime-number-p (n) ;; Doubly BAD!
|
||||
(let ((m (abs n)))
|
||||
(if (<= m *prime-number-cutoff*)
|
||||
(small-prime-number-p m)
|
||||
(big-prime-number-p m))))
|
||||
</BAD_CODE_SNIPPET>
|
||||
<CODE_SNIPPET>
|
||||
(eval-when (:compile-toplevel :load-toplevel :execute) ;; Better
|
||||
(defun prime-number-p (n)
|
||||
(when (integerp n) ;; Better
|
||||
(let ((m (abs n)))
|
||||
(if (<= m *prime-number-cutoff*)
|
||||
(small-prime-number-p m)
|
||||
(big-prime-number-p m))))))
|
||||
</CODE_SNIPPET>
|
||||
<p>
|
||||
In particular, the above means that the
|
||||
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_satisf.htm">example</a>
|
||||
used in the Common Lisp Standard is erroneous:
|
||||
<code>(and integer (satisfies evenp))</code>
|
||||
is <em>not</em> a safe, conformant type specifier to use,
|
||||
because <code>EVENP</code> will throw an error
|
||||
rather than return <code>NIL</code>
|
||||
when passed a non-integer as an argument.
|
||||
</p>
|
||||
<p>
|
||||
Finally, there is a catch when your <code>DEFTYPE</code> code expands
|
||||
to a <code>SATISFIES</code> with a dynamically generated function:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
You cannot control when implementations will or will not
|
||||
expand a <code>DEFTYPE</code>.
|
||||
</li>
|
||||
<li>
|
||||
The expansion itself cannot contain a function definition
|
||||
or any code in the expression language.
|
||||
</li>
|
||||
<li>
|
||||
You cannot control when the expansion is used,
|
||||
it may happen in a different process
|
||||
that didn't expand the definition.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Therefore, you cannot merely create the function
|
||||
as a side-effect of expansion
|
||||
using <code>EVAL</code> at type-expansion time.
|
||||
The solution is to use <code>EVAL-AT-TOPLEVEL</code>
|
||||
from <code>ASDF-FINALIZERS</code>.
|
||||
It will not only <code>EVAL</code> your function definition
|
||||
at type-expansion time for immediate availability,
|
||||
it will also save the form aside, for inclusion in a
|
||||
<code>(FINAL-FORMS)</code> at the end of the file being compiled,
|
||||
which <code>ASDF-FINALIZERS</code> ensures you include,
|
||||
throwing an error if you omit it.
|
||||
This way, the form is also available at load-time.
|
||||
</p>
|
||||
<p>
|
||||
Common Lisp is hard to satisfy.
|
||||
</p>
|
||||
</BODY>
|
||||
</STYLEPOINT>
|
||||
</CATEGORY>
|
||||
|
||||
<HR/>
|
||||
|
||||
<small>Credits:
|
||||
@ -3266,7 +3519,7 @@ Robert Brown
|
||||
</small>
|
||||
|
||||
<p align="right">
|
||||
Revision 1.10
|
||||
Revision 1.11
|
||||
</p>
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user