diff --git a/collects/scribblings/style/constructs.scrbl b/collects/scribblings/style/constructs.scrbl index fe3bd384fe..fe25b97c62 100644 --- a/collects/scribblings/style/constructs.scrbl +++ b/collects/scribblings/style/constructs.scrbl @@ -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))))) +] +] diff --git a/collects/scribblings/style/correct-maintain-speed.scrbl b/collects/scribblings/style/correct-maintain-speed.scrbl index 7046b95734..61231d83af 100644 --- a/collects/scribblings/style/correct-maintain-speed.scrbl +++ b/collects/scribblings/style/correct-maintain-speed.scrbl @@ -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 diff --git a/collects/scribblings/style/some-performance.scrbl b/collects/scribblings/style/some-performance.scrbl index db08b4b262..f587d4d7d8 100644 --- a/collects/scribblings/style/some-performance.scrbl +++ b/collects/scribblings/style/some-performance.scrbl @@ -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. - - diff --git a/collects/scribblings/style/textual.scrbl b/collects/scribblings/style/textual.scrbl index 680f414d8c..19553af1a2 100644 --- a/collects/scribblings/style/textual.scrbl +++ b/collects/scribblings/style/textual.scrbl @@ -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%] diff --git a/collects/scribblings/style/unit.scrbl b/collects/scribblings/style/unit.scrbl index c69b64c46f..ce89cf75f6 100644 --- a/collects/scribblings/style/unit.scrbl +++ b/collects/scribblings/style/unit.scrbl @@ -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.