Coding Conventions
In addition to following the design recipe, we would like you to observe basic style guidelines for your programs. Not observing these very basic guidelines leads to unreadable code.
Layout Conventions
- Start the file with the a purpose statement for the file, the requires, and a comment describing how to run the program.
- Then put constant definitions and data definitions.
- Then put your functions and tests. Organize your functions into meaningful groups. Put help functions near the functions that they help. If your tests are short, put them near the function they test. Long test suites can be put at the end of the file.
- Use function names and variable names that make sense with respect to the problem and the purpose statement. See the section on Naming Conventions below.
- Use consistent indenting. The Racket indenter does a very good job. A carriage return will, by default, start the next line at the right indentation. As you edit code, lines may wind up improperly indented. TAB correctly re-indents the current line. If a portion of your file is selected (shown in blue), TAB reindents everything in the current region. Type ctl-A TAB to select the entire file and reindent the whole thing.
- No line should span more than 80-90 characters. The position of the cursor, including its position on the line, is shown in the bottom right of the Racket definitions window. (A few long lines are OK, but don't make a habit of it.)
- Use visually distinctive separators to divide distinct sections of your program. I tend to use lines consisting of many semicolons, but you may use asterisks, dashes, or whatever pleases you. If you have sections and subsections, you can use 2 lines of separators between major sections and one line of separators between minor sections.
Naming Conventions
In general, we use the following conventions:
Function Names
- Choose function names carefully. When a function is used in some other piece of code, the reader (that is, someone other than you!) should be able to tell roughly what a function computes just by looking at its name. If further detail is needed, then the reader can refer to the purpose statement of the function. If the function name is chosen well and the purpose statement is written well, the reader should rarely, if ever, need to refer to the function definition.
- Function names should be in lower case. Use a minus sign ("-") to separate words in function names, e.g. cat-speed, world-after-tick.
- Function names should almost always be nouns. The function name
should usually describe the result of the function, not
what the function does. (e.g. area, not
compute-area) Use the first component of the name to
distinguish similar functions with different arguments, e.g.:
- circle-area, ring-area
- book-price, total-order-price
- rectangle-after-tick, selected-rectangle-after-tick, unselected-rectangle-after-tick
Write a WHERE clause in your purpose statement to document the meaning of names like this, eg.
rectangle-after-tick : Rectangle -> Rectangle GIVEN: A rectangle r RETURNS: The rectangle that should follow the given one after a tick selected-rectangle-after-tick : Rectangle -> Rectangle GIVEN: A rectangle r WHERE: r is selected RETURNS: The rectangle that should follow the given one after a tick
- Predicates should end in ? : e.g., square? (pronounced "square-huh?")
Variable Names
- We generally use short names for variables (arguments) e.g.,
- b for a Ball
- lob for a ListOfBall
- It is preferable to use mnemonic names, like cost or price, or qualified names, like mouse-x or bomb-x, rather than names that merely identify the data type of the variable.
- Sometimes I'll use plural names or abbreviations for lists, such as balls or bs for a ListOfBall
Other Names
- Global constants are in upper-case: eg. HEIGHT, WIDTH, BALL-SPEED
- Names of data definitions are in CamelCase: e.g. ListOfNumber, World, FallingCatKeyEvent .
- Everything else is in lower case, including names of structs and functions. Use a minus sign ("-") to separate words in procedure names, e.g. cat-speed, world-after-tick.
- Names of interfaces are Capitalized, like data definitions, and end in <%>
- Names of clases of Capitalized, like data definitions, and end in %.
Use Help Functions to Simplify Your Code
Use help functions to clarify your intentions in a piece of code. That messy junk you just wrote in your function must have had some purpose. Turn it into a help function so you can document its purpose, and give it independent tests.
Short is good. Long is bad. Period.
Here are two radically different versions of the same function.
bad | good |
---|---|
; ball-after-tick : Ball -> Ball (define (ball-after-tick b) (cond [(and (<= YUP (where b) YLO) (or (<= (ball-x b) XWALL (+ (ball-x b) (ball-dx b))) (>= (ball-x b) XWALL (+ (ball-x b) (ball-dx b))))) (make-ball (- (* 2 XWALL) (ball-x (straight b 1.))) (ball-y (straight b 1.)) (- (ball-dx (straight b 1.))) (ball-dy (straight b 1.)))] [else (straight b 1.)])) |
; ball-after-tick : Ball -> Ball (define (ball-after-tick b) (cond [(would-hit-wall? b) (ball-at-wall-reversed b)] [else (ball-after-straight-move b 1.)])) |
Both always return the same answer. But which one do you understand immediately? Which programming style makes sense? Which will drive the TA crazy?
Last modified: Sun Sep 18 08:03:53 Eastern Daylight Time 2016