Ad-hoc conversion to markdown.
This commit is contained in:
parent
3667e0f898
commit
173c721611
|
@ -1,6 +1,4 @@
|
|||
|
||||
su4sml coding style
|
||||
===================
|
||||
# su4sml coding style
|
||||
|
||||
We here document coding-style guidelines that should be adhered to.
|
||||
Much of the existing code is not yet following these guidelines,
|
||||
|
@ -8,8 +6,7 @@ patches are welcome. This style-guide was inspired by the SML Style Guide
|
|||
from Cornell university [1].
|
||||
|
||||
|
||||
Chapter 1: Indentation and breaking long lines
|
||||
==========
|
||||
## Chapter 1: Indentation and breaking long lines
|
||||
|
||||
The limit on the length of lines is 80 columns and this is a hard
|
||||
limit. Using more than 80 columns causes your code to wrap around to
|
||||
|
@ -20,21 +17,21 @@ spaces to control indenting. Indent by two spaces.
|
|||
|
||||
Long expressions can be broken up and the parts aligned, as in the second
|
||||
example. Either is acceptable.
|
||||
|
||||
```sml
|
||||
val x = "Long line..."^
|
||||
"Another long line."
|
||||
|
||||
val x = "Long line..."^
|
||||
"Another long line."
|
||||
|
||||
```
|
||||
Case expressions should be indented as follows:
|
||||
|
||||
```sml
|
||||
case expr of
|
||||
pat1 => ...
|
||||
| pat2 => ...
|
||||
|
||||
```
|
||||
If expressions should be indented according to one of the following schemes:
|
||||
|
||||
```sml
|
||||
if exp1 then exp2 if exp1 then
|
||||
else if exp3 then exp4 exp2
|
||||
else if exp5 then exp6 else exp3
|
||||
|
@ -42,13 +39,12 @@ else if exp5 then exp6 else exp3
|
|||
|
||||
if exp1 then exp2 else exp3 if exp1 then exp2
|
||||
else exp3
|
||||
|
||||
```
|
||||
Comments should be indented to the level of the line of code that follows the
|
||||
comment.
|
||||
|
||||
|
||||
Chapter 2: Factoring
|
||||
==========
|
||||
## Chapter 2: Factoring
|
||||
|
||||
Avoid breaking expressions over multiple lines. If a tuple consists of more
|
||||
than two or three elements, you should consider using a record instead of a
|
||||
|
@ -60,16 +56,17 @@ multiple lines to something that has good style is to factor the code using a
|
|||
let expression. Consider the following:
|
||||
|
||||
Bad
|
||||
|
||||
```sml
|
||||
fun euclid (m:int,n:int) : (int * int * int) =
|
||||
if n=0
|
||||
then (b 1, b 0, m)
|
||||
else (#2 (euclid (n, m mod n)), u - (m div n) *
|
||||
|
||||
(euclid (n, m mod n)), #3 (euclid (n, m mod n)))
|
||||
```
|
||||
|
||||
Better
|
||||
|
||||
```sml
|
||||
fun euclid (m:int,n:int) : (int * int * int) =
|
||||
if n=0
|
||||
then (b 1, b 0, m)
|
||||
|
@ -78,7 +75,7 @@ Better
|
|||
u - (m div n) * (euclid (n, m mod n)),
|
||||
|
||||
#3 (euclid (n, m mod n)))
|
||||
|
||||
```
|
||||
Best
|
||||
|
||||
fun euclid (m:int,n:int) : (int * int * int) =
|
||||
|
@ -94,38 +91,36 @@ Best
|
|||
|
||||
|
||||
Do not factor unnecessarily.
|
||||
|
||||
Bad
|
||||
|
||||
```sml
|
||||
let
|
||||
val x = TextIO.inputLine TextIO.stdIn
|
||||
in
|
||||
case x of
|
||||
...
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
Good
|
||||
|
||||
```sml
|
||||
case TextIO.inputLine TextIO.stdIn of
|
||||
...
|
||||
|
||||
```
|
||||
Bad (provided y is not a large expression):
|
||||
|
||||
```sml
|
||||
let val x = y*y in x+z end
|
||||
|
||||
```
|
||||
|
||||
Good
|
||||
|
||||
```sml
|
||||
y*y + z
|
||||
```
|
||||
|
||||
|
||||
Chapter 3: Comments
|
||||
==========
|
||||
## Chapter 3: Comments
|
||||
|
||||
Comments should be written with SMLDoc [2] in mind; a, nightly updated, API
|
||||
documentation is available [3]. For example:
|
||||
|
||||
```sml
|
||||
(**
|
||||
* opens a file.
|
||||
* @params {fileName, mode}
|
||||
|
@ -133,21 +128,22 @@ documentation is available [3]. For example:
|
|||
* @param mode mode flag
|
||||
* @return file stream *)
|
||||
val openFile : {fileName : string, mode : openMode} -> stream
|
||||
|
||||
```
|
||||
We mainly restrict ourselves to the following SMLDoc tags:
|
||||
```
|
||||
@params gives names to formal parameters of functions and value constructors
|
||||
@param a description of a formal parameter
|
||||
@return a description about return value of the function
|
||||
@see related items (specified text is not analyzed in the current version)
|
||||
@throws same as the @exception tag
|
||||
|
||||
```
|
||||
Signatures must be commented using SMLDoc.
|
||||
|
||||
Comments go above the code they reference, as in the following example:
|
||||
|
||||
```sml
|
||||
(** Sums a list of integers. *)
|
||||
val sum = foldl (op +) 0
|
||||
|
||||
```
|
||||
Avoid Useless Comments. Avoid comments that merely repeat the code they
|
||||
reference or state the obvious. Comments should state the invariants, the
|
||||
non-obvious, or any references that have more information about the code.
|
||||
|
@ -172,17 +168,16 @@ Multi-line Commenting. When comments are printed on paper, the reader lacks the
|
|||
advantage of color highlighting performed by an editor such as Emacs.
|
||||
Multiline comments can be distinguished from code by preceding each line of the
|
||||
comment with a * similar to the following:
|
||||
|
||||
```sml
|
||||
(**
|
||||
* This is one of those rare but long comments
|
||||
* that need to span multiple lines because
|
||||
* the code is unusually complex and requires
|
||||
* extra explanation. *)
|
||||
fun complicatedFunction () = ...
|
||||
```
|
||||
|
||||
|
||||
Chapter 4: Parentheses
|
||||
==========
|
||||
## Chapter 4: Parentheses
|
||||
|
||||
Over Parenthesizing. Parentheses have many semantic purposes in ML, including
|
||||
constructing tuples, grouping sequences of side-effect expressions, forcing a
|
||||
|
@ -198,7 +193,7 @@ already wrapped by a let...in...end block, you can drop the parentheses.
|
|||
Alternative Block Styles. Blocks of code such as let...in...end, struct...end,
|
||||
and sig...end should be indented as follows. There are several alternative
|
||||
styles to choose from.
|
||||
|
||||
```sml
|
||||
fun foo bar = fun foo bar = fun foo bar = let
|
||||
let let val p = 4 val p = 4
|
||||
val p = 4 val q = 38 val q = 38
|
||||
|
@ -206,11 +201,10 @@ fun foo bar = fun foo bar = fun foo bar = let
|
|||
in bar * (p + q) bar * (p + q)
|
||||
bar * (p + q) end end
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
Chapter 5: Pattern matching
|
||||
==========
|
||||
## Chapter 5: Pattern matching
|
||||
|
||||
No Incomplete Pattern Matches. Incomplete pattern matches are flagged with
|
||||
compiler warnings, which should be treated as errors.
|
||||
|
@ -219,7 +213,7 @@ Pattern Match in the Function Arguments When Possible. Tuples, records and
|
|||
datatypes can be deconstructed using pattern matching. If you simply
|
||||
deconstruct the function argument before you do anything useful, it is better
|
||||
to pattern match in the function argument. Consider these examples:
|
||||
|
||||
```sml
|
||||
Bad Good
|
||||
fun f arg1 arg2 = let fun f (x,y) (z,_) = ...
|
||||
val x = #1 arg1
|
||||
|
@ -237,7 +231,7 @@ fun f arg1 = let fun f {foo=x, bar=y, baz} = ...
|
|||
in
|
||||
...
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
|
||||
Avoid Unnecessary Projections. Prefer pattern matching to projections with
|
||||
|
@ -246,6 +240,7 @@ as it is infrequent and the meaning is clearly understood from the context.
|
|||
The above rule shows how to pattern-match in the function arguments. Here is
|
||||
an example for pattern matching with value declarations.
|
||||
|
||||
```sml
|
||||
Bad Good
|
||||
let let
|
||||
val v = someFunction() val (x,y) = someFunction()
|
||||
|
@ -254,7 +249,7 @@ let let
|
|||
in end
|
||||
x+y
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
|
||||
Combine nested case Expressions. Rather than nest case expressions, you can
|
||||
|
@ -262,7 +257,7 @@ combine them by pattern matching against a tuple, provided the tests in the
|
|||
case expressions are independent. Here is an example:
|
||||
|
||||
Bad
|
||||
|
||||
```sml
|
||||
let
|
||||
val d = Date.fromTimeLocal(Time.now())
|
||||
in
|
||||
|
@ -277,11 +272,11 @@ Bad
|
|||
10 => print "Happy Metric Day"
|
||||
| _ => ())
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
|
||||
Good
|
||||
|
||||
```sml
|
||||
let
|
||||
val d = Date.fromTimeLocal(Time.now())
|
||||
in
|
||||
|
@ -291,7 +286,7 @@ Good
|
|||
| (Date.Oct, 10) => print "Happy Metric Day"
|
||||
| _ => ()
|
||||
end
|
||||
|
||||
```
|
||||
Avoid the use valOf, hd, or tl. The functions valOf, hd, and tl are used to
|
||||
deconstruct option types and list types. However, they raise exceptions on
|
||||
certain inputs. You should avoid these functions altogether. It is usually
|
||||
|
@ -299,8 +294,7 @@ easy to achieve the same effect with pattern matching. If you cannot manage to
|
|||
avoid them, you should handle any exceptions that they might raise.
|
||||
|
||||
|
||||
Chapter 6: Naming and declarations
|
||||
==========
|
||||
## Chapter 6: Naming and declarations
|
||||
|
||||
Naming Conventions. The best way to tell at a glance something about the type
|
||||
of a variable is to use the standard SML naming conventions. The following are
|
||||
|
@ -309,20 +303,20 @@ SML/NJ libraries:
|
|||
|
||||
Token SML Naming Convention
|
||||
Variables Symbolic or initial lower case. Use embedded caps for multiword names.
|
||||
Example: getItem
|
||||
Example: `getItem`
|
||||
Functions Initial lower case. Use embedded caps for multiword names.
|
||||
Example: nameOf
|
||||
Example: `nameOf`
|
||||
Constructors Initial upper case. Use embedded caps for multiword names. Historic
|
||||
exceptions are nil, true, and false. Rarely are symbolic names like :: used.
|
||||
Example: Node, EmptyQueue
|
||||
Example: `Node`, `EmptyQueue`
|
||||
Types All lower case. Use underscores for multiword names.
|
||||
Example: priority_queue
|
||||
Example: `priority_queue`
|
||||
Signatures All upper case. Use underscores for multiword names.
|
||||
Example: PRIORITY_QUEUE
|
||||
Example: `PRIORITY_QUEUE`
|
||||
Structures Initial upper case. Use embedded caps for multiword names.
|
||||
Example: PriorityQueue
|
||||
Example: `PriorityQueue`
|
||||
Functors Same as structure convention, except Fn completes the name.
|
||||
Example: PriorityQueueFn
|
||||
Example: `PriorityQueueFn`
|
||||
|
||||
These conventions are not enforced by the compiler, though violations of the
|
||||
variable/constructor conventions ought to cause warning messages because of the
|
||||
|
@ -333,7 +327,7 @@ variable names that reflect their intended use. Choose words or combinations
|
|||
of words describing the value. Variable names may be one letter in short let
|
||||
blocks. Functions used in a fold, filter, or map are often bound to the name
|
||||
f. Here is an example for short variable names:
|
||||
|
||||
```sml
|
||||
let
|
||||
val d = Date.fromTimeLocal(Time.now())
|
||||
val m = Date.minute d
|
||||
|
@ -342,7 +336,7 @@ let
|
|||
in
|
||||
List.filter f [m,s]
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
Avoid Global Mutable Variables. Mutable values should be local to closures and
|
||||
almost never declared as a structure's value. Global mutable values cause many
|
||||
|
@ -362,26 +356,26 @@ by functions within the current structure are aliased to one or two letter
|
|||
variables at the top of the struct block. This serves two purposes: it shortens
|
||||
the name of the structure and it documents the structures you use. Here is an
|
||||
example:
|
||||
|
||||
```sml
|
||||
struct
|
||||
structure H = HashTable
|
||||
structure T = TextIO
|
||||
structure A = Array
|
||||
...
|
||||
end
|
||||
|
||||
```
|
||||
Order of Declarations in a Structure. When declaring elements in a structure,
|
||||
you should first alias the structures you intend to use, followed by the types,
|
||||
followed by exceptions, and lastly list all the value declarations for the
|
||||
structure. Here is an example:
|
||||
|
||||
```sml
|
||||
struct
|
||||
structure L = List
|
||||
type foo = unit
|
||||
exception InternalError
|
||||
fun first list = L.nth(list,0)
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
Every declaration within the structure should be indented the same amount.
|
||||
|
||||
|
@ -389,14 +383,14 @@ Moreover, every top-level structure should be restricted by a (documented)
|
|||
signature.
|
||||
|
||||
Functions should declared in their their curried form, e.g.,
|
||||
```sml
|
||||
fun f x y = ... instead of fun f(x,y) = ...
|
||||
|
||||
```
|
||||
Datatypes should be preferred to type synonyms in particular for
|
||||
record types
|
||||
|
||||
|
||||
Chapter 7: Verbosity
|
||||
==========
|
||||
## Chapter 7: Verbosity
|
||||
|
||||
Don't Rewrite Library Functions. The basis library and the SML/NJ library have
|
||||
a great number of functions and data structures -- use them! Often students
|
||||
|
@ -412,6 +406,7 @@ case that the type is bool, you should not be using if at all. Consider the
|
|||
following:
|
||||
|
||||
|
||||
```sml
|
||||
Bad Good
|
||||
if e then true else false e
|
||||
if e then false else true not e
|
||||
|
@ -421,40 +416,40 @@ if x then true else y x orelse y
|
|||
if x then y else false x andalso y
|
||||
if x then false else y not x andalso y
|
||||
if x then y else true not x orelse y
|
||||
|
||||
```
|
||||
|
||||
Misusing case Expressions. The case expression is misused in two common
|
||||
situations. First, case should never be used in place of an if expression
|
||||
(that's why if exists). Note the following:
|
||||
|
||||
```sml
|
||||
case e of
|
||||
true => x
|
||||
| false => y
|
||||
|
||||
if e then x else y
|
||||
|
||||
```
|
||||
The latter is much better. Another situation where if expressions are
|
||||
preferred over case expressions is as follows:
|
||||
|
||||
```sml
|
||||
case e of
|
||||
c => x (* c is a constant value *)
|
||||
| _ => y
|
||||
|
||||
if e=c then x else y
|
||||
|
||||
```
|
||||
The latter is definitely better. The other misuse is using case when pattern
|
||||
matching with a val declaration is enough. Consider the following:
|
||||
|
||||
```sml
|
||||
val x = case expr of (y,z) => y
|
||||
|
||||
val (x,_) = expr
|
||||
|
||||
```
|
||||
The latter is better.
|
||||
|
||||
Other Common Misuses. Here are some other common mistakes to watch out for:
|
||||
|
||||
```sml
|
||||
Bad Good
|
||||
|
||||
l::nil [l]
|
||||
l::[] [l]
|
||||
length + 0 length
|
||||
|
@ -471,30 +466,30 @@ Int.compare(x,y)=GREATER x>y
|
|||
Int.sign(x)=~1 x<0
|
||||
Int.sign(x)=0 x=0
|
||||
Int.sign(x)=1 x>0
|
||||
```
|
||||
|
||||
|
||||
Don't Re-wrap Functions. When passing a function as an argument to another
|
||||
Do not re-wrap Functions. When passing a function as an argument to another
|
||||
function, don't re-wrap the function unnecessarily. Here's an example:
|
||||
|
||||
```sml
|
||||
List.map (fn x => Math.sqrt x) [1.0, 4.0, 9.0, 16.0]
|
||||
|
||||
List.map Math.sqrt [1.0, 4.0, 9.0, 16.0]
|
||||
|
||||
```
|
||||
The latter is better. Another case for rewrapping a function is often
|
||||
associated with infix binary operators. To prevent rewrapping the binary
|
||||
operator, use the op keyword as in the following example:
|
||||
|
||||
```sml
|
||||
foldl (fn (x,y) => x + y) 0
|
||||
|
||||
foldl (op +) 0
|
||||
|
||||
```
|
||||
The latter is better.
|
||||
|
||||
Don't Needlessly Nest let Expressions. Multiple declarations may occur in the
|
||||
first block of a let...in...end expression. The bindings are performed
|
||||
sequentially, so you may use a name bound earlier in the same block. Consider
|
||||
the following:
|
||||
|
||||
```sml
|
||||
let
|
||||
val x = 42
|
||||
in
|
||||
|
@ -511,7 +506,7 @@ let
|
|||
in
|
||||
x + y
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
The latter is better.
|
||||
|
||||
|
@ -522,8 +517,7 @@ This has the added benefit of letting you document the purpose of the value
|
|||
with a name.
|
||||
|
||||
|
||||
Chapter 9: File names and encoding
|
||||
==========
|
||||
## Chapter 8: File names and encoding
|
||||
|
||||
In general, a source file should only contain one signature or structure. In more
|
||||
detail:
|
||||
|
@ -537,8 +531,7 @@ and separated by underscore, e.g., ocl_term.sig
|
|||
Source files should use the Unix line ending convention and be either encoding
|
||||
using ASCII (preferred) or UTF-8.
|
||||
|
||||
Chapter 9: Compatibility
|
||||
==========
|
||||
## Chapter 9: Compatibility
|
||||
|
||||
Any code developed must be portable among the supported SML systems
|
||||
(currently: sml/NJ, mlton, polyml 5.x). Moreover, the code should
|
||||
|
@ -547,25 +540,23 @@ warnings as errors. Keep in mind that polyml does only provide a subset
|
|||
of the SML standard library.
|
||||
|
||||
|
||||
Appendix I: Machine-support
|
||||
===========
|
||||
## Appendix
|
||||
### Appendix I: Machine-support
|
||||
The following elisp-snippet provides marginal support for this coding-style for
|
||||
the sml-mode [4] of Emacs [5]:
|
||||
|
||||
```emacs
|
||||
(setq sml-indent-level 2)
|
||||
(setq sml-pipe-indent -2)
|
||||
(setq sml-case-indent t)
|
||||
(setq sml-nested-if-indent t)
|
||||
(setq sml-type-of-indent nil)
|
||||
(setq sml-electric-semi-mode nil)
|
||||
```
|
||||
|
||||
Appendix II: References
|
||||
============
|
||||
|
||||
[1] http://www.cs.cornell.edu/Courses/cs312/2007fa/handouts/style.htm
|
||||
[2] SMLDoc. http://www.pllab.riec.tohoku.ac.jp/smlsharp/?SMLDoc
|
||||
[3] http://projects.brucker.ch/su4sml/smldoc/
|
||||
[4] http://www.smlnj.org/doc/Emacs/sml-mode.html
|
||||
[5] http://www.gnu.org/software/emacs/
|
||||
--
|
||||
Last updated on $Date:$
|
||||
### Appendix II: References
|
||||
1. http://www.cs.cornell.edu/Courses/cs312/2007fa/handouts/style.htm
|
||||
2. SMLDoc. http://www.pllab.riec.tohoku.ac.jp/smlsharp/?SMLDoc
|
||||
3. http://projects.brucker.ch/su4sml/smldoc/
|
||||
4. http://www.smlnj.org/doc/Emacs/sml-mode.html
|
||||
5. http://www.gnu.org/software/emacs/
|
Loading…
Reference in New Issue