CS 217: ML Introductory Lab

Part 0: Setting up Emacs

Emacs is a really good editor to use when programming ML, for any number of reasons. You are welcome, of course, to use any editor you like; but I strongly recommend that you learn Emacs.

Most of what we do below will be described via keyboard shortcuts. You can do most of it as well with the menus within Emacs, if you like - but you'll be much faster and more effective in the end if you learn the keyboard techniques.

The settings for emacs are placed in a file called ".emacs" that is contained in your home directory. Have you modified your .emacs before? To find out, change to your home directory and issue the following command:

ls -la .emacs

Look at the right hand column. If you see a symbolic link like

.emacs -> /usr/local/skel/.emacs

then you have no modifications to make. Your .emacs file is a symbolic link to the system standard .emacs, which contains settings for color and ML indentation. Skip the remaining steps in this part and skip to Part 1 of this lab.

On the other hand, if you do not see a symbolic link, then you have modified your .emacs file in the past. Open up your .emacs file (in emacs or any other editor if choice), and add the following lines to the end if they don't already appear in there somewhere:

(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/sml-mode")
(load "sml-mode-startup")

Part 1: Getting started in SML

1. Start up emacs by typing

emacs &

at the command prompt. The "&" means start it as a background job, so that you still have access to the command line if you want it.

2. In emacs, most commands are proceeded by either ctrl-x or alt-x. Open up a Linux shell inside emacs by typing alt-x, then type the phrase run-sml at the M-x prompt at the bottom of the screen. Hit enter at the prompt "ML command: sml".  (Note: if nothing happens when you type alt-x, the alt key may not be configured right on your computer. That's ok - you can simulate hitting alt-x by first hitting the Esc key, letting go, and then hitting the x).

In the bottom half of your emacs window, you should see the following:

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

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

7. 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;

Hit enter.

8. To exit SML, enter ctrl-d. You should see a message that says "process sml finished". Finally, to exit emacs, type ctrl-x, ctrl-c.

Last word of encouragement: emacs is many, many times more powerful than gedit, nedit, pico, and the other editors floating around the system. As a result, it's more difficult to learn. If you stick with it and learn the keyboard shortcuts, however, you'll be able to edit your code considerably faster than when using the other tools (vi religion wars aside). It took me about a month of regular coding to feel comfortable in emacs. I never went back!

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. 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 four 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. Here's how to do it.

1. Shut down emacs by typing ctrl-x, ctrl-c in each.

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

mkdir cs217

from your home directory. Then change to this directory by typing

cd cs217

3. Open an emacs window by typing

emacs &

at the terminal window command prompt.

4. To start working on a program, type ctrl-x, ctrl-f (the f means "find"), and give emacs a directory and 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.

5. Start typing in your 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").

6. Open a second emacs window from the command prompt. Follow the instructions in Part 1 to start up SML within emacs.

7. Run your program in SML. To do this, in the SML window 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 SML window is good for testing out quick ideas.

Part 4: Defining functions

1. Try running the following ML code:

fn x => x + 1;

What does ML return? fn and the => operator return a function without a name, also known as an anonymous function. In this case, you have defined a function to take one argument and add 1 to it.

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 for size:

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 let do? 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 mystery(L) =
  if L = nil then
    L
  else
    mystery(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"]);
["a","b","c"]

Part 6: Pattern Matching

1. The following code is an example of pattern matching in SML. Type it in, and try it. What does it do? Why?

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.
 

Part 7: Iteration and printing

1. 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!