Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
COMP 302: Programming Languages and Paradigms, Fall 2008 -- FAQ
[go: Go Back, main page]

Frequently Asked Questions

Administrativia

Getting and Using SML/NJ

Getting and Using Emacs

Programming in SML

Assignments


Answers

Administrativia

Q: Do I need a SOCS account/username, and if so, how do I get one?

A: Yes, you need a McGill School of Computer Science (SOCS) account in order to be able to submit assignments electronically. If you are registered for the course (or any other COMP course), you are automatically eligible for a SOCS account.

The default log in screens of the SOCS computers in the Trottier labs (on the 3rd floor of Trottier) display instructions on how to obtain a SOCS account. If you encounter any difficulties during the automated account creation process, please contact the friendly SOCS sytems staff via email at help at cs dot mcgill dot ca, or, better yet, visit them in person in ENGMC 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 ass01, ass02, 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 ask handin to place the relevant files into the ass01 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 cs302

to see what submission directories are available for COMP 302. You should see something like

Existing subdirectories (comments in parentheses):
ass01
ass02
ass03
ass04
ass05

Enter

handin cs302 assXX

to see what files you have submitted into the submission directory assXX (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 ass01 ~/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 assXX 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 MacOS, such as Fugu and Fetch. Alternatively, MacOS also comes with implementations of the command-line tools ssh and sftp (see next paragraph).

Most UNIX-like operating systems (Linux, BSD, MacOS, etc.) come with command-line implementations of SSH and SFTP clients. The usage 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.

There are probably graphical SSH and SFTP clients for UNIX-like operating systems, as well, but if you're a UNIX user, I'm sure you don't need or want to use them. :)


Getting and Using SML/NJ

Q: What is 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?").


Q: Where can I download SML/NJ?

A: At the official SML/NJ site. The specific files you have to download for the most recent version are linked to on this page, where you can also find installation instructions for Unix-like operating systems, Macs, and Windows.


Q: I'm using operating system <insert your favourite OS here>. How do I install SML/NJ?

A: Fairly comprehensive installation information for Unix-like OS's, MacOS X, and Windows is provided on the official SML/NJ site, on this page. OS-specific information is provided in the files INSTALL (for Unix-like OS's), WININSTALL (for Windows), and MACOSXINSTALL (for MacOS X).


Q: How do I use the SML/NJ interactive shell?

A: SML/NJ provides an interactive interpreter/evaluator, into which you can type any SML code. It will be immediately evaluated and the return value will be shown. 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.59 [built: Thu Jul 27 11:22:51 2006]
- val foo = "hello";
val foo = "hello" : string
- val bar = "world";
val bar = "world" : string
- val baz = 123;
val baz = 123 : int
- foo ^ ", " ^ bar ^ "! " ^ Int.toString(baz);
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[autoloading done]
val it = "hello, world! 123" : string
- val foo = nil;
val foo = [] : 'a list
- 1::2::3::nil = [1,2,3];
val it = true : bool
- case [1, 2, 3] of nil => 0 | (h1::(h2::t)) => h2 | _ => ~1;
val it = 2 : int

(You can safely 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. (Note that you should terminate every statement with a semicolon, ;, before pressing enter.)

To exit the interactive shell, type in your operating system's end-of-file (EOF) character (ctrl-d in Linux, 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: Suppose you've written some SML code in a file, file.sml, and you want to test it or play around with it. First, start an SML/NJ interactive shell session by running sml at the command line (a terminal under Linux or a DOS box under Windows), and suppose your code is in a file file.sml in the current directory (where you launched the SML/NJ interactive shell). In the interactive shell, enter

- 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. Moreover, 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: The SML interpreter only shows some fixed number of levels of a nested expression. This is called the "print depth", and is set to 5 by default. The reference Control.Print.printDepth can be set 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 first launch the SML/NJ interpreter, by using the following 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 options (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, try to make sure that neither errors nor warnings are generated (or explain why you think they're generated, if you can't get rid of them -- sometimes warnings are, in fact, mostly harmless).


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 nifty shortcuts and, with practice, can significantly shorten the time you spend writing code. But is it not necessary to use emacs, and you will not 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 your 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) If you're running Linux, look here. For information on how to obtain and install GNU Emacs for Windows, look here. (2) XEmacs download and installation information for Unix-like OS's, MacOS, and Windows is provided 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.


Q: How do I use emacs?

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

ctrl-x ctrl-c

exit emacs

ctrl-x ctrl-s

save the file

ctrl-c ctrl-b

send filel to sml buffer

ctrl-x ctrl-f

open file

ctrl-x 2

split window horizontally

ctrl-x 3

split window vertically

ctrl-x 1

display current window only, hides split ones

ctrl-x ctrl-o

go to next window

ctrl-x k

kill current window (buffer)

ctrl-a

home

ctrl-e

end

ctrl-k

kill everything on the current line after the cursor

alt-x sml

starts an SML buffer

alt-x shell

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 very common functions, like List.length are aliased by top-level identifiers like length. Such functions can be called 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 actually 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 simply calling 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 "foo" "foobar";
val it = true : bool

Be warned, however, that you must be very careful when opening structures. If you open two structures with the same identifier, for instance, then the one from the most recently opened structure is the one that is bound to the "overridden" identifier. SML/NJ will not issue a warning in this case (since it's a perfectly valid instance of rebinding a top-level binder). In general, you should never need to open structures, so it's probably best to simply qualify all accesses to things inside them.


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. Eg, 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 types of the things you're comparing are not polymorphic).


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:

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.


Assignments