Lab #2: ML Introductory Lab

Part 1: Setting up Emacs

Emacs is a really good editor to use when programming ML, for any number of reasons. You're 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.

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 your .emacs setup file by entering ctrl-x, ctrl-f (the f means "file") to get the prompt to open a file, then open the file ~/.emacs . Copy and paste in the following:

;; Comment: Here are the commands to get SML mode setup within emacs
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/sml-mode")
(load "sml-mode-startup")

;; Setup for color use
(if (fboundp 'global-font-lock-mode)
    (global-font-lock-mode t))
(setq font-lock-maximum-decoration t)
(set-face-foreground 'font-lock-comment-face "GreenYellow")

3. Close emacs by typing ctrl-x, ctrl-c (the c means "close"), and start emacs up again. Maximize the window. 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 "~/cs217/ml/prog1.sml". Emacs will open up this file if it exists, or will present you a new blank file.

Type in a very simple ML program just to try it out. For example, enter in the code

val x = 3;

Now save this program by typing ctrl-x, ctrl-s (the s means "save").

4. 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. This opens up a command line prompt within emacs. Change to the directory in which you stored your ML program. For example, if I stored my ML program in the directory ~/cs217/ml, I would type

cd ~/cs217/ml

5. Start up SML by typing

sml

at the prompt. This may take a little time to start up.

6. Run your program in SML. To do this, type

use "prog1.sml";

(or substitute here whatever you named your program).

7. Change your program in some way. For example, change it to read

val x = 8;

Save your program without having to enter the filename again by typing ctrl-x, ctrl-s (the s means "save").

9. Re-run your program. To avoid having to type the "use" command again, use alt-p to move backwards through your command history. alt-n will move forwards.

10. To exit SML, enter ctrl-d. To exit from the command prompt, type exit. You should see a message that says "process shell 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 things you already know

1. At the SML command prompt, enter the following commands

hd [5,2,1,7,8];
hd([5,2,1,7,8]);
tl [5,2,1,7,8];
tl([5,2,1,7,8]);

What do hd and tl do? Why are the parentheses optional?

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

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

["apple", "orange", "pear", "grapefruit"]

3. Execute the following statements. One of them gives an error. Why? What do are the corresponding names for the operators :: and @ in Scheme? (Both of these operators in ML are pronounced the same way in ML as you would pronounce the corresponding operators in Scheme).

(* This is a comment, by the way! *)
1::[2,3];
[1,5]::[2,3];
[1,5]@[2,3];

4. When are lists equal? Experiment and find out. For example, what happens when you enter the following?

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

4. Execute the following code. What do length and reverse do?

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

Part 3: 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. In this case, you have defined a procedure to take one argument and add 1 to it. What is this equivalent to in Scheme?

2. A procedure 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 binding 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. What does let do?

let
  val x = 10
in
  x * x
end;

Part 4: 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)];

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 5: 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:int];

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 6: Iteration and printing

1. ML has iteration constructs, but recursion is much cleaner. Enter and load the following function. 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... continue working on assignment 3!

By the way - if those garbage collection messages bug you, you can enter

SMLofNJ.Internals.GC.messages(false);