1 Plain SML code
2 Interaction between ML and Scheme
Bibliography

Strandard ML

Chongkai Zhu

This is an implementation of the Standard ML programming language [SML'97]. It features all the requirements of the language, except static type checking. The main aim of this project is to provide interaction between Scheme and ML, i.e, a prgram can be written partly in ML and partly in Scheme, with the ability of either side calling the other side. Part of the standard ML basis library [LIB] is also provided.

1 Plain SML code

To execute ML code in PLT Scheme, simply start your program with

 #lang planet chongkai/sml

No ML interaction mode is supported. To get output from ML code, use the ML print procedure. Notice that the code is executed in an untyped manner.

For example, the following figure is a simple SML program, run in DrScheme.

Our system also supports interoperation between Scheme and ML. For example, put the following code into file "fact.ss":

#lang planet chongkai/sml

fun factorial n = let

  fun fac (0, acc) = acc

    | fac (n, acc) = fac (n-1, n*acc)

  in

    if (n < 0) then raise Fail "negative argument"

    else fac (n, 1)

  end

Then have another Scheme file:

  (require (file "fact.ss"))
  
  (factorial 5)
  (factorial -5)

You will get the result 120 and error message "fact-struct.ss:9:20: Fail" respectively.

You can also split big ML programs into seperate files. The unit of seperation can be either a ML signature, structure, or functor. For example, you can put a ML structure named FOO in file "FOO-struct.ss", and reference it in another file that lives in the same directory (no sepcial syntax needed). The file name of signarue/functor should be "FOO-sig.ss" and "FOO-functor.ss" respectively.

For example, you have file "fact.ss" that have the following content:

#lang planet chongkai/sml

structure fact =

struct

fun factorial n = let

fun fac (0, acc) = acc

    | fac (n, acc) = fac (n-1, n*acc)

  in

    if (n < 0) then raise Fail "negative argument"

    else fac (n, 1)

  end

end

Than for another file "use-fact.ss", which is saved in the same folder as "fact.ss", you can just write

#lang planet chongkai/sml

val it = fact.factorial 5

the variable it will be bound to 120.

"Check Syntax" works with SML code. For example:

2 Interaction between ML and Scheme

Most ML datatypes have a obvious match in PLT Scheme. For example, ML number is just Scheme number; ML string is just Scheme string; ML character is just Scheme character; ML bool is just Scheme bool.

The same holds for simple ML datatype: ML list is just Scheme list; ML array is Scheme mutable vector; ML vector is Scheme immutable vector.

Several compound datatypes need some elaboration. ML records are mapped to Scheme association lists, using symbols as labels. ML tuples are mapped to Scheme vectors. Note that ML vectors and arrays are also mapped to Scheme vectors, but this shouldn’t been a problem.

ML datatype is mapped roughly to structure type in PLT Scheme. For example,

datatype t = FOO | BAR of s;

is equivalent to

  (define-syntax t-type (list #'FOO-datatype #'BAR-datatype))
  (define-values (FOO FOO?)
    (let ()
      (define-struct FOO () #:transparent)
      (values (make-FOO) FOO?)))
  (define-syntax FOO-datatype (list #'FOO #'FOO?))
  (define-values (BAR BAR? BAR-content)
    (let ()
      (define-struct BAR (content) #:transparent)
      (values make-BAR BAR? BAR-content)))
  (define-syntax BAR-datatype (list #'BAR #'BAR? #'BAR-content))

Since information about t is needed only at compile time, it is turned into a define-syntax. ML has a sepearte namespace for datatypes, so a "-type" postfix is added to the name to avoid name clash. FOO and BAR is the actual datatype that is similar to structure in Scheme, so they are also bind at compile time to be a list of constructor, predicate, and accessor. The constructor, predicate, and accessor are no different than the one defined with a Scheme define-struct.

ML exception is mapped to Scheme exn in a similar way:

exception E1;

is equivalent to

  (define-values (E1 E1?)
    (let ()
      (define-struct (E1 exn) () #:transparent)
      (values (lambda (m) (make-E1 "E1" m))
              (lambda (t) (E1? (t (current-continuation-marks)))))))
  (define-syntax E1-datatype
    (list #'E1 #'E1?))

The only difference comparing to ML datatype is that all exceptions are a subtype of the Scheme base structure type exn. The constructor, predicate, and accessor are adjusted to accommodate the added field.

The most used part (but not all) of the Standard ML Basis Library [LIB] are provided. They also serve as a good example of interactions between ML and Scheme. Interested readers are encourged to study the code under /lib subdirectory.

An ML structure is implemented as an "ml-package", which is very similar to scheme/package. It is implemented in (planet chongkai/sml/ml-package). The difference is that ml-package works on symbols while scheme/package works on identifers (syntax object). So they are different and not interchangeable.

See file "sml/lib/List-struct.ss" as an example of how to implement an ML structure directly in Scheme.

The other way around, to access ML code in Scheme, just require the name-struct module, and then open-package, as if the ML code is implemented in Scheme by the aforementioned method.

Bibliography

[SML'97] Robin Milner, Mads Tofte, Robert Harper, David MacQueen, The Definition of Standard ML (Revised), MIT Press, 1997. http://mitpress.mit.edu/book-home.tcl?isbn=0262631814
[LIB] Emden Gansner, John Reppy, “The Standard ML Basis Library,” Cambridge University Press, 2004. http://www.standardml.org/Basis/