Our primary goal in this class is to teach you how to program elegantly. You have most likely spent many years in secondary school learning style with respect to the English language -- programming should be no different. Every programming language demands a particular style of programming, and forcing one language's style upon another can have disastrous results. Of course there are some elements of writing a computer program that are shared between all languages. You should be able to pick up these elements through experience. In CS211 you learned a great deal about object-oriented programming, in this class you will learn about value-oriented programming.
As you will soon realize, this class takes style seriously. Listed below are the style rules we expect you to follow. The numbered rules are mandatory -- breaking them will result in point deductions. Other comments and suggestions are simply bulleted. These are not mandatory. Lastly, you should note that our rules come no where near the style mandates you will likely come across in industry. Many of companies go so far as to dictate exactly where spaces can go. You can rejoice that you do not have to learn Hungarian style.
Mandatory Rules:
Suggestions:
val sum = foldl (op +) 0 (* Sums a list of integers. *) (* Sums a list of integers. *) val sum = foldl (op +) 0
(* 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 () = ...
let val d = Date.fromTimeLocal(Time.now()) val m = Date.minute d val s = Date.second d fun f n = (n mod 3) = 0 in List.filter f [m,s] end
fun foo x = x+1 fun foo(x:int):int = x+1
struct structure H = HashTable structure T = TextIO structure A = Array ... end
struct structure L = List type foo = unit exception InternalError fun first list = L.nth(list,0) end
Token | Convention | Example | ||
Variables | Symbolic or initial lower case. Use embedded caps for multiword names. | getItem | ||
Constructors | Initial upper case. Use embedded caps for multiword names. Historic exceptions are nil, true, and false. Rarely are symbolic names like :: used. | Node EmptyQueue |
||
Types | All lower case. Use underscores for multiword names. | priority_queue | ||
Signatures | All upper case. Use underscores for multiword names. | PRIORITY_QUEUE | ||
Structures | Initial upper case. Use embedded caps for multiword names. | PriorityQueue | ||
Functors | Same as structure convention, except Fn completes the name. | PriorityQueueFn |
val x = "Long line..."^ "Another long line." val x = ("Long line..."^ "Another long line.")
val x = function1 (arg1) (arg2) (function2 (arg3)) (arg4) val x = function1 arg1 arg2 (function2 arg3) arg4
if exp1 then exp2 if exp1 then else if exp3 then exp4 exp2 else if exp5 then exp6 else exp3 else exp8 if exp1 then exp2 else exp3 if exp1 then exp2 else exp3
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 val q = 38 in in in bar * (p + q) bar * (p + q) bar * (p + q) end end end
Bad | Good | |
fun f arg1 arg2 = let val x = #1 arg1 val y = #2 arg1 val z = #1 arg2 in ... end |
fun f (x,y) (z,_) = ...
|
|
fun f arg1 = let val x = #foo arg1 val y = #bar arg1 val baz = #baz arg1 in ... end |
fun f {foo=x, bar=y, baz} = ...
|
fun fact 0 = 1 | fact n = n * fact(n-1) fun fact n = if n=0 then 1 else n * fact(n-1)
Bad | Good | |
let val v = someFunction() val x = #1 v val y = #2 v in x+y end |
let val (x,y) = someFunction() in x+y end |
let val d = Date.fromTimeLocal(Time.now()) in case Date.month d of Date.Jan => (case Date.day d of 1 => print "Happy New Year" | _ => ()) | Date.Jul => (case Date.day d of 4 => print "Happy Independence Day" | _ => ()) | Date.Oct => (case Date.day d of 10 => print "Happy Metric Day" | _ => ()) endGood
let val d = Date.fromTimeLocal(Time.now()) in case (Date.month d, Date.day d) of (Date.Jan, 1) => print "Happy New Year" | (Date.Jul, 4) => print "Happy Independence Day" | (Date.Oct, 10) => print "Happy Metric Day" | _ => () end
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)))Good
fun euclid (m:int,n:int) : (int * int * int) = if n=0 then (b 1, b 0, m) else let val q = m div n val r = n mod n val (u,v,g) = euclid (n,r) in (v, u-(q*v), g) end
let val x = TextIO.inputLine TextIO.stdIn in case x of ... endGood
case TextIO.inputLine TextIO.stdIn of ...
let val x = y*y in x+z end y*y + z
Bad | Good | |
if e then true else false | e | |
if e then false else true | not e | |
if beta then beta else false | beta | |
if not e then x else y | if e then y else x | |
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 |
case e of true => x | false => y if e then x else y
case e of c => x (* c is a constant value *) | _ => y if e=c then x else y
val x = case expr of (y,z) => y val (x,_) = expr
Bad | Good | |
l::nil | [l] | |
l::[] | [l] | |
length + 0 | length | |
length * 1 | length | |
big exp * same big exp | let val x = big exp in x*x end | |
if
x then f a b c1 else f a b c2 |
f a b if x then c1 else c2 | |
String.compare(x,y)=EQUAL | x=y | |
String.compare(x,y)=LESS | x<y | |
String.compare(x,y)=GREATER | x>y | |
Int.compare(x,y)=EQUAL | x=y | |
Int.compare(x,y)=LESS | x<y | |
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 |
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]
foldl (fn (x,y) => x + y) 0 foldl (op +) 0
let val x = 42 in let val y = 101 in x + y end end let val x = 42 val y = 101 in x+y end