Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | SyntacticConventions |
2 | ==================== | |
3 | :toc: | |
4 | ||
5 | Here are a number of syntactic conventions useful for programming in | |
6 | SML. | |
7 | ||
8 | ||
9 | == General == | |
10 | ||
11 | * A line of code never exceeds 80 columns. | |
12 | ||
13 | * Only split a syntactic entity across multiple lines if it doesn't fit on one line within 80 columns. | |
14 | ||
15 | * Use alphabetical order wherever possible. | |
16 | ||
17 | * Avoid redundant parentheses. | |
18 | ||
19 | * When using `:`, there is no space before the colon, and a single space after it. | |
20 | ||
21 | ||
22 | == Identifiers == | |
23 | ||
24 | * Variables, record labels and type constructors begin with and use | |
25 | small letters, using capital letters to separate words. | |
26 | + | |
27 | [source,sml] | |
28 | ---- | |
29 | cost | |
30 | maxValue | |
31 | ---- | |
32 | ||
33 | * Variables that represent collections of objects (lists, arrays, | |
34 | vectors, ...) are often suffixed with an `s`. | |
35 | + | |
36 | [source,sml] | |
37 | ---- | |
38 | xs | |
39 | employees | |
40 | ---- | |
41 | ||
42 | * Constructors, structure identifiers, and functor identifiers begin | |
43 | with a capital letter. | |
44 | + | |
45 | [source,sml] | |
46 | ---- | |
47 | Queue | |
48 | LinkedList | |
49 | ---- | |
50 | ||
51 | * Signature identifiers are in all capitals, using `_` to separate | |
52 | words. | |
53 | + | |
54 | [source,sml] | |
55 | ---- | |
56 | LIST | |
57 | BINARY_HEAP | |
58 | ---- | |
59 | ||
60 | ||
61 | == Types == | |
62 | ||
63 | * Alphabetize record labels. In a record type, there are spaces after | |
64 | colons and commas, but not before colons or commas, or at the | |
65 | delimiters `{` and `}`. | |
66 | + | |
67 | [source,sml] | |
68 | ---- | |
69 | {bar: int, foo: int} | |
70 | ---- | |
71 | ||
72 | * Only split a record type across multiple lines if it doesn't fit on | |
73 | one line. If a record type must be split over multiple lines, put one | |
74 | field per line. | |
75 | + | |
76 | [source,sml] | |
77 | ---- | |
78 | {bar: int, | |
79 | foo: real * real, | |
80 | zoo: bool} | |
81 | ---- | |
82 | ||
83 | ||
84 | * In a tuple type, there are spaces before and after each `*`. | |
85 | + | |
86 | [source,sml] | |
87 | ---- | |
88 | int * bool * real | |
89 | ---- | |
90 | ||
91 | * Only split a tuple type across multiple lines if it doesn't fit on | |
92 | one line. In a tuple type split over multiple lines, there is one | |
93 | type per line, and the `*`-s go at the beginning of the lines. | |
94 | + | |
95 | [source,sml] | |
96 | ---- | |
97 | int | |
98 | * bool | |
99 | * real | |
100 | ---- | |
101 | + | |
102 | It may also be useful to parenthesize to make the grouping more | |
103 | apparent. | |
104 | + | |
105 | [source,sml] | |
106 | ---- | |
107 | (int | |
108 | * bool | |
109 | * real) | |
110 | ---- | |
111 | ||
112 | * In an arrow type split over multiple lines, put the arrow at the | |
113 | beginning of its line. | |
114 | + | |
115 | [source,sml] | |
116 | ---- | |
117 | int * real | |
118 | -> bool | |
119 | ---- | |
120 | + | |
121 | It may also be useful to parenthesize to make the grouping more | |
122 | apparent. | |
123 | + | |
124 | [source,sml] | |
125 | ---- | |
126 | (int * real | |
127 | -> bool) | |
128 | ---- | |
129 | ||
130 | * Avoid redundant parentheses. | |
131 | ||
132 | * Arrow types associate to the right, so write | |
133 | + | |
134 | [source,sml] | |
135 | ---- | |
136 | a -> b -> c | |
137 | ---- | |
138 | + | |
139 | not | |
140 | + | |
141 | [source,sml] | |
142 | ---- | |
143 | a -> (b -> c) | |
144 | ---- | |
145 | ||
146 | * Type constructor application associates to the left, so write | |
147 | + | |
148 | [source,sml] | |
149 | ---- | |
150 | int ref list | |
151 | ---- | |
152 | + | |
153 | not | |
154 | + | |
155 | [source,sml] | |
156 | ---- | |
157 | (int ref) list | |
158 | ---- | |
159 | ||
160 | * Type constructor application binds more tightly than a tuple type, | |
161 | so write | |
162 | + | |
163 | [source,sml] | |
164 | ---- | |
165 | int list * bool list | |
166 | ---- | |
167 | + | |
168 | not | |
169 | + | |
170 | [source,sml] | |
171 | ---- | |
172 | (int list) * (bool list) | |
173 | ---- | |
174 | ||
175 | * Tuple types bind more tightly than arrow types, so write | |
176 | + | |
177 | [source,sml] | |
178 | ---- | |
179 | int * bool -> real | |
180 | ---- | |
181 | + | |
182 | not | |
183 | + | |
184 | [source,sml] | |
185 | ---- | |
186 | (int * bool) -> real | |
187 | ---- | |
188 | ||
189 | ||
190 | == Core == | |
191 | ||
192 | * A core expression or declaration split over multiple lines does not | |
193 | contain any blank lines. | |
194 | ||
195 | * A record field selector has no space between the `#` and the record | |
196 | label. So, write | |
197 | + | |
198 | [source,sml] | |
199 | ---- | |
200 | #foo | |
201 | ---- | |
202 | + | |
203 | not | |
204 | + | |
205 | [source,sml] | |
206 | ---- | |
207 | # foo | |
208 | ---- | |
209 | + | |
210 | ||
211 | * A tuple has a space after each comma, but not before, and not at the | |
212 | delimiters `(` and `)`. | |
213 | + | |
214 | [source,sml] | |
215 | ---- | |
216 | (e1, e2, e3) | |
217 | ---- | |
218 | ||
219 | * A tuple split over multiple lines has one element per line, and the | |
220 | commas go at the end of the lines. | |
221 | + | |
222 | [source,sml] | |
223 | ---- | |
224 | (e1, | |
225 | e2, | |
226 | e3) | |
227 | ---- | |
228 | ||
229 | * A list has a space after each comma, but not before, and not at the | |
230 | delimiters `[` and `]`. | |
231 | + | |
232 | [source,sml] | |
233 | ---- | |
234 | [e1, e2, e3] | |
235 | ---- | |
236 | ||
237 | * A list split over multiple lines has one element per line, and the | |
238 | commas at the end of the lines. | |
239 | + | |
240 | [source,sml] | |
241 | ---- | |
242 | [e1, | |
243 | e2, | |
244 | e3] | |
245 | ---- | |
246 | ||
247 | * A record has spaces before and after `=`, a space after each comma, | |
248 | but not before, and not at the delimiters `{` and `}`. Field names | |
249 | appear in alphabetical order. | |
250 | + | |
251 | [source,sml] | |
252 | ---- | |
253 | {bar = 13, foo = true} | |
254 | ---- | |
255 | ||
256 | * A sequence expression has a space after each semicolon, but not before. | |
257 | + | |
258 | [source,sml] | |
259 | ---- | |
260 | (e1; e2; e3) | |
261 | ---- | |
262 | ||
263 | * A sequence expression split over multiple lines has one expression | |
264 | per line, and the semicolons at the beginning of lines. Lisp and | |
265 | Scheme programmers may find this hard to read at first. | |
266 | + | |
267 | [source,sml] | |
268 | ---- | |
269 | (e1 | |
270 | ; e2 | |
271 | ; e3) | |
272 | ---- | |
273 | + | |
274 | _Rationale_: this makes it easy to visually spot the beginning of each | |
275 | expression, which becomes more valuable as the expressions themselves | |
276 | are split across multiple lines. | |
277 | ||
278 | * An application expression has a space between the function and the | |
279 | argument. There are no parens unless the argument is a tuple (in | |
280 | which case the parens are really part of the tuple, not the | |
281 | application). | |
282 | + | |
283 | [source,sml] | |
284 | ---- | |
285 | f a | |
286 | f (a1, a2, a3) | |
287 | ---- | |
288 | ||
289 | * Avoid redundant parentheses. Application associates to left, so | |
290 | write | |
291 | + | |
292 | [source,sml] | |
293 | ---- | |
294 | f a1 a2 a3 | |
295 | ---- | |
296 | + | |
297 | not | |
298 | + | |
299 | [source,sml] | |
300 | ---- | |
301 | ((f a1) a2) a3 | |
302 | ---- | |
303 | ||
304 | * Infix operators have a space before and after the operator. | |
305 | + | |
306 | [source,sml] | |
307 | ---- | |
308 | x + y | |
309 | x * y - z | |
310 | ---- | |
311 | ||
312 | * Avoid redundant parentheses. Use <:OperatorPrecedence:>. So, write | |
313 | + | |
314 | [source,sml] | |
315 | ---- | |
316 | x + y * z | |
317 | ---- | |
318 | + | |
319 | not | |
320 | + | |
321 | [source,sml] | |
322 | ---- | |
323 | x + (y * z) | |
324 | ---- | |
325 | ||
326 | * An `andalso` expression split over multiple lines has the `andalso` | |
327 | at the beginning of subsequent lines. | |
328 | + | |
329 | [source,sml] | |
330 | ---- | |
331 | e1 | |
332 | andalso e2 | |
333 | andalso e3 | |
334 | ---- | |
335 | ||
336 | * A `case` expression is indented as follows | |
337 | + | |
338 | [source,sml] | |
339 | ---- | |
340 | case e1 of | |
341 | p1 => e1 | |
342 | | p2 => e2 | |
343 | | p3 => e3 | |
344 | ---- | |
345 | ||
346 | * A `datatype`'s constructors are alphabetized. | |
347 | + | |
348 | [source,sml] | |
349 | ---- | |
350 | datatype t = A | B | C | |
351 | ---- | |
352 | ||
353 | * A `datatype` declaration has a space before and after each `|`. | |
354 | + | |
355 | [source,sml] | |
356 | ---- | |
357 | datatype t = A | B of int | C | |
358 | ---- | |
359 | ||
360 | * A `datatype` split over multiple lines has one constructor per line, | |
361 | with the `|` at the beginning of lines and the constructors beginning | |
362 | 3 columns to the right of the `datatype`. | |
363 | + | |
364 | [source,sml] | |
365 | ---- | |
366 | datatype t = | |
367 | A | |
368 | | B | |
369 | | C | |
370 | ---- | |
371 | ||
372 | * A `fun` declaration may start its body on the subsequent line, | |
373 | indented 3 spaces. | |
374 | + | |
375 | [source,sml] | |
376 | ---- | |
377 | fun f x y = | |
378 | let | |
379 | val z = x + y + z | |
380 | in | |
381 | z | |
382 | end | |
383 | ---- | |
384 | ||
385 | * An `if` expression is indented as follows. | |
386 | + | |
387 | [source,sml] | |
388 | ---- | |
389 | if e1 | |
390 | then e2 | |
391 | else e3 | |
392 | ---- | |
393 | ||
394 | * A sequence of `if`-`then`-`else`-s is indented as follows. | |
395 | + | |
396 | [source,sml] | |
397 | ---- | |
398 | if e1 | |
399 | then e2 | |
400 | else if e3 | |
401 | then e4 | |
402 | else if e5 | |
403 | then e6 | |
404 | else e7 | |
405 | ---- | |
406 | ||
407 | * A `let` expression has the `let`, `in`, and `end` on their own | |
408 | lines, starting in the same column. Declarations and the body are | |
409 | indented 3 spaces. | |
410 | + | |
411 | [source,sml] | |
412 | ---- | |
413 | let | |
414 | val x = 13 | |
415 | val y = 14 | |
416 | in | |
417 | x + y | |
418 | end | |
419 | ---- | |
420 | ||
421 | * A `local` declaration has the `local`, `in`, and `end` on their own | |
422 | lines, starting in the same column. Declarations are indented 3 | |
423 | spaces. | |
424 | + | |
425 | [source,sml] | |
426 | ---- | |
427 | local | |
428 | val x = 13 | |
429 | in | |
430 | val y = x | |
431 | end | |
432 | ---- | |
433 | ||
434 | * An `orelse` expression split over multiple lines has the `orelse` at | |
435 | the beginning of subsequent lines. | |
436 | + | |
437 | [source,sml] | |
438 | ---- | |
439 | e1 | |
440 | orelse e2 | |
441 | orelse e3 | |
442 | ---- | |
443 | ||
444 | * A `val` declaration has a space before and after the `=`. | |
445 | + | |
446 | [source,sml] | |
447 | ---- | |
448 | val p = e | |
449 | ---- | |
450 | ||
451 | * A `val` declaration can start the expression on the subsequent line, | |
452 | indented 3 spaces. | |
453 | + | |
454 | [source,sml] | |
455 | ---- | |
456 | val p = | |
457 | if e1 then e2 else e3 | |
458 | ---- | |
459 | ||
460 | ||
461 | == Signatures == | |
462 | ||
463 | * A `signature` declaration is indented as follows. | |
464 | + | |
465 | [source,sml] | |
466 | ---- | |
467 | signature FOO = | |
468 | sig | |
469 | val x: int | |
470 | end | |
471 | ---- | |
472 | + | |
473 | _Exception_: a signature declaration in a file to itself can omit the | |
474 | indentation to save horizontal space. | |
475 | + | |
476 | [source,sml] | |
477 | ---- | |
478 | signature FOO = | |
479 | sig | |
480 | ||
481 | val x: int | |
482 | ||
483 | end | |
484 | ---- | |
485 | + | |
486 | In this case, there should be a blank line after the `sig` and before | |
487 | the `end`. | |
488 | ||
489 | * A `val` specification has a space after the colon, but not before. | |
490 | + | |
491 | [source,sml] | |
492 | ---- | |
493 | val x: int | |
494 | ---- | |
495 | + | |
496 | _Exception_: in the case of operators (like `+`), there is a space | |
497 | before the colon to avoid lexing the colon as part of the operator. | |
498 | + | |
499 | [source,sml] | |
500 | ---- | |
501 | val + : t * t -> t | |
502 | ---- | |
503 | ||
504 | * Alphabetize specifications in signatures. | |
505 | + | |
506 | [source,sml] | |
507 | ---- | |
508 | sig | |
509 | val x: int | |
510 | val y: bool | |
511 | end | |
512 | ---- | |
513 | ||
514 | ||
515 | == Structures == | |
516 | ||
517 | * A `structure` declaration has a space on both sides of the `=`. | |
518 | + | |
519 | [source,sml] | |
520 | ---- | |
521 | structure Foo = Bar | |
522 | ---- | |
523 | ||
524 | * A `structure` declaration split over multiple lines is indented as | |
525 | follows. | |
526 | + | |
527 | [source,sml] | |
528 | ---- | |
529 | structure S = | |
530 | struct | |
531 | val x = 13 | |
532 | end | |
533 | ---- | |
534 | + | |
535 | _Exception_: a structure declaration in a file to itself can omit the | |
536 | indentation to save horizontal space. | |
537 | + | |
538 | [source,sml] | |
539 | ---- | |
540 | structure S = | |
541 | struct | |
542 | ||
543 | val x = 13 | |
544 | ||
545 | end | |
546 | ---- | |
547 | + | |
548 | In this case, there should be a blank line after the `struct` and | |
549 | before the `end`. | |
550 | ||
551 | * Declarations in a `struct` are separated by blank lines. | |
552 | + | |
553 | [source,sml] | |
554 | ---- | |
555 | struct | |
556 | val x = | |
557 | let | |
558 | y = 13 | |
559 | in | |
560 | y + 1 | |
561 | end | |
562 | ||
563 | val z = 14 | |
564 | end | |
565 | ---- | |
566 | ||
567 | ||
568 | == Functors == | |
569 | ||
570 | * A `functor` declaration has spaces after each `:` (or `:>`) but not | |
571 | before, and a space before and after the `=`. It is indented as | |
572 | follows. | |
573 | + | |
574 | [source,sml] | |
575 | ---- | |
576 | functor Foo (S: FOO_ARG): FOO = | |
577 | struct | |
578 | val x = S.x | |
579 | end | |
580 | ---- | |
581 | + | |
582 | _Exception_: a functor declaration in a file to itself can omit the | |
583 | indentation to save horizontal space. | |
584 | + | |
585 | [source,sml] | |
586 | ---- | |
587 | functor Foo (S: FOO_ARG): FOO = | |
588 | struct | |
589 | ||
590 | val x = S.x | |
591 | ||
592 | end | |
593 | ---- | |
594 | + | |
595 | In this case, there should be a blank line after the `struct` | |
596 | and before the `end`. |