CS 217: SML Introductory Lab

Work through the following lab, and finish up in your own time if you need it. Turn in your code for Part 6 #3 within a week. Keep in mind that in addition to this lab, there are SML resources linked on the course page if you need help. You may turn this in in pairs.

The questions that you find intermixed throughout the lab are for you to think about. Ask in lab or in class if the answers aren't in clear.

Part 1: Getting started in SML

  1. Start up emacs by typing

    emacs &

  2. Open up a Linux shell inside emacs by typing alt-x, then type shell at the M-x prompt at the bottom of the screen. Type sml at the Linux prompt you see.

    In your emacs window, you should see the following:

    Standard ML of New Jersey, Version 110.0.7, September 28, 2000 [CM; autoload enabled]
    -

  3. Type in a very simple ML program just to try it out. At the prompt, enter in the code

    val x = 3;

    SML should return the following:

    val x = 3 : int

  4. Change your program in some way. Use alt-p to move backwards through your command history (alt-n will move forwards), and change it to read

    val x = 8;

  5. To exit SML, enter ctrl-d. Enter ctrl-d again to log out from the Linux prompt. You should see a message that says "process sml finished". Finally, to exit Emacs, type ctrl-x, ctrl-c.

Part 2: Lists and SML basics

  1. Start up Emacs and SML again. At the SML command prompt, enter the following commands:

    val numbers = [6,4,9,2];
    val fruits = ["apple","orange","pineapple"];

    What "type" do the brackets define?

    (By the way - if garbage collection messages looking something like
    GC #0.0.0.0.1.12:   (0 ms)

    start bothering you, you can enter
    SMLofNJ.Internals.GC.messages(false);
    to make them go away.)

  2. 2. Enter in the following:

    hd ["apple","orange","pineapple"];
    tl ["apple","orange","pineapple"];
    

    What do the functions hd and tl do?

    The response that you get from SML indicates the answer in a variable called it, as well as a type (either string, or string list). Why is the type different for the functions hd and tl?

  3. Write a sequence of hd and tl that will pick the string "pear" out of the following expression.

    ["apple", "orange", "pear", "grapefruit"]
    [[["apple"], ["orange"], ["pear"], ["grapefruit"]]]
    
  4. Execute the following statements. The last three produce errors. Why?

    (* This is a comment, by the way! *)
    1::[2,3];
    "apple"::["pear","orange"];
    [1,5]@[2,3];
    ["apple","grapefruit"]@["pear","orange"];
    [2,"apple"];
    2::["apple","orange"];
    "apple"::[5,7];
    
  5. When are lists equal? Experiment and find out. For example, what happens when you enter the following?

    [1,2]=[1,2,3];

  6. Execute the following code. What do length and rev do?

    length ["plato","socrates","aristotle"];
    rev ["plato","socrates","aristotle"];
    

Part 3: Saving your code

Entering your code interactively is fun, but not a good idea for creating large programs. A better way to go is to create a file with your code, save it, then run it inside SML. We do this in exactly the same way we did with Scheme:

  1. In the terminal window, make a directory to store your code for this class in. For example, you can type from your home directory

    cd cs217

    Create a subdirectory from here by typing

    mkdir sml

    Then change to this directory by typing

    cd sml

  2. Open an Emacs window by typing

    emacs &

    at the terminal window command prompt.

  3. To start working on a program, type ctrl-x, ctrl-f (the f means find), and provide Emacs with a filename. For example, I might type prog1.sml. Emacs will open up this file if it exists, or will present you a new blank file.

  4. Start typing in some SML code. Use any of the above examples that you wish. When finished, save your program by typing ctrl-x, ctrl-s (the s means save).

  5. Open up a second window inside emacs by typing ctrl-x, 2 (the 2 indicates two windows). To switch between windows, you can either use the mouse or enter the command ctrl-x, o (the o means "other"). Switch to the bottom window, enter alt-x, and at the command prompt type the word shell. Change to the directory in which you stored your SML program. For example, if I stored my Scheme program in the directory ~/cs217/sml, I would type

    cd ~/cs217/sml

  6. Startup SML by typing sml in the shell window.

  7. Run your program. To do this, type

    use "prog1.sml";

    (or substitute here whatever you named your program).

    You should generally use this approach for entering and running SML code, but entering code directly into the shell window is good for testing out quick ideas.

Part 4: Defining functions

  1. Try running the following SML code:

    fn x => x + 1;

    What does SML return? fn and the => operator return a function without a name. How does this compare to Scheme?

  2. A function without a name is useless, as it is immediately garbage collected. Try this instead:

    val addOne =
      fn x => x + 1;
    

    (Use the "tab" key to indent your code. Emacs will indent all your code correctly, automatically.)

    Then try:

    addOne 5;

    The val statement created a pointer to a function, called addOne, which refers to the function you just created.

  3. The above can be entered via shorthand as

    fun addOne x =
      x + 1;
    

    Try it! Remember, this is just shorthand for the slightly longer code above.

  4. Try this:

    val anotherAddOne = addOne;
    anotherAddOne 5;

    At the pointer level, what is happening here? Draw a picture on this paper indicating what is happening.

  5. Try the following code.

    let
      val x = 10
    in
      x * x
    end;
    
    x;
    

    What does letdo? Why do you get an error when you try to see the value of x after the "let block" has ended?

Part 5: Recursion

  1. Enter and load the following function. What does it do? Why?

    fun lessOfAMystery(L) =
      if L = nil then
        L
      else
        lessOfAMystery(tl(L)) @ [hd(L)];
    

    Note that I've used parentheses around the function arguments. This is optional (in the cases we have seen so far).

  2. Write a recursive function, keepFirstN, that returns a list of the first n elements in a list. You may assume that there are at least n elements.

    Example:

    - keepFirstN(3,["a","b","c","d","e","f","g","h","i"]);
    val it = ["a","b","c"] : int list
    

Part 6: Pattern Matching

  1. The following code does something familiar. Type it in, and try it. Why does it work? What is it doing?

    fun mystery([]) = []
      | mystery(start::rest) =
          mystery(rest) @ [start];
    
  2. Here are a few more examples of pattern matching. Try them and figure out what's going on. What does the underscore do?

    fun fact(1) = 1
      | fact(n) = n*fact(n-1);
    
    fun last([]) = ~1
      | last(first::nil) = first
      | last(_::rest) = last(rest);
    
  3. Rewrite keepFirstN using pattern matching. Submit your code in a file called keepfirstn.sml.

Part 7: Iteration and printing

As a functional language, SML lends itself much better to recursion than iteration. The following code is an example of how you would approach iteration. Enter and run the following code. What does it do? Why? What do the parentheses on the lines by themselves do?

let fun loop(count) =
 (
  print("Count = ");
  print(Int.toString(count));
  print("\n");
  if count < 10 then
      loop(count+1)
  else
      count
  )
in
    loop(0)
end;
 

If you still have time... start working on the SML assignment!