Coding Conventions - SML
Table of Contents
1. Naming Conventions
1.1. Prefixes
is
prefixes predicates, e.g.,isNegative
1.2. Cases (snake, upper, title, Pascal, etc.)
Chris Okasaki uses the following conventions:
- User-defined types (and type constructors) are written in
PascalCase
- Datatype constructors in algebraic datatypes are written using
PascalCase
- Function names use
camelCase
- Variable names use
snake_case
1.2.1. Modules, Signatures, Functors
- Signatures are written
SCREAMING_SNAKE_CASE
- Functors are written in
PascalCase
- Structures are written in
PascalCase
1.2.2. Filenames
- Functors are in
functor-name.fun
orfunctor-name.sml
- Signatures are in
signature-name.sig
- Structures are in
structure-name.sml
- Everything else (i.e., the default situation):
file-name.sml
Only MLton separates out functors into functor-name.fun
2. Compiling
This is unpleasant, since each compiler has its own conventions. And it seems only Poly/ML really runs on a Raspberry Pi.
Poly/ML expects a fun main () = ...
somewhere. When compiled, Poly/ML
will interpret the main()
function as the entry point.
For MLton, I believe, it will only run a function if it's executed. That
is to say, we need to have val _ = main();
somewhere, if we want MLton
to execute main()
. But Poly/ML doesn't like this, and won't compile
it happily.
If we want to support compiling with both Poly/ML and MLton, we need
to also have main.sml
file which consists of a single line:
(* main.sml *) val _ = main ();
MLton will compile everything, including main.sml
, while Poly/ML will
just omit main.sml
from compilation.
2.1. Build Scripts: Using the Standard Library
We need to tell the SML compiler to include the SML standard basis library. Each compiler has its own way to do this.
MLton has .mlb
basis files to guide compilation, but "basis files" are
unique/idiosyncratic to MLton. (MLKit supports .mlb
files, but only a
fragment of MLton's syntax is supported.)
Poly/ML has build scripts (e.g., polybuild).
SML/NJ has its own compilation manager (the aptly named "Compilation
Manager") which examines .cm
files to guide compilation.
HaMLet does not seem to have any build script (well, it's an
interpreter, so you'd need to use
files).
I won't pretend to understand Moscow ML's build process.
3. Typesetting in LaTeX
The following is what Chris Okasaki does in his book, Purely Functional Data Structures, and it works well.
Reserved keywords (like of
, struct
, fun
, let
, in
, val
,
etc.) are written in bold.
Datatype constructors are written in small-caps.
Signatures are typeset by:
- Step 1: convert
SCREAMING_SNAKE_CASE
into Pascal snake caseScreaming_Snake_Case
- Step 2: delete underscores, converting
Screaming_Snake_Case
intoScreamingSnakeCase
- Step 3: typeset the result of step 2 in small-caps.
Type variables (the 'a
in declarations like datatype 'a Foo
),
including the apostrophe, are converted to lowercase Greek counterparts
(so we would get: "datatype \(\alpha\) Foo").
Comments are italicized or slanted.
Everything else seems to be typeset "as expected".
A kludge which accomplishes the typesetting for signatures (the only
downside is that I need to manually add signature names to the
morekeywords={[2]...}
parameter):
\documentclass{article} \usepackage{mfirstuc} \makeatletter % I need to do something like % https://tex.stackexchange.com/a/448770/14751 % to transform the "\lst@um_" back to "_" \def\@Screaming@Snake@To@Pascal@Case#1\_#2\end@Screaming@Snake@To@Pascal@Case{% \makefirstuc{\MakeLowercase{#1}}% \ifx#2\@empty\else\ignorespaces\@Screaming@Snake@To@Pascal@Case#2\end@Screaming@Snake@To@Pascal@Case\fi} \newcommand\ScreamingSnakeToPascalCase[1]{% \ifx\@empty#1\else{\normalfont\scshape\@Screaming@Snake@To@Pascal@Case#1\_\@empty\end@Screaming@Snake@To@Pascal@Case}\fi} % https://tex.stackexchange.com/questions/439396/listings-highlight-a-prefixed-keyword-starting-with-a-single-quote \makeatother \usepackage{listings} \makeatletter \def\@setlststyle{% \unskip\edef\lt@temp{\unskip\noexpand\ScreamingSnakeToPascalCase{\expandafter\the\lst@token\unskip\relax\unskip}}% \unskip\global\lst@token=\expandafter{\lt@temp}% \unskip\the\lst@token } \begingroup \catcode`\_=11 \gdef\scsty#1{ \unskip\edef\lsttokens{\the\lst@token} \unskip\global\lst@token={} \unskip\expandafter\replaceUM\lsttokens\noexpand\lst@um_\relax\@end \unskip\unskip\@setlststyle } \gdef\appendall#1\@endAppend{\unskip \global\lst@token=\expandafter{\the\lst@token#1} } \gdef\replaceUM#1\lst@um_#2\@end{\unskip \if\relax\detokenize{#1}\relax \else \ifx\relax#2 \appendall#1\@endAppend \else \appendall#1\_\@endAppend \replaceUM#2\@end \fi \fi } \endgroup \makeatother \def\haskWildcard{\kern0.06em \vbox{\hrule width .5em}} \lstset{ columns=flexible, alsoletter={_}, basicstyle = {\ttfamily}, keywordstyle = [2]\scsty, morekeywords = [2]{SOME,NONE,GREATER,EQUAL,LESS,STRETCH_GOAL}, keywordstyle = {\bfseries\sffamily}, morekeywords={abstype,and,andalso,as,case,do,datatype,else,end,% eqtype,exception,fn,fun,functor,handle,if,in,include,infix,% infixr,let,local,nonfix,of,op,open,orelse,raise,rec,sharing,sig,% signature,struct,structure,then,type,val,with,withtype,while},% sensitive,% morecomment=[n]{(*}{*)},% morestring=[d]", literate={JOB}{{\ttfamily\scshape Job}}3 {'a}{{$\alpha$}}1 {'b}{{$\beta$}}1 {\ o\ }{{$\circ$}}3 {+}{{$+$}}1 {/}{{$/$}}1 {*}{{$*$}}1 {=}{{$=$\ }}1 {>}{{$>$}}1 {<}{{$<$}}1 {\\\\}{{\char`\\\char`\\}}1 {->}{{$\rightarrow$}}2 {>=}{{$\geq$}}2 {<-}{{$\leftarrow$}}2 {|}{{$\mid$\ }}1 {\ _}{{\ \haskWildcard}}2 } \begin{document} \begin{lstlisting} signature STRETCH_GOAL = sig val will_work : bool; val issued : bool option; end; fun giveRaise (SOME true) = true | giveRaise _ = false; \end{lstlisting} \end{document}
4. Emacs
SML-mode provides all the functionality we need. Just add to the
init.el
file:
(use-package sml-mode :ensure t :defer t) (add-to-list 'auto-mode-alist '("\\.\\(sml\\|sig\\|fun\\)\\'" . sml-mode))
5. References
- Naming conventions in Standard ML, The Breakfast Post (blogpost)
- Standard ML and how I'm compiling it, The Breakfast Post (blogpost)
- Theory ML, Isabelle's coding conventions
- Symbol attempts to provide a minimal build tool for Standard ML