Frequently Asked Questions

How do I use the provided Tiger code with SML/NJ?

SML/NJ has a newer version of the Compilation Manager (CM) than when the book was written, so you'll have to make a few small changes. First, to load your code in SML/NJ, you need to provide sources.cm as an argument to the sml command:

   % sml -m sources.cm

The -m flag causes SML/NJ to compile and load your files eagerly, which will catch errors sooner. You can try omitting the -m flag and see what happens.

If you are running SML/NJ from Emacs's SML mode, set the compilation command to CM.make("sources.cm").

You also need to make three changes to your sources.cm:

  • SML/NJ libraries such as sml-nj.cm need to be prefixed by $/.
  • You'll need to include the SML/NJ libraries $/basis.cm and $/ml-yacc-lib.cm.
  • SML/NJ now uses a different lexer generator by default, so to get it to use ML-LEX, change your tiger.lex line to read tiger.lex: mllex, which tells CM to start the lexer generator in ML-LEX compatibility mode.

Here is my sources.cm from Homework 1:

    Group is

    driver.sml
    errormsg.sml
    tokens.sig
    tokens.sml
    tiger.lex: mllex

    $/smlnj-lib.cm
    $/basis.cm
    $/ml-yacc-lib.cm
Do I have to write every record field every time?

For record construction, yes. For record pattern matching, no.

If the field name and the variable you'd like to assign are the same, you don't have to mention them twice. You can also use an ellipsis (three dots) to stand in for as many fields as you'd like to leave out from the pattern match. For example, from assem.sml (homework 6):

    fn OPER{assem, dst, src, jump=NONE}   => speak(assem, dst, src, nil)
     | OPER{assem, dst, src, jump=SOME j} => speak(assem, dst, src, j)
     | LABEL{assem, ...}                  => assem
     | MOVE{assem, dst, src}              => speak(assem, [dst], [src], nil)

Also, if you have a record value, each fields name prepended by a hash may be used as a selector: #assem oper.

What do these "flex record" error messages mean?

For any mention of a record, SML/NJ needs to know exactly what fields the record has. When you use ... patterns or # selectors, the type inference may not be able to figure out what fields you've left out, and then it will complain. Here's an example:

    type point = { x: int, y: int, c: color }

    fun quadrant1 {x, y, ...} = x > 0 andalso y > 0

There are three simple ways to fix this:

  • Make your pattern match mention all the fields:
        fun quadrant1 {x, y, color} = x > 0 andalso y > 0
  • Use a type annotation on the pattern to specify what type you are matching:
        fun quadrant1 ({x, y, ...}: point) = x > 0 andalso y > 0
  • Constrain the type with an datatype constructor:
        datatype point = PT of { x: int, y: int, c: color }
    
        fun quadrant1 (PT {x, y, ...}) = x > 0 andalso y > 0

The latter two strategies also work with field selectors:

    fun quadrant1 (p: point) = #x p > 0 andalso #y p > 0
How can I make lookup tables / associative arrays / finite maps?

The SML/NJ library (not the Standard Basis Library) provides a functor BinaryMapFn that constructs applicative (purely functional) maps, given an orderable key type. For example, to create mappings with string keys, you could do:

    structure StringMap = BinaryMapFn(struct
        type ord_key = string
        val compare = String.compare
    end)

    val myMap  = StringMap.empty
    val myMap2 = StringMap.insert (myMap, "hello", 4)

    fun whatIsHello m =
      case StringMap.find (m, "hello")
        of SOME i => i
         | NONE   => raise NoGreetingHere

The resulting structure satisfies the ORD_MAP signature.

How do I call an SML function from the command line?
   % echo 'compile "test.tig";' | sml sources.cm

Last updated 9 January 2008.

Valid XHTML 1.1 Valid CSS!