From Textbook: Specifications and Abstractions
Specification of Functions
1 | (** [f x] is ... |
- Returns: Don’t write
Returns: ...
, instead, just use[f x] is ...
- Requires: specific conditions on input
Requires: [x >=0]
- Raises: what the program will do if a bad input is given
Raises: xxx Exception if [x<0]
- Examples: give an example input and output of the function to better explain what it does
Specification of Modules
What to document in a module:
- functions not specified in the interface
- abstraction function
- representation invariant
1 | (* Implementation of sets as lists without duplicates. |
note that the above code keeps representation invariant, abstraction function, and other spec about implementation details inside the module definition, because when we generate the docs of this module, everything outside the definition of the module will be come “public” specs for clients and everything inside will become “private” spec for maintainers.
Abstraction Function
Abstraction function maps valid concrete values to abstract values
1 | (** AF: ... *) |
Representation Invariant
Representation Invariant distinguishes valid concrete values and from invalid concrete values and is the implicit part of all the precondition and postcondition.
1 | (** RI: ... *) |
Common Mistakes
Some common mistakes include not stating enough in preconditions, failing to identify when exceptions will be thrown, failing to specify behavior at boundary cases, writing operational specifications instead of definitional and stating too much in postconditions.
Long Variable Name
1 | let number_of_zeros_in_the_list = |
Code using such long names is verbose and hard to read. Instead of trying to embed a complete description of a variable in its name, use a short and suggestive name (e.g., zeroes
), and if necessary, add a comment at its declaration explaining the purpose of the variable.