[Style] @compare plus some rules on use of constructs
This commit is contained in:
parent
75edb5a064
commit
bceca4b22f
73
collects/scribblings/style/constructs.scrbl
Normal file
73
collects/scribblings/style/constructs.scrbl
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#lang scribble/base
|
||||||
|
|
||||||
|
@(require "shared.rkt")
|
||||||
|
|
||||||
|
@title{Choosing the Right Construct}
|
||||||
|
|
||||||
|
Racket provides a range of constructs for the same or similar purposes.
|
||||||
|
Although the Racket designers don't think that there is one right way for
|
||||||
|
everything, we prefer certain constructs in certain situations for consistency
|
||||||
|
and readability.
|
||||||
|
|
||||||
|
@; -----------------------------------------------------------------------------
|
||||||
|
@section{Definitions}
|
||||||
|
|
||||||
|
Here are a few of Racket's definitional constructs: @scheme[let], @scheme[let*],
|
||||||
|
@scheme[letrec], and @scheme[define]. Except for the last one, all others force
|
||||||
|
an increase to the indentation level. We therefore request that you favor
|
||||||
|
@scheme[define] over all other features when feasible.
|
||||||
|
|
||||||
|
@compare[
|
||||||
|
@racketmod[#:file
|
||||||
|
@tt{good}
|
||||||
|
racket
|
||||||
|
|
||||||
|
(define (swap x y)
|
||||||
|
(define t (unbox x))
|
||||||
|
(set-box! x (unbox y))
|
||||||
|
(set-box! y t))
|
||||||
|
]
|
||||||
|
@; -----------------------------------------------------------------------------
|
||||||
|
@racketmod[#:file
|
||||||
|
@tt{bad}
|
||||||
|
racket
|
||||||
|
|
||||||
|
(define (swap x y)
|
||||||
|
(let ([t (unbox x)])
|
||||||
|
(set-box! x (unbox y))
|
||||||
|
(set-box! y t)))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
@; -----------------------------------------------------------------------------
|
||||||
|
@section{Conditionals}
|
||||||
|
|
||||||
|
Like definitional constructs, conditionals come in many flavors, too. Because
|
||||||
|
@scheme[cond] and its relatives now allow local uses of @scheme[define], you
|
||||||
|
should prefer them over @scheme[if].
|
||||||
|
|
||||||
|
@compare[
|
||||||
|
@racketmod[#:file
|
||||||
|
@tt{good}
|
||||||
|
racket
|
||||||
|
|
||||||
|
(cond
|
||||||
|
[(empty? l) true]
|
||||||
|
[else
|
||||||
|
(define fst (first l))
|
||||||
|
(define rst (rest l))
|
||||||
|
(and (flat-rate fst)
|
||||||
|
(curved fst (chk rst)))])
|
||||||
|
]
|
||||||
|
@racketmod[#:file
|
||||||
|
@tt{bad}
|
||||||
|
racket
|
||||||
|
|
||||||
|
(if (empty? l)
|
||||||
|
true
|
||||||
|
(let ([fst (first l)]
|
||||||
|
[rst (rest l)])
|
||||||
|
(and (flat-rate fst)
|
||||||
|
(curved fst (chk rst)))))
|
||||||
|
]
|
||||||
|
]
|
|
@ -1,5 +1,7 @@
|
||||||
#lang scribble/base
|
#lang scribble/base
|
||||||
|
|
||||||
|
@(require "shared.rkt")
|
||||||
|
|
||||||
@title[#:tag "correct-maintain-speed"]{Basic Facts of Life}
|
@title[#:tag "correct-maintain-speed"]{Basic Facts of Life}
|
||||||
|
|
||||||
@nested[#:style 'inset]{ Favor readers over writers.
|
@nested[#:style 'inset]{ Favor readers over writers.
|
||||||
|
@ -29,11 +31,11 @@ watching Matthew, Robby, Shriram and others create the original code base}
|
||||||
@nested[#:style 'inset]{It is the way we choose to fight our bugs that
|
@nested[#:style 'inset]{It is the way we choose to fight our bugs that
|
||||||
determines our character, not their presence or absence. -- Robby, in response}
|
determines our character, not their presence or absence. -- Robby, in response}
|
||||||
|
|
||||||
Correctness is a perfectionist goal beyond the reach of PLT. All software
|
Complete correctness is a perfectionist goal beyond the reach of PLT. All
|
||||||
has mistakes. If they are unknown, the software isn't being used. The goal
|
software has mistakes. If they are unknown, the software isn't being
|
||||||
is, however, to ensure some basic level of correctness before a
|
used. The goal is, however, to ensure some basic level of correctness
|
||||||
feature is released and to ensure that the same mistake isn't introduced
|
before a feature is released and to ensure that the same mistake isn't
|
||||||
again.
|
introduced again.
|
||||||
|
|
||||||
Formulate test suites. Use unit testing. Use random testing. Use fuzz
|
Formulate test suites. Use unit testing. Use random testing. Use fuzz
|
||||||
testing. Test!
|
testing. Test!
|
||||||
|
@ -41,15 +43,13 @@ Formulate test suites. Use unit testing. Use random testing. Use fuzz
|
||||||
Run the test suites before you commit. Read DrDr's emails; don't ignore
|
Run the test suites before you commit. Read DrDr's emails; don't ignore
|
||||||
them.
|
them.
|
||||||
|
|
||||||
Add tests to test suites during debugging. That is, first, write an
|
When you debug, formulate a test case first. Put it into the test suite for
|
||||||
automated test that exposes the mistake in the existing
|
the component so the mistake will never be accidentally re-introduced.
|
||||||
implementation. Put this in the software's test suite so it will never be
|
Second, modify the code to fix the mistake. Do this second to be sure you
|
||||||
accidentally introduced again. Second, modify the code to fix the
|
didn't introduce a mistake in your tests; it is all too easy to think
|
||||||
mistake. Do this second to be sure you didn't introduce a mistake in your
|
you've fixed a mistake when in reality your new test just doesn't properly
|
||||||
tests; it is all too easy to think you've fixed a mistake when in reality
|
reveal the old mistake. Third, re-run the test suite to ensure that the
|
||||||
your new test just doesn't properly reveal the old mistake. Third, re-run
|
mistake is fixed and no existing tests fail.
|
||||||
the test suite to ensure that the mistake is fixed and no existing tests
|
|
||||||
fail.
|
|
||||||
|
|
||||||
Create test suites. Editing code without an existing test suite is like
|
Create test suites. Editing code without an existing test suite is like
|
||||||
flying blind. If there is no existing test suite, you have no idea
|
flying blind. If there is no existing test suite, you have no idea
|
||||||
|
@ -62,7 +62,7 @@ Create test suites. Editing code without an existing test suite is like
|
||||||
and you should be able to turn that into a test case. If you cannot, you
|
and you should be able to turn that into a test case. If you cannot, you
|
||||||
have a few options:
|
have a few options:
|
||||||
|
|
||||||
@itemlist[#:style 'ordered
|
@itemlist[#:style 'ordered
|
||||||
|
|
||||||
@item{Add an end-to-end test that may have to be verified by a human. For
|
@item{Add an end-to-end test that may have to be verified by a human. For
|
||||||
example, it might be hard to test Slideshow, so you could create a slide
|
example, it might be hard to test Slideshow, so you could create a slide
|
||||||
|
@ -84,16 +84,18 @@ Comprehensible code is maintainable.
|
||||||
Code is comprehensible when you can understand its external purpose. To
|
Code is comprehensible when you can understand its external purpose. To
|
||||||
this end, code must come with external documentation. Released code must
|
this end, code must come with external documentation. Released code must
|
||||||
have documentation. A change to the external behavior of code must induce
|
have documentation. A change to the external behavior of code must induce
|
||||||
an immediate change to its documentation.
|
a simultaneous change to its documentation---"simultaneous" means that the
|
||||||
|
two changes are in the same commit to the code base.
|
||||||
|
|
||||||
In order to document code, refer to the
|
In order to document code, refer to the
|
||||||
@hyperlink["http://docs.racket-lang.org/scribble/how-to-doc.html#%28part._reference-style%29"]{style
|
@hyperlink["http://docs.racket-lang.org/scribble/how-to-doc.html#%28part._reference-style%29"]{style
|
||||||
guide} in the Scribble manual. Ideally documentation comes in two parts:
|
guide} in the Scribble manual. Ideally documentation comes in two parts:
|
||||||
a "Guide" section, which explains the purpose and suggests use cases, and
|
a "Guide" section, which explains the purpose and suggests use cases, and
|
||||||
a traditional "Reference" section, which presents the minutae. Also
|
a traditional "Reference" section, which presents the minutae. Also
|
||||||
consider adding examples to each function in your "Reference" section.
|
consider adding examples for each function and construct in your
|
||||||
Finally, ensure you have all the correct @tt{for-label} @tt{require}s
|
"Reference" section. Finally, ensure you have all the correct
|
||||||
and make use of other useful cross-references.
|
@tt{for-label} @tt{require}s and make use of other useful
|
||||||
|
cross-references.
|
||||||
|
|
||||||
Code comprehension also requires adherence to basic elements of style and
|
Code comprehension also requires adherence to basic elements of style and
|
||||||
some internal documentation. The rest of this document is mostly about
|
some internal documentation. The rest of this document is mostly about
|
||||||
|
@ -108,29 +110,34 @@ Making code fast is an endless task.
|
||||||
Making code @emph{reasonably} fast is the goal.
|
Making code @emph{reasonably} fast is the goal.
|
||||||
|
|
||||||
It is especially the goal for all pieces of the code base that are reused
|
It is especially the goal for all pieces of the code base that are reused
|
||||||
elsewhere.
|
elsewhere. Write them using @racketmod[racket] so that they don't affect
|
||||||
|
the load-time for scripts. See the next section.
|
||||||
|
|
||||||
Just as for correctness, strive for basic tests, that is, tests that
|
As with correctness, performance demands some "testing". At a minimum,
|
||||||
exercise your code on reasonably large inputs. While a regular test suite
|
exercise your code on some reasonably large inputs. Add a file to the test
|
||||||
for a Universe display deals with a 50 x 50 display window, the stress test
|
suite that runs large inputs regularly. For example, a regular test suite
|
||||||
suite should check whether Universe event handlers and drawing routines
|
for a Universe display deals with a 50 x 50 display window; one of its
|
||||||
|
stress tests checks whether Universe event handlers and drawing routines
|
||||||
can cope with laptop size displays or even a 30in display. Or, if you were
|
can cope with laptop size displays or even a 30in display. Or, if you were
|
||||||
to write a library for a queue data structure, a regular test suite
|
to write a library for a queue data structure, a regular test suite
|
||||||
ensures that it deals correctly with enqueue and dequeue for small
|
ensures that it deals correctly with enqueue and dequeue for small queues,
|
||||||
queues, including empty ones; a stress test suite for the same library
|
including empty ones; a stress test suite for the same library would run
|
||||||
would run the queue operations on a variety of queue sizes, including very
|
the queue operations on a variety of queue sizes, including very large
|
||||||
large queues of say 10,000 elements.
|
queues of say 10,000 elements.
|
||||||
|
|
||||||
Stress tests don't normally have an expected output, so they never
|
Stress tests don't normally have an expected output, so they never
|
||||||
"pass". The practice of writing stress tests exposes implementation flaws
|
"pass". The practice of writing stress tests exposes implementation flaws
|
||||||
or provides comparative data to be used when choosing between two
|
or provides comparative data to be used when choosing between two
|
||||||
APIs. Just writing them and keeping them around reminds us that things can
|
APIs. Just writing them and keeping them around reminds us that things can
|
||||||
go bad and we can detect when performance degrades through some other
|
go bad and we can detect when performance degrades through some other
|
||||||
door. Most importantly, a stress test suite may reveal that your code
|
door. Most importantly, a stress test may reveal that your code isn't
|
||||||
isn't implementing an algorithm with the expected O(.) running
|
implementing an algorithm with the expected O(.) running time. Finding out
|
||||||
time. Finding out that much alone is useful. If you can't think of an
|
that much alone is useful. If you can't think of an improvement, just
|
||||||
improvement, just document the weakness in the external library and move
|
document the weakness in the external library and move on.
|
||||||
on.
|
|
||||||
|
|
||||||
We are not perfectionists. We produce reasonable software.
|
And as you read on, keep in mind that we are not perfectionists. We produce
|
||||||
|
reasonable software.
|
||||||
|
|
||||||
|
@nested[#:style 'inset]{When you fix a bug, make sure to commit (1) the
|
||||||
|
code delta, (2) the new test case, and (3) the revised docs (if
|
||||||
|
applicable) in one batch.}
|
||||||
|
|
|
@ -3,10 +3,25 @@
|
||||||
; things to be shared among all sections of the style guide
|
; things to be shared among all sections of the style guide
|
||||||
|
|
||||||
(require (for-label racket)
|
(require (for-label racket)
|
||||||
scribble/manual)
|
scribble/manual
|
||||||
|
scribble/struct
|
||||||
|
(only-in scribble/core table-columns style)
|
||||||
|
scribble/html-properties
|
||||||
|
racket/list)
|
||||||
|
|
||||||
(provide (for-label (all-from-out racket))
|
(provide (for-label (all-from-out racket))
|
||||||
(all-from-out scribble/manual))
|
(all-from-out scribble/manual))
|
||||||
|
|
||||||
|
(provide compare)
|
||||||
|
|
||||||
; (provide good)
|
;; compare: two elements,
|
||||||
|
|
||||||
|
(define (compare stuff1 stuff2)
|
||||||
|
(define stuff (list (list stuff1) (list stuff2)))
|
||||||
|
(define space (style #f (list (attributes '((width . "500") (valign . "top"))))))
|
||||||
|
(table
|
||||||
|
(style #f
|
||||||
|
(list
|
||||||
|
(attributes '((border . "1") (cellpadding . "10")))
|
||||||
|
(table-columns (make-list (length stuff) space))))
|
||||||
|
(apply map (compose make-flow list) stuff)))
|
||||||
|
|
|
@ -3,15 +3,27 @@
|
||||||
@(require "shared.rkt")
|
@(require "shared.rkt")
|
||||||
|
|
||||||
@; -----------------------------------------------------------------------------
|
@; -----------------------------------------------------------------------------
|
||||||
@(define rurl "http://docs.racket-lang.org/reference/index.html?q=racket/base")
|
@(define (rurl x) (format "http://docs.racket-lang.org/reference/index.html?q=racket/~a" x))
|
||||||
@(define (rkt) @hyperlink[rurl]{racket})
|
@(define (rkt) @hyperlink[(rurl "")]{racket})
|
||||||
@(define (rkt/base) @hyperlink[rurl]{racket/base})
|
@(define (rkt/base) @hyperlink[(rurl "base")]{racket/base})
|
||||||
|
@(define (rkt/gui) @hyperlink[(rurl "gui")]{racket/gui})
|
||||||
|
|
||||||
@title{Some Performance Hints}
|
@title{Some Performance Hints}
|
||||||
|
|
||||||
Use @rkt/base[] instead of @rkt[] for any library that others may use
|
When you write a module, you first pick a language. In Racket you can
|
||||||
eventually. For all other modules, use @rkt[].
|
choose a lot of languages. The most important choice concerns @rkt/base[]
|
||||||
|
vs @rkt[].
|
||||||
|
|
||||||
|
If you are writing a script, try to use @rkt/base[]. The @rkt/base[]
|
||||||
|
language loads significantly faster than the @rkt[] language because it is
|
||||||
|
significantly smaller than the @rkt[].
|
||||||
|
|
||||||
|
If your module is intended as a library, stick to @rkt/base[]. That way
|
||||||
|
script writers can use it without incurring the overhead of loading all of
|
||||||
|
@rkt[] unknowingly.
|
||||||
|
|
||||||
|
Conversely, you should use @rkt[] (or even @rkt/gui[]) when you just want a
|
||||||
|
convenient language to write some program. The @rkt[] language comes with
|
||||||
|
almost all the batteries, and @rkt/gui[] adds the rest of the GUI base.
|
||||||
|
|
||||||
|
|
||||||
The @rkt/base[] language loads significantly faster than the @rkt[]
|
|
||||||
language and is also significnatly smaller. Conversely, it is much more
|
|
||||||
convenient to program with @rkt[] than @rkt/base[].
|
|
||||||
|
|
|
@ -8,31 +8,50 @@
|
||||||
@; -----------------------------------------------------------------------------
|
@; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
Since 1995 PLT has grown from a handful of people who worked on/with the
|
Since 1995 PLT has grown from a handful of people who worked on/with the
|
||||||
repository to three dozen and more. In addition, Racket is an open source
|
repository to three dozen and more. This growth naturally implies a lot
|
||||||
project, meaning other people study the code in the repository and use it
|
of learning on our side and the introduction of inconsistencies. It is
|
||||||
as an implicit guide to Racket programming. To manage the growth of the PLT
|
time to leverage the former and to start eliminating the latter. Doing
|
||||||
developer basis and to showcase good Racket coding, every contribution
|
so will help us, the developers, and our users, who use the open source
|
||||||
should satisfy certain basic criteria.
|
code in our repository as an implicit guide to Racket programming.
|
||||||
|
|
||||||
This document spells out these criteria, and it is also a call for
|
To manage the growth of PLT and to showcase good Racket coding, we need
|
||||||
improvements and suggestions for additional criteria. Like code, this
|
rules that govern the contributions to the code base. This document
|
||||||
document lives and benefits from the "many pairs of eyes" effect of open
|
spells out some basic criteria. They cover a range of topics, from basic
|
||||||
source. Code should be viewed by more than one person; a second person is
|
work (commit) habits to small syntactic ideas like indentations and
|
||||||
likely to catch mistakes and problems and hidden bugs. This document is
|
naming. The major goal is to achieve some level of consistency across
|
||||||
meta-code, and the same ideas apply. If you have suggestions, contact the
|
the different portions of the code base so that everyone who opens files
|
||||||
authors via email.
|
should easily find his way around.
|
||||||
|
|
||||||
@bold{Note} We understand that some of the files in the code base do not
|
Many pieces of the code base don't live up to our suggestions yet. Here
|
||||||
live up to these standards. Help us improve these files. If you need to
|
is how we get started. We encourage everyone to look over the commit
|
||||||
edit and understand a file that fails in some ways, take the time to
|
messages. If you see problems with the code deltas, let the committer
|
||||||
reorganize it properly as soon as the comprehension step takes longer than
|
know. If you see a bug fix without docs and tests, let the committer
|
||||||
a few minutes. Because if understanding takes a lot of time, it is likely
|
know. Code should be viewed by more than one person because a second
|
||||||
that the file isn't maintainable. Whoever touches the file next will be
|
person is likely to catch logical mistakes, performance problems, and
|
||||||
grateful.
|
unintended effects. In the past Eli has done a great job catching
|
||||||
|
problems; now everyone is asked to do so.
|
||||||
|
|
||||||
|
Also, help us improve the existing files. If you need to edit and
|
||||||
|
understand an imperfect file, take the time to fix some of it as soon as
|
||||||
|
comprehending the file takes longer than a few minutes. After all, if
|
||||||
|
the inconsistencies throw you off for that much time, others are likely
|
||||||
|
to have the same problems. If you help fixing it, we reduce future
|
||||||
|
maintenance time. In other words, whoever touches the file next will be
|
||||||
|
grateful to you.
|
||||||
|
|
||||||
|
@bold{Request} This document isn't complete and it isn't perfect. In other
|
||||||
|
words, it is also a call for improvements and suggestions. If you have
|
||||||
|
ideas, contact the first author via email.
|
||||||
|
|
||||||
|
@bold{Note} If the style guide doesn't suit your personal style because
|
||||||
|
you grew up on something different, fine. Use your @emph{personal style}
|
||||||
|
for your @emph{personal programs}, but do use this style guide when you
|
||||||
|
create code for the PLT code base.
|
||||||
|
|
||||||
@; -----------------------------------------------------------------------------
|
@; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
@include-section["correct-maintain-speed.scrbl"]
|
@include-section["correct-maintain-speed.scrbl"]
|
||||||
@include-section["some-performance.scrbl"]
|
@include-section["some-performance.scrbl"]
|
||||||
@include-section["size.scrbl"]
|
@include-section["size.scrbl"]
|
||||||
|
@include-section["constructs.scrbl"]
|
||||||
@include-section["textual.scrbl"]
|
@include-section["textual.scrbl"]
|
||||||
|
|
|
@ -33,22 +33,24 @@ that it follows DrRacket's indentation style.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
@racketmod[#:file
|
@compare[
|
||||||
@tt{good}
|
@racketmod[#:file
|
||||||
racket
|
@tt{good}
|
||||||
|
racket
|
||||||
|
|
||||||
(if (positive? x)
|
(if (positive? (rocket-x r))
|
||||||
(send rocket-object launch)
|
(launch r)
|
||||||
(redirect (- x)))
|
(redirect (- x)))
|
||||||
]
|
]
|
||||||
|
|
||||||
@racketmod[#:file
|
@racketmod[#:file
|
||||||
@tt{bad}
|
@tt{bad}
|
||||||
racket
|
racket
|
||||||
|
|
||||||
(if (positive? x)
|
(if (positive? (rocket-x r))
|
||||||
(send rocket-object launch)
|
(launch r)
|
||||||
(redirect (- x)))
|
(redirect (- x)))
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@margin-note{We need more of these rules}
|
@margin-note{We need more of these rules}
|
||||||
|
@ -60,32 +62,35 @@ Next to indentation, proper line breaks are critical.
|
||||||
|
|
||||||
For an @scheme[if] expression, put each alternative on a separate line.
|
For an @scheme[if] expression, put each alternative on a separate line.
|
||||||
|
|
||||||
|
@compare[
|
||||||
@racketmod[#:file
|
@racketmod[#:file
|
||||||
@tt{good}
|
@tt{good}
|
||||||
racket
|
racket
|
||||||
|
|
||||||
(if (positive? x)
|
(if (positive? x)
|
||||||
(send rocket-object launch)
|
(launch r)
|
||||||
(redirect (- x)))
|
(redirect (- x)))
|
||||||
]
|
]
|
||||||
|
|
||||||
@racketmod[#:file
|
@racketmod[#:file
|
||||||
@tt{bad}
|
@tt{bad}
|
||||||
racket
|
racket
|
||||||
|
|
||||||
(if (positive? x) (send rocket-object launch)
|
(if (positive? x) (launch r)
|
||||||
(redirect (- x)))
|
(redirect (- x)))
|
||||||
]
|
]
|
||||||
|
]
|
||||||
|
|
||||||
Each definition and each local definition deserves at least one line.
|
Each definition and each local definition deserves at least one line.
|
||||||
|
|
||||||
|
@compare[
|
||||||
@racketmod[#:file
|
@racketmod[#:file
|
||||||
@tt{good}
|
@tt{good}
|
||||||
racket
|
racket
|
||||||
|
|
||||||
(define (start-reactor x)
|
(define (launch x)
|
||||||
(define width (* 10 x))
|
(define wdt (* 10 x))
|
||||||
(define height (* 3 x))
|
(define hgt (* 3 x))
|
||||||
...)
|
...)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -93,10 +98,11 @@ racket
|
||||||
@tt{bad}
|
@tt{bad}
|
||||||
racket
|
racket
|
||||||
|
|
||||||
(define (start-reactor x)
|
(define (launch x)
|
||||||
(define width (* 10 x)) (define height (* 3 x))
|
(define wdt (* 10 x)) (define hgt (* 3 x))
|
||||||
...)
|
...)
|
||||||
]
|
]
|
||||||
|
]
|
||||||
@margin-note{We need more of these rules}
|
@margin-note{We need more of these rules}
|
||||||
|
|
||||||
@; -----------------------------------------------------------------------------
|
@; -----------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue
Block a user