[Style] @compare plus some rules on use of constructs

This commit is contained in:
Matthias Felleisen 2011-02-20 14:32:31 -05:00 committed by Eli Barzilay
parent 75edb5a064
commit bceca4b22f
6 changed files with 216 additions and 84 deletions

View 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)))))
]
]

View File

@ -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.}

View File

@ -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)))

View File

@ -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[].

View File

@ -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"]

View File

@ -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}
@; ----------------------------------------------------------------------------- @; -----------------------------------------------------------------------------