Frequently Asked Questions
Administrative
- Do I need a SOCS account/username, and if so, how do I get one?
- How do I submit assignments?
- How do I transfer files from my home machine to my SOCS user account?
Getting and Using SML/NJ
- What is SML/NJ?
- How can I install SML/NJ?
- How do I use the SML/NJ interactive shell?
- Using the interactive shell in the console, how do I run SML code I've saved in a file?
- The SML/NJ interpreter is showing
#symbols instead of deeply nested expressions. How can I see the whole expression? - What should I know about SML/NJ's error messages?
Getting and Using Emacs
- What is (1) emacs, (2) SML-mode?
- Can I use SML without emacs?
- Where can I download emacs?
- How do I install (1) GNU Emacs, (2) XEmacs?
- I use Linux. (1) Where can I download SML-mode for emacs? (2) How do I install it?
- I use Windows. (1) Where can I download SML-mode for emacs? (2) How do I install it?
- How do I use emacs?
- Using emacs, how do I run SML code I've saved in a file?
Programming in SML
- What is the SML basis library?
- Can I invoke functions within structures without writing out the fully-qualified function name, that is, without including the structure name?
- Why should I pattern match instead of using equality checks?
- Is there such a thing as a "standard" or "correct" style of writing SML?
Assignments
No questions yet...
Answers
Administrativia
Q: Do I need a SOCS account/username, and if so, how do I get one?
A: Yes, to submit assignments, you need a McGill School of Computer Science (SOCS) account. If you are registered for 302 (or any other COMP course), you are automatically eligible for a SOCS account.
The default login screens of the computers in the labs on the 3rd floor of Trottier
display instructions on how to get a SOCS account. If you have any problems creating
an account, please contact the friendly SOCS systems staff via email at help at
cs dot mcgill dot ca, or, better yet, visit them in person in McConnell
Engineering 209N.
Q: How do I submit assignments?
A: By using the handin file submission tool,
available on the SOCS server mimi.cs.mcgill.ca. On
mimi.cs.mcgill.ca, there is a special file repository called
cs302, which contains directories called hw1,
hw2, etc. Each of these directories contains a subdirectory with your
username, into which you can submit files by using the handin tool. All
file submissions are done indirectly through handin; you need not worry
about where exactly the cs302 repository is. For instance, to submit
files for assignment 1, you would first make sure that the files you want to submit
are located in your network-wide SOCS user account. You would then log in to
mimi.cs.mcgill.ca and use handin to place the relevant files
into the hw1 directory of the cs302
repository. handin will then copy your files into the subdirectory with
your username, from where we will be able to retrieve them. What follows is a more
detailed, step-by-step tutorial on how to use handin.
From any Linux machine (such as the SOCS computers in the Trottier labs), you can open a terminal and run
ssh your_socs_username@mimi.cs.mcgill.ca
to log into your network-wide SOCS user account on mimi.cs.mcgill.ca, the SOCS server on which handin is set up. When logging in, you may be asked for your SOCS password. If you are on a SOCS computer (in cs.mcgill.ca) and are logged in under your username, you need only type
ssh mimi
Once logged in to mimi.cs.mcgill.ca, enter
handin -l cs302
to see what submission directories are available for COMP 302. You should see something like
hw1 2010.01.20 13:35:00 2010.01.22 13:35:00 hw2This means that hw1 is due Jan. 20, 2010 at 1:35 pm, but submission is allowed until Jan. 22 at 1:35 pm (using up late days).
Enter
handin cs302 hwXX
to see what files you have submitted into the submission directory hwXX (if any). If you have not submitted any files, the listing will be empty:
The following input files have been received:
To submit a file ~/somedir/somefile.sml into the directory for assignment 1, for instance, enter
handin cs302 hw1 ~/somedir/somefile.sml
The submission will be confirmed by a message similar to
Submitting /home/user/sheila1/somedir/somefile.sml... ok
Now, when checking which files have been received, we see the file we just submitted:
The following input files have been received: Wed Jan 17 06:41:29 2007 0 bytes somefile.sml
If you submit a file which has the same filename as a previously submitted file, the previous file will get overwritten. Note that all submitted files get timestamped, and listing the files you've submitted displays the date and time when you submitted them.
Note that you can also submit multiple files at once:
handin cs302 hwXX file1.sml file2.sml file3.sml ... fileN.sml
More information on handin can be found here.
If you are working from home, you can log in to mimi.cs.mcgill.ca remotely using ssh (if running Linux) or the SSH Secure Shell Client (if running Windows). The SSH Secure Shell Client for Windows is free for non-commercial and university use.
Note that to submit files using handin, your files need to be located in your SOCS account. This means that if you are submitting remotely, you must transfer your files to your SOCS account (using, for instance, sftp if running Linux, or the SSH File Transfer Client, which comes with the SSH Secure Shell Client, if running Windows) before being able to submit them via handin. See "How do I transfer files from my home machine to my SOCS user account?" for more information.
Q: How do I transfer files from my home machine to my SOCS user account?
A: That depends on what operating system you use. If you use Windows, it's easiest to use a graphical file transfer client, such as the SSH File Transfer Client, which comes with the SSH Secure Shell Client. Other SSH and SFTP clients for Windows are also available, such as WinSCP and PuTTY.
Several graphical SSH and SFTP clients are also available for OS X, such as Fugu and Fetch. Alternatively, OS X also comes with
the command-line tools ssh and sftp (see next paragraph).
UNIX-like operating systems (Linux, BSD, OS X, etc.) include command-line
SSH and SFTP clients. The use of the command-line SSH client
(called ssh) was demonstrated in the answer to the previous question. Using the command-line SFTP client
(called sftp) is not much more difficult. To log in to your SOCS account
from any UNIX-like machine, run
sftp your_socs_username@mimi.cs.mcgill.ca
You may be asked for your SOCS password. Note that since your SOCS account is
available on all SOCS machines, you need not necessarily log in to
mimi.cs.mcgill.ca to transfer files to your SOCS account. Any SOCS
machine will do. (The hostnames of most lab machines and compute servers in the SOCS
domain cs.mcgill.ca can be found here and here, respectively.)
Having logged in to your account via sftp, you will be able to
navigate the filesystem using a subset of the commands available in most shells (run
help when logged in through sftp to see all available
commands). To transfer a file from your local filesystem to the remote filesystem,
run
put foo.sml
This will locate the file foo.sml in the local filesystem (on your
machine), looking in the current directory on your local machine (which will generally
be the directory in which you ran sftp, although you can change it from
an active sftp session), and will transfer it to the current directory on
the remote machine. To go the other way, you can run
get foo.sml
to transfer the file foo.sml from the remote filesystem to your local one.
Rather than using sftp, you can also use a simpler tool called
scp. This is exactly like the cp command for copying files,
except that for the destination file (and even the source file, optionally), you also
specify the hostname of the machine you want to do the copying to. If you're on your
home machine, and you want to copy the file foo.sml to your SOCS
account's home directory, the following will do.
scp foo.sml your_socs_username@mimi.cs.mcgill.ca:~/foo.sml
You will probably be asked for your SOCS password again.
Getting and Using SML/NJ
A: SML/NJ (Standard ML of New Jersey) is an implementation of the programming language SML (Standard ML). After having installed SML/NJ, you can run programs written in SML by feeding them to the SML/NJ interactive shell, which you can launch in a console (see "How do I use the SML/NJ interactive shell?").
A: Follow the instructions for version 110.71. OS-specific instructions are provided for Unix, for OS X and for Windows.
Q: How do I use the SML/NJ interactive shell?
A: SML/NJ provides an interactive interpreter/evaluator: you
can type any SML code, and the code will be evaluated and its return value printed.
Once SML/NJ is installed, the interactive shell can be started by
typing sml at a terminal (or DOS box under Windows). Here is a sample run
of the interactive shell:
Standard ML of New Jersey v110.71 [built: Sun Dec 20 21:33:42 2009]
- val greeting = "hello";
val greeting = "hello" : string
- val greetings = (greeting, "bonjour");
val greetings = ("hello","bonjour") : string * string
- val room = 280;
val room = 280 : int
- let val (en, fr) = greetings
= in
= (en ^ "!", fr ^ " !")
= end;
val it = ("hello!","bonjour !") : string * string
- val (en, fr) = it;
val en = "hello!" : string
val fr = "bonjour !" : string
- en ^ " " ^ Int.toString(room);
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[autoloading done]
val it = "hello! 280" : string
-
(Just ignore the autoloading messages.) You can also write and use functions in the interactive shell:
- fun fac 0 = 1 = | fac n = n * fac (n - 1); val fac = fn : int -> int - fac 5; val it = 120 : int - fac 0; val it = 1 : int
Note that the prompt character - changes into a = when
you are entering multiple lines. SML/NJ will keep asking for more lines until you
type a semicolon ; and press enter.
To exit the interactive shell, type ctrl-d in Linux or OS X,
or ctrl-z followed by enter in Windows.
Q: Using the interactive shell in the console, how do I run SML code I've saved in a file?
A: First, start an
SML/NJ interactive shell session by running sml at the command line. Assuming your code
is in a file
file.sml in the current directory (where you launched the SML/NJ
interactive shell), type
- use "file.sml";
The quotes indicate that the argument is a string and are neccessary. This loads the contents of the file into the interactive shell, including any functions you've written, which you can then call directly from the interactive session.
Note that your code need not be in the current directory. If your file is
~/foo/bar/baz.sml and you launched the SML/NJ interactive shell from any
directory, you can load the file by entering
- use "~/foo/bar/baz.sml";
You can also achieve this by starting SML/NJ with your filename as an argument:
sml file.sml
This is equivalent to starting SML/NJ, then using the use function,
as above, to load in the file. You can also use this approach to quickly feed
multiple files into the SML/NJ interactive shell:
sml file1.sml file2.sml file3.sml ... fileN.sml
Here, too, the filenames can have arbitrary paths.
Q: The SML/NJ interpreter
is showing # symbols instead of deeply nested expressions. How can I see
the whole expression?
A: SML/NJ only shows some fixed number of levels
of a nested expression. This is called the "print depth", and is set to 5 by default.
Set the reference Control.Print.printDepth to change this:
- Control.Print.printDepth := 100;
will set the print depth to 100, for instance. Alternatively, you can set the print depth when you launch SML/NJ, using a command line switch:
sml -Cprint.depth=100
will launch the SML interactive shell and set the print depth to 100. Run
sml -H
to see a list of all such command line switches (there are quite a few).
Q: What should I know about SML/NJ's error messages?
A: Like most programming languages and environments, SML/NJ provides two kinds of feedback: errors, which mean that typechecking failed (your code "didn't compile" and can't be run), and warnings, which aren't fatal, but might result in unpredictable exceptions being raised at runtime. Here's a silly example of one of the more common warnings, nonexhaustive matches:
- fun iszero(0) = true;
stdIn:1.4-1.20 Warning: match nonexhaustive
0 => ...
val iszero = fn : int -> bool
- iszero(0);
val it = true : bool
- iszero(1);
uncaught exception Match [nonexhaustive match failure]
raised at: stdIn:1.20
So iszero typechecks, but the pattern matching doesn't cover all
possible cases for the argument (it only covers 0). Before submitting
code, make sure that no errors are generated. If nonexhaustiveness warnings are
generated, you may have a bug (as with our iszero above); if you're
confident that the missing case is impossible, you should explain why in a comment
(that way, if you're wrong and the missing case is possible, we'll have an
idea of what you were thinking and a reason to consider deducting fewer points).
Getting and Using Emacs
Q: What is (1) emacs, (2) SML-mode?
A: (1) Emacs is a text editor that can do an astounding number of things. It is particularly suitable for writing SML programs because of the existence of SML-mode (see next point). (2) SML-mode is an extension to emacs that, once installed, allows emacs to enter a special mode customized for writing SML code. For instance, SML-mode will allow you to run an SML/NJ session from within emacs. Effectively, this allows you to write code in one buffer inside emacs, then immediately run it in SML/NJ in another buffer by pressing a single key combination. This saves you time, as otherwise, you would have to switch to a terminal or DOS box, launch SML/NJ separately and import your code into there (see "Using the interactive shell in the console, how do I run SML code I've saved in a file?" and "Using emacs, how do I run SML code I've saved in a file?" for more information on how to run SML code through both the SML/NJ interactive shell in the console and emacs).
Q: Can I use SML without emacs?
A: Yes, you can run the SML/NJ interactive shell perfectly well from a console terminal window (or DOS box under Windows) and run any SML code (written using your favourite editor) through it (see "Using the interactive shell in the console, how do I run SML code I've saved in a file?"). However, SML-mode under emacs provides some shortcuts and, with practice, can shorten the time you spend writing code. But it isn't necessary to use emacs, and you won't be evaluated on anything related to it. Emacs has a steep learning curve, so it might be better to begin writing code in an editor you're familiar with, and migrating later, if you like.
Q: Where can I download emacs?
A: There are two main forks of emacs, GNU Emacs and XEmacs, which can be downloaded from their respective sites. The two forks are very similar in behaviour and you can use either one. However, SML-mode was originally written for GNU Emacs, and it can be more difficult to get SML-mode working in XEmacs. Under Linux, it makes no difference which one you use, as SML-mode works well for both, but if using Windows, we would recommend GNU Emacs.
Some insight on why two forks exist is given here, while history and more background information is provided here.
Q: How do I install (1) GNU Emacs, (2) XEmacs?
A: (1) For Emacs, there are instructions for Linux and Windows. (2) For XEmacs, look here.
Q: I use Linux. (1) Where can I download SML-mode for emacs? (2) How do I install it?
A: (1) The latest version of SML-mode for emacs can be downloaded from here. (2) Unpack the SML-mode tarball using tar xvzf sml-mode.tar.gz. Move the files contained in it into some convenient directory, such as ~/emacs/sml-mode. If you use GNU Emacs, add the following lines to the file ~/.emacs, creating it if it doesn't exist:
(add-to-list 'load-path "/home/sam/emacs/sml-mode") (load "sml-mode-startup")
where you must replace "/home/sam/emacs/sml-mode" with the name of the directory that contains the SML-mode files.
If you use XEmacs, you should add the lines above to the file ~/.xemacs/init.el, creating both the ~/.xemacs directory and the ~/.xemacs/init.el file if they don't exist.
If you restart emacs, SML-mode should now work. To test it, launch emacs (with emacs for GNU Emacs and xemacs for XEmacs) and open a file containing SML code (to open a file, use CTRL + x + f, then enter the name of the file). Press ALT + x to move the cursor to the minibuffer (the input line at the bottom of the Emacs window), type in run-sml and press enter. Emacs should ask you what the command is to launch SML (ML command: sml). Press enter to accept the default.
This should open a new buffer running the SML/NJ interactive shell. To test if the communication between the buffers works, place the cursor into the code buffer, then press CTRL + c + b. This should send the code in the buffer you are working on to the SML buffer and transfer control to the SML buffer, where you can play around with the code you've just written. See "How do I use emacs?" for more information.
Q: I use Windows. (1) Where can I download SML-mode for emacs? (2) How do I install it?
A: (1) The latest version of SML-mode for emacs can be downloaded as a zip file from here. (2) This depends on whether you want to use GNU Emacs or XEmacs. Getting SML-mode to work with XEmacs under Windows can be tricky, since SML-mode was originally written for GNU Emacs. Since we've been unable to get SML-mode to work with XEmacs in Windows, we strongly recommend using GNU Emacs or, better yet, Linux. But here's how to get SML-mode working with GNU Emacs under Windows.
First, download sml-mode.zip and unzip its contents into a temporary folder.
In the folder into which you unpacked GNU Emacs (eg, C:\Program Files\emacs-21.3
there should be a folder called site-lisp. Create a folder in
site-lisp called sml-mode. Move the SML-mode files (which you have
unpacked from sml-mode.zip) into this
site-lisp\sml-mode folder.
Next, copy and paste the following two lines at the bottom of the file
site-start.el in the site-lisp folder (if such a file does
not exist, create it):
(add-to-list 'load-path
"c:\\progra~1\\emacs\\site-lisp\\sml-mode") (load "sml-mode-startup")
where you must replace c:\\progra~1\\emacs\\site-lisp\\sml-mode by the
location of the site-lisp\sml-mode folder on your computer. Note that you
need to use double backslashes (\\) in this path. If you restart GNU
Emacs, SML-mode should now work. To test it, launch Emacs
(bin\runemacs.exe in the Emacs folder) and open a file containing SML
code (to open a file, use ctrl-x ctrl-f, then enter the name of the file;
if you enter the name of a folder, Emacs will list the folder contents and will let
you browse the folder). Press ALT + x to move the cursor to the
minibuffer (the input line at the bottom of the Emacs window), type in
run-sml and press enter. Emacs should ask you what the
command is to launch SML (ML command: sml). Press enter to
accept the default.
This should open a new buffer running the SML/NJ interactive shell. To test if the
communication between the buffers works, place the cursor into the code buffer, then
press CTRL + c + b. This should send the code in the buffer you are
working on to the SML buffer and transfer control to the SML buffer, where you can
play around with the code you've just written. See "How do I use emacs?" for more information.
A: Learning to use emacs is a lifelong endeavour, but here are a few key combinations that might come in handy. Again, though, bear in mind that you need not use emacs to use SML. See also "Using emacs, how do I run SML code I've saved in a file?".
Command |
Action |
|
exit emacs |
|
save the file |
|
send filel to sml buffer |
|
open file |
|
split window horizontally |
|
split window vertically |
|
display current window only, hides split ones |
|
go to next window |
|
kill current window (buffer) |
|
go to start of line |
|
go to end of line |
|
cut the rest of the current line into the kill buffer |
|
paste ("yank") the contents of the kill buffer |
|
mark |
|
cut text from the mark to the cursor into the kill buffer |
|
starts an SML buffer |
|
starts a shell buffer |
Q: Using emacs, how do I run SML code I've saved in a file?
A: We assume you've installed emacs and SML-mode for emacs (see the previous questions and their answers for how to go about doing this). Start emacs by typing
emacs filename.sml
(in this case, we're using GNU Emacs; XEmacs is launched by typing
xemacs) then use ctrl-c ctrl-b to start SML/NJ in emacs. Hit
enter twice to confirm and run alt-x sml-mode and you'll have a new
window with the same results as loading the file into the SML/NJ interactive shell. To
reload a file in the SML/NJ buffer, just send it to the SML/NJ buffer again by typing
ctrl-c ctrl-b.
Programming in SML
Q: What is the SML basis library?
A: The SML Basis Library is a collection of functions and constants provided with almost every SML implementation. They are organized into structures, which can be thought of as modules, which you will learn about during the course. For instance, the Math structure provides a function called sqrt, of type real -> real. It is called by qualifying the name of the function with the name of the structure:
- Math.sqrt(43.2); val it = 6.57267069006 : real
Other useful functions provided by the Math structure are
Math.sin, Math.cos, Math.tan,
Math.pow, as well as some real constants like Math.pi and
Math.e. Note that an identifier like sqrt, by itself, will,
in general, be unbound, although some common functions like
List.length are aliased by top-level identifiers. You can call such functions
without qualifying the call with the structure name:
- List.length [1,2,3]; val it = 3 : int - length [1,2,3]; val it = 3 : int
There is also a structure called Real, providing operations on
reals, such as Real.toString, Real.fromString,
Real.fromInt, etc.. Again, several of these are aliased by various
top-level identifiers. For instance, instance of writing Real.fromInt(3),
you could write simply real(3).
Descriptions and types of all functions in the basis library can be found by exploring the SML Basis Library website, so it's worth it to browse the basis library to see what's available, in particular before implementing a function that seems like it might be common.
Here are some more examples from an interactive shell session (note that the it identifier refers to the result of the last evaluation):
- 4.0 + Real.fromInt(2);
val it = 6.0 : real
- Math.sqrt(it);
val it = 2.44948974278 : real
- case Bool.fromString("true") of
= SOME(true) => String.explode(b)
= | _ => nil;
val it = [#"t",#"r",#"u",#"e"] : char list
(The String.explode function is also aliased
by a top-level explode, so qualifying the function name is
unnecessary in this case.)
Q: Can I invoke functions within structures without writing out the fully-qualified function name, that is, without including the structure name?
A: In general, you can invoke a function in a given structure by
using structure_name.function_name, but if you'll be using functions in
the structure a lot, and there are no other top-level functions with the same names as
those in the structure, then you can open the structure
structure_name using the open keyword, and then call the
function by just function_name:
- open String; opening String type char = ?.char type string = ?.string val maxSize : int val size : string -> int val sub : string * int -> char val extract : string * int * int option -> string val substring : string * int * int -> string val ^ : string * string -> string val concat : string list -> string val concatWith : string -> string list -> string val str : char -> string val implode : char list -> string val explode : string -> char list val map : (char -> char) -> string -> string val translate : (char -> string) -> string -> string val tokens : (char -> bool) -> string -> string list val fields : (char -> bool) -> string -> string list val isPrefix : string -> string -> bool val isSubstring : string -> string -> bool val isSuffix : string -> string -> bool val compare : string * string -> order val collate : (char * char -> order) -> string * string -> order val < : string * string -> bool val <= : string * string -> bool val > : string * string -> bool val >= : string * string -> bool val fromString : String.string -> string option val toString : string -> String.string val fromCString : String.string -> string option val toCString : string -> String.string - isPrefix "Plate" "Plateau"; val it = true : bool
You must be very careful when opening structures. For instance, if you open two structures with identically named declarations, the declaration from the most recently opened structure will get bound to the "overridden" identifier. SML/NJ will not issue a warning in this case.
If you need to refer many times to declarations in the same structure, you can make your code more concise while maintaining clarity by declaring your own name for the structure:
- structure S = String; [autoloading] [library $SMLNJ-BASIS/basis.cm is stable] [autoloading done] structure S : STRING - S.isPrefix "ab" "abcd"; val it = true : bool
This is more readable than opening the structure, because it's clear that isPrefix
is part of a structure; the reader just needs to find the structure S = String
declaration.
Since using open interactively prints all the definitions in the structure,
it can be useful as a crude documentation tool. If I'm trying to remember if
String has something for checking prefixes, I'll use open.
Q: Why should I pattern match instead of using equality checks?
A: Let's say that p, the input to some function we're
writing, is expected to be a pair that consists of an int and a
bool. Then our function, taking p as an argument, should
not look like this:
fun f p = if p = (3, true) then ...
nor should it look like this:
fun f p = let val (i, b) = p in ... end
(Although this is better than the first version.) Ideally, it should be:
fun f (i, b) = ...
with the two variables bound in the function body.
This is not just a stylistic issue. There are good reasons for using pattern matching
wherever possible, which have to do with things called equality types. Recall that
'a is the generic polymorphic type. For example, an 'a list is a list
that contains elements of some arbitrary type. SML also has a weaker kind of polymorphic
type, the equality type, denoted ''a (two apostrophes). Equality types are
types whose inhabitants can be compared using the = operator. These include
ints, bools, strings, but not, for instance,
reals or functions:
- 3 = 4;
val it = false : bool
- "foo" = "foo";
val it = true : bool
- 3.0 = 1.0;
stdIn:2.15-3.9 Error: operator and operand don't agree [equality type required]
operator domain: ''Z * ''Z
operand: real * real
in expression:
3.0 = 1.0
- (fn x => x + 1) = (fn x => x - 1);
stdIn:1.1-3.12 Error: operator and operand don't agree [equality type required]
operator domain: ''Z * ''Z
operand: (int -> int) * (int -> int)
in expression:
(fn x => x + 1) = (fn x => x - 1)
(Note that any datatype built up inductively from equality types is also an
equality type.) The idea is that if you want to write a function that takes, as an
argument, a generally polymorphic type (eg, an 'a list), then you mustn't
use the = operator on the argument to the function, or SML's type
inference engine will force the input argument to be an equality type.
For instance, a function for testing whether a list is empty,
fun empty_bad list = (list = nil)
will not have type 'a list -> bool, but ''a list ->
bool, even though you never need to use = to compare elements of
the list. If you use pattern matching instead:
fun empty_good nil = true | empty_good list = false
then the type is 'a list -> bool. The bottom line is: use pattern
matching whenever you can, and only use the = operator on polymorphic
types if you absolutely must (but it's fine to use = if the things
you're comparing aren't of polymorphic type).
Q: Is there such a thing as a "standard" or "correct" style of writing SML?
A: Like most programming languages, SML has its own idioms and conventions that tend to encourage good coding practices. Here are two SML style guides:
- About Coding Style, an SML style guide written by students at CMU.
- An SML Style Guide for a course at Cornell.
You're not required to slavishly follow these guides, of course, but it's a good idea to skim over them, to get an idea of what's considered "good" or "bad" SML.