[Style] first round of edits

This commit is contained in:
Matthias Felleisen 2011-07-26 22:19:34 -04:00 committed by Eli Barzilay
parent b14b90655c
commit 11a04cd6fa
5 changed files with 124 additions and 127 deletions

View File

@ -12,10 +12,10 @@ 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.
Racket comes with quite a few definitional constructs, including
@scheme[let], @scheme[let*], @scheme[letrec], and @scheme[define]. Except
for the last one, definitional construct increase the indentation level. We
therefore request that you favor @scheme[define] when feasible.
@compare[
@racketmod[#:file
@ -55,10 +55,10 @@ racket
(cond
[(empty? l) true]
[else
(define fst (first l))
(define rst (rest l))
(and (flat-rate fst)
(curved fst (chk rst)))])
(define f (fir l))
(define r (rest l))
(and (flat-rate f)
(curved f (chk r)))])
]
@racketmod[#:file
@tt{bad}
@ -66,12 +66,38 @@ racket
(if (empty? l)
true
(let ([fst (first l)]
[rst (rest l)])
(and (flat-rate fst)
(curved fst (chk rst)))))
(let ([f (fir l)]
[r (rest l)])
(and (flat-rate f)
(curved f (chk r)))))
]
]
Of course you should also favor @scheme[cond] (and its relatives) over
@scheme[if] to match the shape of the data definition.
@; -----------------------------------------------------------------------------
@section{Expressions}
Keep expressions small. Name intermediate results.
@compare[
@racketmod[#:file
@tt{good}
racket
(define (distance0 p)
(define x (posn-x p))
(define y (posn-y p))
(sqrt (+ (sqr x) (sqr y))))
]
@; -----------------------------------------------------------------------------
@racketmod[#:file
@tt{bad}
racket
(define (distance0 p)
(sqrt
(+ (sqr (posn-x p))
(sqr (posn-y p)))))
]
]

View File

@ -122,13 +122,7 @@ Having said that, the production of a system like Racket occasionally
@; -----------------------------------------------------------------------------
@section{Speed}
Making code fast is an endless task.
Making code @emph{reasonably} fast is the goal.
It is especially the goal for all pieces of the code base that are reused
elsewhere. Write them using @rkt/base[] so that they don't affect the
load-time for scripts. See the next section.
Making code fast is an endless task. Making code @emph{reasonably fast} is the goal.
As with correctness, performance demands some ``testing.'' At a minimum,
exercise your code on some reasonably large inputs. Add a file to the test

View File

@ -10,9 +10,9 @@ When you write a module, you first pick a language. In Racket you can
choose a lot of languages. The most important choice concerns @rkt/base[]
vs @rkt[].
If you are writing a script, try using @rkt/base[]. The @rkt/base[]
language loads significantly faster than the @rkt[] language because it is
much smaller than the @rkt[].
For scripts, use @rkt/base[]. The @rkt/base[] language loads significantly
faster than the @rkt[] language because it is much 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
@ -21,5 +21,3 @@ If your module is intended as a library, stick to @rkt/base[]. That way
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.

View File

@ -26,29 +26,25 @@ your code.
@racketmod[#:file
@tt{good}
racket
@;%
(define (conversion f)
(* 5/9 (- f 32)))
]
@racketmod[#:file
@tt{really bad}
racket
@filebox[@tt{really bad}
@codeblock{#lang racket
(define (conversion f)
(* 5/9 (- f 32)
)
)
]
}]
]
You are allowed to place all closing parenthesis on a line by itself at the
end of long sequences, be those definitions or pieces of data.
@compare[
@racketmod[#:file
@tt{acceptable}
racket
@filebox[@tt{acceptable}
@codeblock{#lang racket
(define modes
'(edit
help
@ -56,12 +52,10 @@ end of long sequences, be those definitions or pieces of data.
test
trace
step
)) ;; <--- bug in scribble: the last two are on their own line
]
@racketmod[#:file
@tt{also acceptable}
racket
))
}]
@filebox[@tt{also acceptable}
@codeblock{#lang racket
(define turn%
(class object%
(init-field state)
@ -73,8 +67,8 @@ end of long sequences, be those definitions or pieces of data.
(define/public (is-placable? place)
(send state legal? place))
)) ;; <--- bug in scribble: the last two are on their own line
]
))
}]
]
@; -----------------------------------------------------------------------------
@ -85,9 +79,7 @@ on. So use DrRacket's indentation style. Here is what this means.
@nested[#:style 'inset]{
For every file in the repository, DrRacket's "indent all" functions leaves
the file alone.}
That's all there is to it. @margin-note{See @secref{correctness}. If you
really believe that DrRacket indents some construct improperly, submit a
bug report. When the bug report is closed, the discussion is finished.}
That's all there is to it.
If you prefer to use some other editor (emacs, vi/m, etc), program it so
that it follows DrRacket's indentation style.
@ -114,8 +106,6 @@ Examples:
]
]
@margin-note{We need more of these rules}
@; -----------------------------------------------------------------------------
@section{Line Breaks}
@ -150,8 +140,8 @@ Each definition and each local definition deserves at least one line.
racket
(define (launch x)
(define wdt (* 10 x))
(define hgt (* 3 x))
(define w 9)
(define h 33)
...)
]
@ -160,7 +150,7 @@ racket
racket
(define (launch x)
(define wdt (* 10 x)) (define hgt (* 3 x))
(define w 9) (define h 33)
...)
]
]
@ -189,7 +179,7 @@ racket
@tt{bad}
racket
(composition ufo-with-flames
(composition ufo
10 v-delta bg)
]]
@ -203,10 +193,8 @@ racket
10 10
(rectangle 10 100 "solid" "red"))
]
In this case, the two arguments on line 2 are both short and conceptually
related.
@margin-note{We need more of these rules}
In this case, the two arguments on line 2 are both conceptually
related and short.
@; -----------------------------------------------------------------------------
@section{Line Width}
@ -235,12 +223,10 @@ separated by dashes. Racket code benefits from the same convention.
In addition to regular alphanumeric characters, Racketeers use a few
special characters.
@column-table[
@col[? ! "@" ^ %]
@col[1 2 3 4 5]
@col[1 2 3 4 5] ]
@;column-table[ @col[? ! "@" ^ %] @col[1 2 3 4 5] @col[1 2 3 4 5] ]
@row-table[
@row[symbol kind example]
@row[? predicates boolean?]
@row[! setters set!]
@row[% classes a%]

View File

@ -4,20 +4,44 @@
@title{Units of Code}
@; -----------------------------------------------------------------------------
@section{Size Matters}
Keep units of code small. Keep modules, classes, functions and methods small.
A module of 10,000 lines of code is too large. A module of 1,000 lines is
tolerable. A module of 500 lines of code has the right size.
One module should usually a class and its auxiliary functions, which in
turn determines the length of a good-sized class.
And a function (method) of more than 66 lines is barely acceptable. For
many years we had a limited syntax transformation language that forced
people to create @emph{huge} functions. This is no longer the case, so
consider this rule universal.
If a unit of code looks incomprehensible, it is probably too large. Break
it up. To bring across what the pieces compute, implement or serve, use
meaningful names; see @secref{names}. If you can't come up with a good
name for such pieces, you are probably looking at the wrong kind of
division; consider alternatives.
@; -----------------------------------------------------------------------------
@section{Module Interfaces}
The purpose of a module is to provide some services. @margin-note{The
modules we discuss in this section coincide with files.}
The purpose of a module is to provide some services.
@centerline{Equip a module with a short purpose statement.}
@;
Often ``short'' means one line; occasionally you may need several lines.
Its interface describes which services it provides, and its body implements
the services. At least in principle others shouldn't have to read the
implementation ever, but it is quite likely that they have to read the
interface.
A module's interface describes the services it provides; its body
implements these services. Others have to read the interface if the
external documentation doesn't suffice:
@centerline{Place the interface at the top of the module.}
@;
This helps people find the relevant information quickly.
@compare[
@;%
@ -33,38 +57,27 @@ Its interface describes which services it provides, and its body implements
(require "game-basics.rkt")
(provide
;; Strtgy = GameState -> Action
;; Stgy = State -> Action
;; Strtgy
;; a person's strategy
;; Stgy
;; people's strategy
human-strategy
;; Strtgy
;; a complete tree traversal
ai-1-strategy
;; Stgy
;; complete tree traversal
ai-strategy)
;; Strtgy
;; alpha-beta pruning traversal
ai-2-strategy)
;; ------------------------------------------------------------------
(define (general-strategy p)
(define (general p)
... )
;; ------------------------------------------------------------------
... some 100 lines ...
(define human-strategy
(general-strategy create-gui))
(general create-gui))
;; ------------------------------------------------------------------
... some 100 lines ...
(define ai-1-strategy
(general-strategy traversal))
(define ai-strategy
(general traversal))))
;; ------------------------------------------------------------------
... some 100 lines ...
(define ai-2-strategy
(general-strategy alpha-beta))))
@(begin
#reader scribble/comment-reader
(racketmod #:file
@ -76,51 +89,36 @@ Its interface describes which services it provides, and its body implements
(require "game-basics.rkt")
;; Strtgy = GameState -> Action
;; Stgy = State -> Action
;; ------------------------------------------------------------------
(define (general-strategy p)
(define (general p)
... )
... some 100 lines ...
;; ------------------------------------------------------------------
(provide
;; Strtgy
;; Stgy
;; a person's strategy
human-strategy)
(define human-strategy
(general-strategy create-gui))
(general create-gui))
... some 100 lines ...
;; ------------------------------------------------------------------
(provide
;; Strtgy
;; Stgy
;; a complete tree traversal
ai-1-strategy)
ai-strategy)
(define ai-1-strategy
(general-strategy traversal))
(define ai-strategy
(general traversal))
... some 100 lines ...
;; ------------------------------------------------------------------
(provide
;; Strtgy
;; alpha-beta pruning traversal
ai-2-strategy)
(define ai-2-strategy
(general-strategy alpha-beta))))
))
]
As you can see from this comparison, an interface shouldn't just be a
@scheme[provide] with a list of names. Each identifier should come with a
purpose statement.
@bold{Note} Following this documentation guideline is most applicable to
modules that are a component of a large application. It is less relevant
for a module in the Racket that comes with a full-fledged description in
the guide.
As you can see from this comparison, an interface shouldn't just
@scheme[provide] a list of names. Each identifier should come with a
purpose statement. Type-like explanations of data also belong into a
@scheme[provide] specification.
While a one-line purpose statement for a function is usually enough, syntax
should come with a description of the grammar clause it introduces
@ -145,10 +143,12 @@ racket
define-strategy)
))]
If the performance of your module doesn't suffer too much from contracts,
consider using @scheme[provide/contract]. The use of type-like contracts
(constructor predicates that check only a tag) impose a tolerable overhead
and still discover simple mistakes.
Consider using @scheme[provide/contract] for module interfaces.
Although contracts affect the performance of your module's services, they
provide a specification and they will help you with the inevitable bug
reports you will receive. You should definitely consider type-like
contracts (constructor predicates that check only a tag); they tend to
cost relatively little.
Finally, a module consists of sections. It is good practice to separate the
sections with comment lines. You may want to write down purpose statements
@ -157,16 +157,9 @@ Finally, a module consists of sections. It is good practice to separate the
chapter headings in DrRacket to label the sections of a module.
@; -----------------------------------------------------------------------------
@section{Functions & Methods, Classes & Units}
@section{Classes & Units}
@; -----------------------------------------------------------------------------
@section{Size Matters}
@section{Functions & Methods}
Keep functions small. Keep classes small. Keep units small. Keep modules small.
Anytime a unit of code looks incomprehensible, it is probably too
large. Break it up into smaller units. To bring across what these smaller
units compute, implement or serve, use meaningful names; see
@secref{names}. Conversely, if you can't come up with a good name for such
units, you are probably looking at the wrong kind of division; consider
alternatives.