This document describes how to use the SML/NJ implementation of Standard ML on the command line and with SML Mode in the Emacs text editor. Most of it assumes you are familiar with Emacs. (Get started with Emacs here.)

For a large part of the middle of the course, we will work with the Standard ML programming language, using the SML/NJ (Standard ML of New Jersey) compiler and interpreter. Any editor that can handle plain text will work, but we strongly recommend using Emacs and its SML Mode, which provides good syntax highlighting, proper indentation, and the ability to run SML programs directly alongside the code.

SML/NJ and Emacs are installed on the Linux lab machines.

Contents

Using the SML/NJ REPL (Read-Eval-Print Loop) in Emacs

This section shows you how to run SML programs from within Emacs. It assumes you already have an SML file or can write your own SML program in a new one.

  • Edit a file with extension .sml. You should be in SML-mode (?), using Tab to indent your code well.
  • To open the SML/NJ REPL within Emacs, type C-c C-s (and then Enter to accept the sml interpreter) while working in an SML-mode buffer. This command creates an *sml* buffer unless one exists and starts an SML/NJ REPL in that buffer unless one is already running there. Type this key sequence works only in SML-mode buffers (e.g., not *scratch*).
  • To end a REPL session, type C-d while the cursor is at the REPL prompt in the *sml* buffer. (Elsewhere, C-d will delete the character following the cursor!)
  • To restart the REPL, type C-c C-s (and Enter) again in the *sml* buffer or another SML-mode buffer.
    • By ending and restarting a session, the new session has an empty environment. Your earlier interactions remain in the *sml* buffer, so you can save them, cut-paste them, etc., but they have no effect on the evaluation in the restarted REPL session.
  • With the cursor at the REPL prompt, use C-up and C-down (Control-↑ and Control-↓) to navigate through command history. Mac users may need to disable some swoopy desktop something-er-other feature that is usually attached to these key combinations.
  • To load the bindings from an SML file called foo.sml, type use "foo.sml"; at the REPL prompt and hit enter. (Use the name of your SML file.)
  • Follow each expression you want to evaluate in the REPL with a semicolon (;) to signal to the REPL that your expression ends here.
  • Definitely read this advice.

Using SML from the Command Line

SML can also run directly on the command line if you prefer not to use the Emacs integration. Save your ML code in a .sml file and then run the command sml < file.sml to evaluate the contents of the file:

$ sml < file.sml
Standard ML of New Jersey v110.87 [built: Fri Sep 27 10:31:34 2019]
- = val f = fn : int -> int

$

This evaluates your bindings and exits the SML/NJ interpreter. Alternatively, you can evaluate the contents of the file and then drop into a REPL to experiment with the bindings you loaded:

$ sml file.sml
Standard ML of New Jersey v110.87 [built: Fri Sep 27 10:31:34 2019]
[opening file.sml]
val f = fn : int -> int
-

Type and evaluate any bindings you wish, then type Control-D (C-d in Emacs-speak) to exit.

Finally, you can also load an empty REPL session and use use to load the bindings from a file. The same use caveats apply here as when starting a REPL session within Emacs.

Standard ML of New Jersey v110.87 [built: Fri Sep 27 10:31:34 2019]
- use "file.sml";
[opening file.sml]
val f = fn : int -> int
val it = () : unit
-

Advice You Will Wish You Followed!

In each REPL session, follow this pattern:

  • First type use "foo.sml"; once for each SML file you want to use.
  • Then use the REPL manually as long as you wish. End each line you type at the prompt with a semicolon (;).
  • After using the REPL to test something, do not use any files. That holds for all files, including files already used in this session.
  • Instead, end and restart your REPL session before continuing.

Why: use "foo.sml" has a very simple semantics: it adds the bindings in the file to the environment in order. These may or may not shadow bindings from the last time you typed use "foo.sml", depending on how foo.sml changed. This confuses even expert programmers until they train themselves to follow the pattern above.

If you find yourself typing the same non-trivial things repeatedly in the REPL, stop wasting your time. Move the repeated parts to a second file, e.g., test.sml. Then, when you restart your session, begin with: - use "foo.sml"; use "test.sml";. - Even better: - Begin test.sml with the line use "foo.sml";. - Then begin your session with use "test.sml";. - DO NOT DO BOTH. That will evaluate the bindings in foo.sml twice, which is confusing.

If you develop some emotional attachment to the transcript of your *sml* buffer in Emacs, you can save it to a file just like any other buffer (C-x C-s – it will prompt for a file name). But after you do, it’s not an *sml* buffer anymore, so you will have to create a new *sml* buffer from a buffer in SML Mode via C-c C-s. If using sml on the command line and your terminal is set to store enough history, you can also copy-paste your REPL session to a file.

Escaping Trouble

  • Evaluation can go into an infinite loop. This has likely occurred if you are not getting the - - (Emacs) or - (command line) prompt back and nothing appears to be happening.
    • Within Emacs: C-c C-c (within the *sml* buffer) will stop evaluation and get your prompt back.
    • On the command line: Control-C will stop evaluation and get your prompt back.
  • If you forget to end your binding with a ; character, the REPL will print = on the next line, meaning, “you are not done – continue your binding.” Type ; and hit Enter. This is not an infinite loop (nothing is being evaluated; the REPL is waiting for you) so C-c C-c (Emacs) or Control-C (command line) do nothing.
  • Part of the printed result you expected may be replaced by a # or ... if it is large. The REPL limits how many characters it prints, which is good since you might make a large value, such as a list with tens of thousands of elements. You can adjust the limit if you want. Add these lines to the top of your file (or run it in the REPL) to do so:

      Control.Print.printDepth := 100;
      Control.Print.printLength := 100;
    

    You can also expand the length of strings that will be printed with (replace ... with a number):

      Control.Print.stringDepth := ...;
    

Error Messages

When the interpreter reports an error, it includes both an error message about your program and an exception trace from within the interpreter itself. (Don’t go looking in the interpreter source code – it won’t help you…)

Standard ML of New Jersey v110.87 [built: Fri Sep 27 10:31:34 2019]
- use "adts.sml";
[opening adts.sml]
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[autoloading done]
adts.sml:199.1-199.5 Error: unbound variable or constructor: sdfs

uncaught exception Error
  raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
             ../compiler/TopLevel/interact/evalloop.sml:44.55
             ../compiler/TopLevel/interact/evalloop.sml:292.17-292.20

Everything from uncaught exception Error on is from within the interpreter. (Yes, the interpreter is written in…SML.)

Some of the error messages can be tough to understand. Check out this guide to SML/NJ error messages.


Acknowledgments: These instructions are adapted from material prepared by Dan Grossman, Hal Perkins, Ben Wood, and various others at the University of Washington. Stefan Monnier provided feedback on the UW CSE 341 version of this document and even created SML Mode version 6 to simplify SML Mode installation substantially.