Style Guide

Programs are easier to read and to understand when they are written in a familiar style and follow standard coding conventions. Most organizations that develop software therefore require programmers to write programs that follow the organization’s preferred style and coding conventions. Programs are generally written once, read many times, and edited over and over again. Style conventions help both you and any other programmer using your code (such as your homework partner) to more easily understand what you were thinking when you wrote the code.

In addition to following the Design Recipe, all code must adhere to the following style guidelines, which are intended to make your code easier to read, debug, and edit.

Document Structure

  1. Separate data definitions. Data definitions (and their corresponding examples/templates) should be placed at the beginning of the relevant exercise. Data definitions do not need to be repeated if used in multiple exercises. Do not split them up.

  2. Keep signature, purpose, test cases, implementations together, e.g.:  

    ;; my-function : Number String -> Number
    ;; Add double the string-length to twice the number cubed    
    (check-expect (my-function 2 "hi") 20)
    (check-expect (my-function 3 "hello") 64)
    (define (my-function n s)
        (+ (double (cube-num n)) (double-length s)))
    

Term Names

Use names that make sense with respect to the problem, for your data definitions, field names, functions, constants, and parameters. Also make sure to capitalize terms appropriately based upon their type…

  1. Constants: All caps with dashes between words:

    (define MY-NUMBER-EXAMPLE 10)
    (define SUN-IMG (circle 35 "solid" "yellow"))
    
  2. Structure names/fields and Function names/parameters: All lower-case with dashes between words (known as kebab-case)

    (define-struct struct-name [field-name])
        
    (define (my-function-name my-param-name)
      (+ my-param-name 3))
    
  3. Data Types: Title case (e.g., ThisIsADataType or ThisIsAnotherOne)

General

  1. Use proper indentation. Use the indentation style of DrRacket in your program. You can go to “Racket” > “Reindent All” to indent your entire file properly. Press tab to reindent the current line, or the currently selected selected portion of your file.
  2. Keep lines narrow. Do not exceed 102 columns for code or comments. DrRacket will show you the current line and column number in the bottom right of the window. You can also use its “Edit” -> “Find Longest Line” menu item. Or, you can go to “Edit” -> “Preferences” -> “Editing” -> “General Editing”, check the Maximum character width guide option and set it to 102.
  3. Do not use dangling parentheses. The closing right-parenthesis should be on the same line as the last expression of your code. The dangling parentheses in the second code excerpt below are considered bad style.
     
    ; ------------------------ GOOD
    (define (f-good l)
        (cond [(empty? l) 0]
              [else (f-good (rest l))])) ; HERE
        
    ; ------------------------ BAD
    (define (f-bad l)
        (cond [(empty? l) 0]
            [else (f-bad (rest l))]
            ) ; NOT HERE
        ) ; OR HERE
    
  4. Break lines to break up logically distinct tasks. By breaking after each argument, you will more often keep complete expressions together, and need fewer line breaks overall. Consider these examples of simple function calls:
     
    ; ----------------- GOOD
    (define (foo-good-short-name x y z)
        (max (* x y)      ; Break after each argument to max,
             (* y z)      ; and align all arguments in a column
             (* x z)      ; (This works best with short-named functions)
             (* x y z)))
        
    ; ----------------- GOOD
        
    (define (foo-good-longer-name x y z)
        (max          ; Break after max itself
         (* x y)      ; Then indent each argument 1 space
         (* y z)      ; (This works better when function names are long)
         (* x z)
         (* x y z)))
        
    ; ----------------- BAD
    (define (foo-bad x y z)
        (max (* x y) (* y    ; These linebreaks make it hard
                        z)   ; to follow what part of the
            (* x z) (* x    ; function we are in and should
                        y    ; be avoided.
                        z)))
    

    In rare cases, you can keep two arguments on a line, when they logically belong together. For example, the x- and y-coordinates in a call to place-image might easily fit on one line, and logically form a pair of coordinates, and so could stay on one line in good style. Some more examples…
     

    ; ---------------- All breaks to avoid...
        
    (define            ; Don't break here
        (foo x y z)
        ...)
        
    (define-struct      ; Don't break here
        foo [x y z])
        
    (define-struct foo  ; or here
        [x y z])
    
  5. Avoid unnecessary comments. Aside from signatures & purpose statements, there should not be comments in your code. If you find yourself having to explain what code does, it’s a sign that the code is too complex, and should be split into either locally named constants with informative names, or helper functions.

  6. Avoid pointless complexity. Do not define functions that do not have a clear purpose; e.g., a function that just invokes another function without doing anything else. Do not wrap calls to list abstractions with cond in order to include an empty branch that would have been handled by the list abstractions. Do not wrap boolean expressions with if and return #t and #f – this is equivalent to just the boolean expression. And in general, any code that could be simplified by just deleting code should be avoided.

Functions to Avoid

For this class, unless we tell you otherwise, you must not use the following functions:

  • equal?
  • equal~?
  • eq?
  • eqv?
  • assq
  • assoc
  • member
  • member?
  • memq
  • memq?
  • memv
  • remove
  • remove-all

The reason is that these functions circumvent the data directed structured programming that we are teaching you. Once you have learned the content of this course, you will be able to use these functions without harm (and indeed, they can be convenient), but using them early can easily hide errors in your programs and cause unnecessary confusion.

Additionally, you may not use the following LISP-style functions and constants:

  • null
  • null?
  • car
  • cdr
  • caar
  • cadr
  • cdar
  • cddr
  • caaar
  • caadr
  • cadar
  • caddr
  • cdaar
  • cdadr
  • cddar
  • cdddr
  • cadddr

Instead please use the more descriptively named list functions learned in class.