On this page:
Basics
Testing & Randomness
6.1.1

The Style

Basics

In addition to following the design recipe, all code must adhere to the following basic style guidelines:

  1. Organize your program top-down, regardless of how you actual work through your wish list. The phrase "top down" means that project files consist of a general purpose statement, a data definition and a constant definition section, a main function, followed by sections for handler functions, and wrapped up by general utility functions.

    Separate distinct sections of your program with dashed lines that are exactly 80 columns wide.

  2. Arrange your functions according to the design recipe. Place short and informative test cases between the signature and the function definition. Place any additional long or complicated test cases below the function or at the bottom of your program in a separate test section.

  3. Use (function, constant, parameter) names that make sense with respect to the problem.

  4. Design concise functions. No function should span more than five to eight lines. If it does, reconsider your interpretation of the "one task, one function" guideline.

    If a function consumes a complex argument and must perform several different tasks, design several functions that all consume the argument, produce a value in the same data collection, and hand it over to the next function:

      ;; ------------------------------------------------- GOOD

      ;; HHState -> HHState

      (define (hungry-henry-action-per-clock-tick hh-world-state)

        (bump-tick-value

          (eat-all-cup-cakes-in-reach

             (move-hungry-henry-closer-to-wp hh-world-state))))

      

      ;; HHState -> HHState

      (define (bump-tick-value hh-world-state) ...)

      

      ;; HHState -> HHState

      (define (eat-all-cup-cakes-in-reach hh-world-state) ...)

      

      ;; HHState -> HHState

      (define (move-hungry-henry-closer-to-wp hh-world-state) ...)

    Piling the code from these three functions into the first one would yield a confusing mess.

  5. Keep lines narrow. No line should span more than 80 characters. See bottom right of DrRacket or use its edit -> find longest line menu.

    Break lines at those points suggested by HtDP.

  6. Programs use the parentheses style displayed in HtDP and this web page:

      ;; ------------------------ GOOD

      (define (f l)

        (cond [(empty? l) 0]

              [else (f (rest l))]))

      ;; ------------------------ BAD

      (define (f l)

        (cond [(empty? 1) 0]

              [else (f (rest l))]

         )

       )

    The dangling parentheses in the second code excerpt are considered extremely bad style. You will lose all style points for using it even once.

Not observing these very basic guidelines leads to unreadable code and to loss of points.

Testing & Randomness

When you design functions that create (pseudo-)random results, do not give up on testing. To make this guide concrete, consider this example:

; Number Number -> Posn
; create a Posn located at random points in [0,width) x [0,height)
(define (create-cupcake width height)
  (make-posn (random width) (random height)))
Just because this function returns a random Posn does not mean you cannot test something about the relationship between its input(s) and output.

At a minimum, your tests can validate that the function always produces a Posn:
(check-expect (posn? (create-cupcake 100 200)) true)
(check-expect (posn? (create-cupcake 20 1000)) true)
Although this simplistic test may appear to be silly, it ensures that the function runs for some examples (e.g., no typos, primitives called correctly).

One step up your tests can include a property checker that confirms the purpose statement:
; Number Number Posn -> Boolean
; property tester
(define (in-range? width height p)
  (and (<= 0 (posn-x p) width)
       (<= 0 (posn-y p) height)))
 
(check-expect (in-range? 100 200 (create-cupcake 100 200)) true)
(check-expect (in-range? 20 1000 (create-cupcake 20 1000)) true)
The introduction of a 2-line property checker for a 1-line function may seem overkill, but keep in mind that we purposefully keep the basic example small.

As your functions get more complicated, deploy "loops" to check entire lists, trees, forests and other complex, of randomly generated data structures:Note how one ignores its argument. Still, why would make-list in lieu of build-list not produce the correct result here?
; N -> [List-of Posn]
; create the given number of cupcakes
(define (create-many-cupcakes n)
  (local ((define (one i)
            (create-cupcake SCENE-WIDTH SCENE-HEIGHT)))
    (build-list n one)))
For a function like this, your property checker must use andmap to check all generated Posns:
; Posn -> Boolean
; property tester
(define (one? p)
  (in-range? SCENE-WIDTH SCENE-HEIGHT p))
 
(check-expect (andmap one? (create-many-cupcakes 100)) true)