On this page:
1 General Style Requirements
2 Racket Style Requirements
3 Variable and Function Names
4 Program Design Recipe Style Guidelines
4.1 Data Design
4.2 Function Specification
4.2.1 Function Specification For Interfaces and Classes
4.3 Function Implementation
4.3.1 "Inline" decomposition of Boolean data
4.3.2 Allowed double decompositions
4.4 Testing
5 OOP Style
5.1 Interfaces, Classes, and Data Definitions
5.2 Function Specification For Interfaces and Classes
5.3 Effectful Methods
5.4 Testing Objects

Style Guide

Last updated: Sat, 11 Apr 2015 13:03:10 -0400

Style and formatting conventions are key to writing readable, maintainable programs. If everyone follows the same set of rules, then all programs become easier to understand because everyone starts with the same expectations and assumptions.

Thus, every programming language will have its own style conventions; organizations and projects often establish their own rules as well. To avoid confusing readers of your programs, it is important to follow the established conventions of your programming language and organization.

This page describes the style rules for this course. Failure to follow any of these rules will result in grade penalties.

1 General Style Requirements

2 Racket Style Requirements

We use (a subset of) Racket in this course. Thus, we adopt many of Racket’s style guidelines. Here are some of them.

3 Variable and Function Names

In Racket, names are case-sensitive, so be aware of this.

Readable programs must have descriptive function and variable names. Name things in terms of information whenever possible.

good

(define (next-x x speed)
  (+ x speed))

bad

(define (adds num1 num2)
  (+ num1 num2))

good

(define (fahrenheit->celsius degf)
  (* (- degf 32) 5/9))

bad

(define (sub32-times5/9 x)
  (* (- x 32) 5/9))

Don’t be lazy when naming things. Some common bad function naming patterns:
  • <some-function-name> and <some-function-name>-helper

  • <some-function-name>, <some-function-name>2, and <some-function-name>3

When you have a group of related functions, they should have a thoughtful, consistent naming scheme that conveys their relation to each other. A common example is a series of data decomposition functions that performs a task, separated by the kind of data.

good

(define-struct world (balls bats gloves))
(define (next-world w)
  (make-world (next-balls (world-balls w))
              (next-bats (world-bats w))
              (next-gloves (world-gloves w))))
(define (next-balls balls)
  (... (next-ball ball) ...))
(define (next-bats bats)
  (... (next-bat bat) ...))
(define (next-gloves gloves)
  (... (next-glove glove) ...))

bad

(define-struct world (balls bats gloves))
(define (world-tick w)
  (make-world (tick-helper1 (world-balls w))
              (tick-helper2 (world-bats w))
              (tick-helper3 (world-gloves w))))
(define (tick-helper1 x)
  (... (tick4 x) ...))
(define (tick-helper2 y)
  (... (tick5 y) ...))
(define (tick-helper3 z)
  (... (tick6 z) ...))

Other naming conventions:

4 Program Design Recipe Style Guidelines

The Program Design Recipe page explains the steps you should follow when writing programs in this course. The following style rules refine the recipe steps to improve program readability.

4.1 Data Design
4.2 Function Specification
4.2.1 Function Specification For Interfaces and Classes

See Function Specification For Interfaces and Classes.

4.3 Function Implementation
4.3.1 "Inline" decomposition of Boolean data

One Boolean may be decomposed inline without declaring it in the strategy.

This means that a function composition function may use one if (but not cond), but may not decompose any other kind of data.

ok strategy

; next-ball : Ball -> Ball
; STRATEGY: function composition
(define (next-ball b)
  (if (hit-wall? b)
      (bounce b)
      (go-straight b)))

bad strategy

; next-ball : Ball -> Ball
; STRATEGY: function composition
(define (next-ball b)
  (if (hit-left-wall? b)
      (bounce b)
      (if (hit-right-ball? b)
          (bounce b)
          (go-straight b))))

bad strategy

; next-ball : Ball -> Ball
; STRATEGY: function composition
(define (next-ball b)
  (cond
    [(hit-left-wall? b) (bounce b)]
    [(hit-right-ball? b) (bounce b)]
    [else (go-straight b)]))

strategy matches function

; An BallX is a Coordinate, split into the following intervals:
; - < 0
; - [0 WIDTH]
; - > WIDTH
 
; A Ball is a (make-ball BallX)
(define-struct ball (x))
 
; next-ball : Ball -> Ball
; STRATEGY: Data Decomposition on b : Ball
(define (next-ball b)
  (make-ball (next-ballx (ball-x b))))
 
; next-ballx : BallX -> BallX
; STRATEGY: data decomposition on x : BallX
(define (next-ballx x)
  (cond
    [(< x 0) (left-bounce x)]
    [(<= 0 x WIDTH) (go-straight x)]
    [else (right-bounce x)]))

Note that this Boolean exception applies to all instances of "function composition", such as when function composition is used to combine the pieces of a data-decomposing function:

strategy matches function

; A Ball is a (make-ball Coordinate)
(define-struct ball (x))
 
; next-ball : Ball -> Ball
; STRATEGY: data decomposition on b : Ball
(define (next-ball b)
  (make-ball
    (if (x-hits-wall? (ball-x b))
        (x-bounce (ball-x b)))
        (x-straight (ball-x b))))
Note in the above example that the if does not have to be the outermost expression in a function’s body.

strategy matches function

; filter : (X -> Boolean) ListOf<X> -> ListOf<X>
; STRATEGY: data decomposition on lst : ListOf<X>
(define (filter p? lst)
  (cond
    [(empty? lst) empty]
    [else
     (if (p? (first lst))
         (cons (first lst) (filter p? (rest lst)))
         (filter p? (rest lst)))]))

4.3.2 Allowed double decompositions

A function should encapsulate one distinct task. Thus, decomposing more than one of a function’s arguments typically makes the function less clear and less readable.

Double decomposition is acceptable, however, in some specific cases, described below. Double decomposing functions must declare all the decomposed kinds of data in the design strategy.

4.4 Testing

At a minimum, every program must have 100% code coverage, according to the DrRacket code coverage tool.

Do not paste and save images directly into the DrRacket buffer (even though some textbook examples do this). Load from file (using bitmap, etc.) if necessary. When testing image-producing functions, generate the expected output programmatically.

5 OOP Style

Here are the conventions we use in this course for object-oriented programs.
  • Class names are capitalized and have a % suffix, for example Robot%.

  • Interface names are capitalized and have a <%> suffix, for example Robot<%>.

  • Any method from an interface must be implemented with define/public.

  • Declare internal helper methods with define, not define/private.

  • You may add any methods to any interface we give you. However, do not change any of the given methods. Also, you should strive to minimize any publicly-visible interface.

  • If you add a method to an interface for testing purposes only, prefix it with testing:

5.1 Interfaces, Classes, and Data Definitions
5.2 Function Specification For Interfaces and Classes
5.3 Effectful Methods

Effectful methods, such as methods that mutate objects, have additional style conventions, so that the effects are always evident to code readers.

Examples:

> (define Ball<%>
    (interface ()
  
      ; move : Real -> Ball<%>
      ; Moves this ball horizontally by the given offset.
      move
  
      ; move! : Real -> Void
      ; EFFECT: Moves this ball horizontally by the given offset.
      move!
  
      ; get-x : -> Coordinate
      ; Returns the x Coordinate of this ball.
      get-x))
> (define Ball%
    (class* object% (Ball<%>)
      (super-new)
  
      (init-field x) ; Represents an x-axis Coordinate
  
      ; move : Real -> Ball<%>
      (define/public (move offset)
        (new Ball% [x (+ x offset)]))
  
      ; move! : Real -> Void
      (define/public (move! offset)
        (set! x (+ x offset)))
  
      ; get-x : -> Coordinate
      (define/public (get-x) x)))
> (define b (new Ball% [x 10]))
> (send b get-x)

10

> (send b move 100)

(object:Ball% ...)

> (send b get-x)

10

> (send (send b move 100) get-x)

110

> (send b move! 200)
> (send b get-x)

210

5.4 Testing Objects