SML/NJ Scripts and the “smlnj-script” Interpreter

Table of Contents

1 Introduction

The program “smlnj-script” is the SML/NJ script intepreter, which provides a way to to run a Standard ML (SML) program as a script, and also provides a few additional features useful for scripts. The interpreter smlnj-script is based on the SML/NJ implementation of SML. The interpreter is, in essence, the SML/NJ compiler (including the SML/NJ run-time machinery) with a few additional utility functions defined and a specialized startup routine.

2 Writing an SML/NJ Script

An SML/NJ script is a program that can make use of these capabilities:

Below are some pointers to resources describing the available language features. If anyone finds any other web sites particularly good at explaining things, please tell me.

2.1 SML

SML is a procedural language with extremely strong support for higher-order functions and abstraction.

In syntax and semantics, SML stays fairly close to the λ-calculus. For example, just like in the λ-calculus, each function in SML has exactly 1 argument, so if you want to pass more than one thing you need to pack them all together into a data structure.

SML is a strongly typed language. Fortunately, most type annotations are optional and will be automatically calculated by the SML implementation. Also, SML/NJ scripts only type check (and parse and compile) each top-level form as it is encountered. So you can at run-time evaluate a string (using SmlnjScriptUtils.useString) or file (using “use”) to supply definitions that will allow later top-level forms to successfully be type checked.

You can learn about SML at locations like:

http://en.wikipedia.org/wiki/Standard_ML

The following texts might be useful to you for learning SML:

Some students have recommended the following books, but the author has some warnings:

2.2 The SML Basis Library and SML/NJ Library

The Standard ML Basis Library is documented at this location:

Most SML implementations (including of course SML/NJ) include “the SML/NJ library” which provides a number of additional useful features not included in the standard basis. The SML/NJ library is partially documented at this location: Unfortunately, there are some features in the SML/NJ library that are only currently documented in the source code. The important files to read are the ones containing the definitions of signatures, which are generally well commented. It is worthwhile to fetch and unpack the source code for the SML/NJ library to get these signature files. For the latest working version of SML/NJ, the SML/NJ library source code can be found at this location: You unpack this file with a command like this:

 tar xfz smlnj-lib.tgz

3 Running an SML/NJ Script

The hallmark of a script on a POSIX (a.k.a. UNIX) system (like any computer running Linux) is that it is a file which has “execute” permission that begins with a line of this form:

 #!PATH-TO-INTERPRETER OPTIONAL-ARGUMENT

The interpreter that an SML/NJ script uses is the program called “smlnj-script”.

In the examples that follow, this text will assume there is a copy of smlnj-script at this file system location (which is true for the HWU (Heriot-Watt University) CS (Computer Science) department's UNIX file systems):

 /u1/staff/jbw/bin/smlnj-script

The ways you can get smlnj-script to be used for an SML/NJ script include the following:

  1. Begin the script with the line

     #!/usr/bin/env smlnj-script
    

    and ensure your PATH environment variable includes a directory containing smlnj-script, for example you could do something like this:

     PATH="$PATH:/u1/staff/jbw/bin/"
    

    This method uses /usr/bin/env as the interpreter. All the “env” program does in this case is search for “smlnj-script” in the PATH and invoke it with the correct arguments.

    After doing the above, the script can simply be executed like any other program.

  2. Make sure the first line of the script to points directly at a location of smlnj-script, for example you could make the first line look like this:

     #!/u1/staff/jbw/bin/smlnj-script
    

    After doing the above, the script can simply be executed like any other program.

  3. You can invoke the interpreter directly passing it the name of the script as its first argument. For example, to run a script Xyzzy with the 3 arguments Plugh, Hello, and Sailor, you could invoke smlnj-script like this (assuming Xyzzy is in the current directory):

     /u1/staff/jbw/bin/smlnj-script Xyzzy Plugh Hello Sailor
    

For methods 1 and 2 above, the script file must have execute permission. You can ensure this for example for a script named Xyzzy by running this command:

 chmod a+x Xyzzy

4 Debugging an SML/NJ Script

SML/NJ (and also the SML/NJ script interpreter “smlnj-script”) provides an interactive “read-eval-print loop” (REPL) which reads individual SML top-level declarations typed by the programmer, then parses, type-checks, compiles, and executes them, and then prints the results. You can get access to the REPL by just running this command:

 smlnj-script

You can also insert an invocation of U.interact in the middle of an SML/NJ script, and it will invoke the REPL to let you inspect top-level definitions. Do so with a line like this:

 val () = SmlnjScriptUtils.interact ();

When you want to continue, just press Control-D (or whatever character is assigned to the “simulate end-of-file” functionality in your terminal window) or evaluate (SmlnjScriptUtils.continue ()) by typing a line like this at the REPL prompt:

 >- SmlnjScriptUtils.continue ();

Unfortunately, definitions that are not at top-level will not be visible to the REPL. If you want to inspect values in the middle of a loop, save the values in some ref cells that are visible in the top-level environment before calling SmlnjScriptUtils.interact, like this:

 val myRefCell = ref (...);
 
 ...
 
 fun runsForever () =
     (...
      myRefCell := (...)
      ...
      SmlnjScriptUtils.interact ()
      ...)

If you find the results printed by the REPL are being truncated (for example, you may see “#” instead of some complicated data), you can get the entire data structure printed by first invoking this (from within your program or from the REPL input prompt):

 SmlnjScriptUtils.raisePrintingLimitsToMax ()

Type error messages generated by SML/NJ can sometimes be quite confusing. If you are having trouble with debugging SML type errors, you may want to use the Skalpel type error slicer for SML which can be found at this location:

There is a web interface into which you can type your erroneous source code, or you can download and install the software for yourself for use in a source code editor. You will get what we think is a much more understandable explanation of how to fix your type error. If you are a student or staff member at HWU, feel free to ask us (currently that is Joe Wells or John Pirie) in person for help using the type error slicer.

5 Additional Features for SML/NJ Scripts

Extending the SML language, the SML Basis Library, and the SML/NJ library, the smlnj-script interpreter provides some additional functions, mostly as part of the SmlnjScriptUtils structure. This structure is also given the short name “U”, and a few of its members are copied into the top-level environment. In addition, there is a new top-level overloaded operator toString, which also has the short name “%”, and which can be extended to handle some user-defined types. Furthermore, SML/NJ's quasiquote feature is turned on by default for use with the new “q” and “qq” Perl-style quoting operators.

Here is a listing of some of the new features:

6 Installing the SML/NJ Script Interpreter

In case you are curious, the source code of smlnj-script is currently available in this directory on the HWU CS department file systems:

 /u1/staff/jbw/smlnj-script-git/smlnj-script/

I recommend against making your own copy of smlnj-script unless it is necessary, because it is quite big, as it includes a full copy of the SML/NJ compiler.

If you have a HWU CS computer account, you don't need a copy of smlnj-script for your own computer because you can log in to the HWU CS department computers from home with SSH, for example you could log in with a command like this:

 ssh linux03.macs.hw.ac.uk

If you want to get the smlnj-script program for your own computer, here is the easiest procedure:

 cd "$HOME"
 git clone ssh://linux02.macs.hw.ac.uk/u1/staff/jbw/smlnj-script-git/smlnj-script
 cd smlnj-script
 make

At this point, you should have a binary at $HOME/smlnj-script/smlnj-script. You can then test it as follows:

 PATH="$HOME/smlnj-script:$PATH"
 cd examples
 ./hello-world

There are various problems that you might encounter. You will need a UNIX machine (any recent Linux will do). The most likely problem is that your installation of SML/NJ (you need SML/NJ installed first) might be missing the heap2exec program or support for the NLFFI foreign function interface. For example, the Ubuntu “smlnj” packages generally have this problem. If so, you will need to reinstall SML/NJ from source with the right options set. Follow the instructions on the SML/NJ web site and make sure the following lines are uncommented in the file config/targets before building SML/NJ:

 request ckit
 ...
 request ml-nlffi-lib
 ...
 request ml-nlffigen
 ...
 request heap2asm

Author: Joe Wells

Date: 2012-02-15 22:40:56