diff --git a/CodingStyle b/CodingStyle.md similarity index 93% rename from CodingStyle rename to CodingStyle.md index 3b26b04..1b31446 100644 --- a/CodingStyle +++ b/CodingStyle.md @@ -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/