[Style] first draft wrapped up

This commit is contained in:
Matthias Felleisen 2011-07-28 16:49:49 -04:00 committed by Eli Barzilay
parent 11a04cd6fa
commit ad583ff720
5 changed files with 291 additions and 5 deletions

View File

@ -0,0 +1,9 @@
#lang scribble/base
@title{Acknowledgment}
The rules borrow from many sources. While many helped the authors survive
their own coding experience, the first author also conducted focus sessions
with the members of PLT and these sessions produced many insights about
coding style. Jacob Matthews took the time to write up his thoughts on
testing, and they are much appreciated.

View File

@ -39,6 +39,32 @@ racket
]
]
@compare[
@racketmod[#:file
@tt{good}
racket
(define-syntax (increment! stx)
(syntax-case stx ()
[(_ s sn fn i)
(with-syntax ([w (r #'s)])
(define g (ff #'sn #'w))
...)]))
]
@; -----------------------------------------------------------------------------
@racketmod[#:file
@tt{bad}
racket
(define-syntax (increment! stx)
(syntax-case stx ()
[(_ s sn fn i)
(with-syntax ([w (r #'s)])
(let ([g (ff #'sn #'w)])
...))]))
]
]
@; -----------------------------------------------------------------------------
@section{Conditionals}
@ -81,7 +107,6 @@ Of course you should also favor @scheme[cond] (and its relatives) over
Keep expressions small. Name intermediate results.
@compare[
@racketmod[#:file
@tt{good}
@ -101,3 +126,152 @@ racket
(sqr (posn-y p)))))
]
]
@; -----------------------------------------------------------------------------
@section{Structs vs Lists}
Use @racket[struct]s when you represent a combination of a fixed number of
values. Don't use lists.
@; -----------------------------------------------------------------------------
@section{Lambda vs Define}
While nobody denies that @racket[lambda] is cute, @racket[define]d
functions have names that tell you what they compute and that helps
accelerate reading.
@compare[
@racketmod[#:file
@tt{good}
racket
(define (process f)
(define (complex-step x)
... 10 lines ...)
(map complext-step
(to-list f)))
]
@; -----------------------------------------------------------------------------
@racketmod[#:file
@tt{bad}
racket
(define (process f)
(map (lambda (x)
... 10 lines ...)
(to-list f)))
]
]
@; -----------------------------------------------------------------------------
@section{List Traversals}
With the availability of @racket[for/fold], @racket[for/list],
@racket[for/vector], and friends, programming with for @racket[for] loops
has become just as functional as programming with @racket[map] and
@racket[foldr]. With @racket[for*] loops, filter, and termination clauses
in the iteration specification, these loops are also far more concise than
explicit traversal combinators. And with @racket[for] loops, you can
decouple the traversal from lists.
@compare[
@;%
@(begin
#reader scribble/comment-reader
[racketmod #:file
@tt{good}
racket
;; [Sequence X] -> Number
(define (sum-up s)
(for/fold ((sum 0)) ((x s))
(+ sum x)))
;; examples:
(sum-up '(1 2 3))
(sum-up #(1 2 3))
(sum-up (open-input-string "1 2 3"))
])
@; -----------------------------------------------------------------------------
@;%
@(begin
#reader scribble/comment-reader
[racketmod #:file
@tt{bad}
racket
;; [Listof X] -> Number
(define (sum-up s)
(for/fold ((sum 0)) ((x s))
(+ sum x)))
;; example:
(sum-up '(1 2 3))
])
]
Note: @racket[for] traversals of user-defined sequences tend to be
slow. If performance matters in these cases, you may wish to fall back on
your own traversal functions.
@; -----------------------------------------------------------------------------
@section{Functions vs Macros}
Use functions when possible; do not introduce macros.
@compare[
@racketmod[#:file
@tt{good}
racket
...
;; Message -> String
(define (message-name msg)
(first (second msg)))
]
@; -----------------------------------------------------------------------------
@racketmod[#:file
@tt{bad}
racket
...
;; Message -> String
(define-syntax-rule
(message-name msg)
;; ===>
(first (second msg)))
]
]
@; -----------------------------------------------------------------------------
@section{Parameters}
If you need to set a parameter, use @racket[parameterize]:
@compare[
@racketmod[#:file
@tt{good}
racket
...
;; String OutputPort -> Void
(define (send-to msg op)
(parameterize
((current-output-port op))
(format-and-display msg))
(record-message-in-log msg))
]
@; -----------------------------------------------------------------------------
@racketmod[#:file
@tt{bad}
racket
...
;; String OutputPort -> Void
(define (send-to msg op)
(define cp
(current-output-port))
(current-output-port op)
(format-and-display msg)
(current-output-port cp)
(record-message-in-log msg))
]
]

View File

@ -59,3 +59,4 @@ Also, we encourage everyone to look over the commit messages. If you see
@include-section["constructs.scrbl"]
@include-section["textual.scrbl"]
@include-section["branch-and-commit.scrbl"]
@include-section{acknowledgment.scrbl}

View File

@ -220,8 +220,46 @@ These lines help both writers and readers to orient themselves in a file.
Use meaningful names. The Lisp convention is to use full English words
separated by dashes. Racket code benefits from the same convention.
@compare[
@;%
@(begin
#reader scribble/comment-reader
[racketmod #:file
@tt{good}
racket
render-game-state
send-message-to-client
traverse-forest
])
@; -----------------------------------------------------------------------------
@;%
@(begin
#reader scribble/comment-reader
[racketmod #:file
@tt{bad}
racket
rndr-st
sendMessageToClient
trvrs-frst
])
]
@;
If you cannot give a unit a good name, consider the possibility that it
isn't a proper unit of code.
You may use dots between parts of names, e.g., @racket[p.x] to suggest that
field @racket[x] is selected from struct @racket[p].
In addition to regular alphanumeric characters, Racketeers use a few
special characters.
special characters by convention, and these characters indicate something
about the name:
@;column-table[ @col[? ! "@" ^ %] @col[1 2 3 4 5] @col[1 2 3 4 5] ]
@ -233,3 +271,7 @@ special characters.
@row["@" units a@]
@row[^ signatures a^]
]
Some Racketeers use the suffix of the name to suggest type(-like)
information, e.g., @racket[body-xexpr] or @racket[body-xml]. For such uses,
a colon is commonly found, e.g., @racket[p:posn] or @racket[p:pair-of-numbers].

View File

@ -27,14 +27,55 @@ If a unit of code looks incomprehensible, it is probably too large. Break
division; consider alternatives.
@; -----------------------------------------------------------------------------
@section{Module Interfaces}
The purpose of a module is to provide some services.
@section{Modules and their Interfaces}
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.
In order to understand a module's services, organize the module in three
sections below the purpose statement: its imports, its exports, and its
implementation:
@;%
@(begin
#reader scribble/comment-reader
(racketmod #:file
@tt{good}
racket/base
;; the module implements a tv server
(require 2htdp/universe htdp/image)
(provide
tv-launch
tv-client)
(define (tv-launch)
(universe ...))
(define (tv-client)
(big-bang ...))
))
@;%
If you choose to use @racket[provide/contract], define auxiliary concepts
related to the contracts between the @racket[require] and the
@racket[provide] sections. A test suite section---if located within the
module---should come at the every end, including its specific
dependencies, i.e., @racket[require] specifications.
@; -----------------------------------------------------------------------------
@subsection{Require}
With @racket[require] specifications at the top of the module, you let
every reader know what is needed to understand the module. The
@racket[require] specification nails down the external dependencies.
@; -----------------------------------------------------------------------------
@subsection{Provide}
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:
@ -150,12 +191,29 @@ Consider using @scheme[provide/contract] for module interfaces.
contracts (constructor predicates that check only a tag); they tend to
cost relatively little.
@subsection{Uniformity of Interface}
Pick a consistency rule for the names of your functions, classes, and
methods. Stick to it. For example, you may wish to prefix all exported
names with the same word, say @racket[syntax-local].
Pick a consistency rule the parameters of your functions and methods. Stick
to it. For example, if your module implements an abstract data type (ADT),
all functions on the ADT should consume the ADT-argument first or last.
@subsection{Sections}
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
for sections so that readers can easily understand which part of a module
implements which service. Alternatively, consider using the large letter
chapter headings in DrRacket to label the sections of a module.
With @racketmodname[rackunit], test suites can be defined within the
module using @racket[define/provide-test-suite]. If you do so, locate the
test section at the end of the module and @racket[require] the necessary
pieces for testing specifically for the test suites.
@; -----------------------------------------------------------------------------
@section{Classes & Units}
@ -163,3 +221,5 @@ Finally, a module consists of sections. It is good practice to separate the
@section{Functions & Methods}
If your function or method consumers more than two parameters, consider
keyword arguments so that call sites can easily be understood.