Introduction of SML and SML/NJ

This is much inspired by the well-written A Gentle Introduction to ML by Andrew Cumming, Computer Studies, Napier University, Edinburgh. I have written this in a rush, so please forgive my mistakes by correcting me if you spot any.

This is not a gentle introduction to programming. I assume you have existing knowledge of a programming language and understand recursion. I am a student pursuing Computing & Information Systems degree from Goldsmiths, University of London external studies programme, via Singapore Institute of Management. My language preferences are PHP and Python. I do not have prior experience in SML or any pure functional programming language (but I do use lambda) so this tutorial is written partly to further my knowledge on the functional programming language – SML. That said, this tutorial introduces the SML language from a slightly imperative point of view.

From what I know about SML

I didn’t research much on SML, but this is from Wikipedia:

Standard ML (SML) is a general-purpose, modular, functional programming language with compile-time type checking and type inference. It is popular among compiler writers and programming language researchers, as well as in the development of theorem provers.

SML is a modern descendant of the ML programming language used in the Logic for Computable Functions (LCF) theorem-proving project. It is distinctive among widely used languages in that it has a formal specification, given as typing rules and operational semantics in The Definition of Standard ML (1990, revised and simplified as The Definition of Standard ML (Revised) in 1997). (Source: Wikipedia)

Python has multiple implementations like CPython, PyPy and Jython, SML too has multiple implementations which includes SML/NJ, MLton and Moscow ML.

My SML environment

I install SML/NJ in my Windows machine. I use Windows 7 predominantly but SML/NJ works in all major operating systems. For any editing of SML files, I prefer to use Notepad++ which can do some syntax highlights for .sml files in CAML. It’s quite similar in coloring to SML. To aid my learning, I also svn-co-ed out the SML repository but that’s not necessary. This tutorial uses SML/NJ implementation so be sure to have it at least if you are interested in following it through.

Downloading and installing SML/NJ

You can get a copy of SML/NJ here. Just choose the latest available version to download. I then choose to download the one for Microsoft Windows – smlnj.msi. It’s right at the bottom.

Installing SML/NJ is rather easy, just follow through the steps and you’re done.

Different filetypes that SML uses

You can check out the things SML/NJ installs. It’s a bunch of .cm files littered around.

  • .sml files are the source files for SML.
  • .sig files are the signatures of the methods inĀ  the corresponding SML files.
  • .cm files are SML/NJ’s Compilation Manager files. Think of them as compiled files for the .sml.

Honestly I don’t really like the way the folders are organized but that’s not really for me to decide.

Go next

Using the SML/NJ interactive shell

Go to Command Prompt. Then type “sml” and hit your Enter key. You should see something like this:

SML command prompt windows 7

As you can see, I am using “Standard ML of New Jersey v110.69 [built: Wed Dec 31 23:09:16 2008]”. If you want to load an SML source file, type:

[code lang=”bash”]sml “myfile.sml”[/code]

This is helpful for small assignments like the one I am doing right now.

The SML/NJ interactive shell resembles that of Python and Ruby’s. You can type in expressions from the shell and receive immediate feedback. Great for learning the language. Much of this tutorial revolves around using the interactive shell.

To quit the interactive shell, press CTRL+Z and then Enter. This would exit the SML/NJ interactive shell and bring you back to the Windows command prompt.

Let’s try some expressions

We begin by asking SML/NJ how does it do (note that each expression terminates with a semi-colon):

[code lang=”bash”]Standard ML of New Jersey v110.69 [built: Wed Dec 31 23:09:16 2008]
– “How do you do?”;
val it = “How do you do?” : string
-[/code]

SML/NJ returns back that it’s fine:

[code lang=”bash”]val it = “How do you do?” : string[/code]

This is to represent that the last evaluation is “How do you do?” and it is a string. All expressions evaluated are assigned to “it” if not otherwise assigned to any symbol.

Try some of the following:

  • 1 + 1;
  • 1.0 + 1.0;
  • 1.0 + 1;

Here’s what you should be seeing:

[code lang=”bash”]- 1 + 1;
val it = 2 : int
– 1.0 + 1.0;
val it = 2.0 : real
– 1.0 + 1;
stdIn:5.1-5.8 Error: operator and operand don’t agree [literal]
operator domain: real * real
operand: real * int
in expression:
1.0 + 1
-[/code]

Let’s analyze what the above means for us. “1” is an integer. And the “+” is an infix function that allows for an integer on the left and an integer on the right. “+” is for integer or real addition. The second evaluation take “1.0 + 1.0” and returns “2.0” (a real). In the third evaluation “1.0 + 1″ results in an error. Let’s try to analyze the rather confusing error message:

[code lang=”bash”]- 1.0 + 1;
stdIn:5.1-5.8 Error: operator and operand don’t agree [literal]
operator domain: real * real
operand: real * int
in expression:
1.0 + 1
-[/code]

“stdIn:5.1-5.8” is informing the user where the fault occurred, it says that it starts from line 5 character 1 to line 5 character 8. “1.0 + 1;” happens to be my 5th evaluation using the interactive shell and if you do the counting, character 1 to 8 is basically all of “1.0 + 1;”. Now why did it fail?

It turns out the “+” requires the left and right side to be of the same type, either int or real but not a mixture. SML/NJ returns the operator (“+”) domain to tell the user “+” has some rules to follow and it is not complied with.

Basic mathematical operators

These are the various mathematical operators of SML/NJ. I urge you to try out some expressions before you proceed to get the feel of how it works. It’s a little different from the usual programming languages due to the type restrictions but you should get the hang of it in no time. Some of the operators are only for either int or real.

Operator Purpose int real Example
+ Addition Y Y 1 + 1 is 2
Substraction Y Y 3.0 – 1.0 is 2.0
* Multiplication Y Y 3.2 * 2.0 is 6.4
/ Division N Y 3.2 / 2.0 is 1.6
div Division Y N 3 div 2 is 1
mod modulo Y N 13 div 5 is 3

These typical mathematical operators are referred to as infix operators because the operator is in between 2 operands like this: 3 + 5. The infix operator is + and the operands are 3 and 5. Now try this evaluating: (op +)(3, 5);

[code lang=”bash”]- 3 + 5;
val it = 8 : int
– op+ (3, 5);
val it = 8 : int[/code]

Recognize that both evaluations are similar. We’ve just converted the infix to a prefix operator using the “op” keyword.

Basic types

There are basically 4 main types – int, real, char and string. To denote a negative value, use “~”. Therefore “~1.3” is a negative real value. String is denoted with double quotes as shown in the table.

Type Example
int ~3
real 712.13
char #”c”
string “how do you do?”

Note that for character, we can only put in a character of length 1.

String concatenation

String concatenation is done using a caret (^) like the following:

[code lang=”bash”]- “I am ” ^ “fine.”;
val it = “I am fine.” : string[/code]

Now, once again if we mix the types, we will get an error:

[code lang=”bash”]- “Ans ” ^ (1 + 1);
stdIn:20.1-20.17 Error: operator and operand don’t agree [literal]
operator domain: string * string
operand: string * int
in expression:
“Ans ” ^ (1 + 1)[/code]

Looks like SML/NJ is angry again. Try interpreting the error yourself. Can you figure what’s wrong?

The val, the fn and the fun

We previously asked SML how did it do:

[code lang=”bash”]Standard ML of New Jersey v110.69 [built: Wed Dec 31 23:09:16 2008]
– “How do you do?”;
val it = “How do you do?” : string
-[/code]

SML assign “How do you do?” to the symbol ‘it’. ‘it’ always stores the last evaluation binded.

The val keyword

[code lang=”bash”]- val greeting = “How do you do?”;
val greeting = “How do you do?” : string
-[/code]

Using the ‘val’ keyword you can bind “How do you do?” to the symbol ‘greeting’. Now we try to bind an integer to ‘greeting’.

[code lang=”bash”]- val greeting = 1;
val greeting = 1 : int
– greeting = 1;
val it = true : bool
-[/code]

Now you can we attempt (and successfully) binded 1 to the symbol ‘greeting’ which seconds ago used to be a string. ‘greeting’ cannot be mutated, you can however bind a new item to ‘greeting’. Note that using “greeting = 1” checks for equality and is not an assignment.

Writing a function

To write a function, we can use write as follows:

[code lang=”bash”]- fn x => x + 1;
val it = fn : int -> int
– it 24;
val it = 25 : int
-[/code]

The above function adds 1 to argument we input. We can call the function using ‘it 25’ since ‘it’ holds the value of last item used. The function is somewhat anonymous right now, and the next time we call ‘it’, it is no longer holds a function. What good is a function without a name. We can bind a function to a symbol using the ‘val’ keyword as shown below:

[code lang=”bash”]- val add_one = fn x => x + 1;
val add_one = fn : int -> int
– add_one 3;
val it = 4 : int
-[/code]

Now let’s take a look at the simple function we wrote:

[code lang=”bash”]- val add_one = fn x => x + 1;
val add_one = fn : int -> int
-[/code]

This function takes in 1 object ‘x’ as a parameter. The function evaluates ‘x + 1’ and returns the value. This function is then bind to the symbol ‘add_one’. On evaluation of the sentence, SML returns:

[code lang=”bash”]val add_one = fn : int -> int[/code]

This can be read as add_one is a symbol that holds a function. The function takes in a type ‘int’ and returns a type ‘int’. How does SML know what type to return?

Previously, we see that “1.0 + 1;” cannot be evaluated as ‘+’ requires both sides to be the same type. Since the function performs ‘x + 1’ and the right side is an int type, x must be an int type too and so has got to be its return type.

SML does this form of implicit typing all the time, it saves you from typing your symbols. We’ll see type inference again later.

The fun keyword

The ‘fun’ keyword is used in this way:

[code lang=”bash”]- fun add_one x = x + 1;
val add_one = fn : int -> int
-[/code]

Notice the difference with using the val keyword:

[code lang=”bash”]- val add_one = fn x => x + 1;
val add_one = fn : int -> int
-[/code]

The type of the function is exactly the same: int -> int

You can think of the ‘fun’ keyword as an alternate syntax to the ‘val’ keyword. Take note of the difference between the keyword ‘fun’ and ‘fn’. Virtually no one bind functions using the ‘val’ keyword since ‘fun’ looks a lot neater. The reason why ‘val’ is being introduced first is to stress that functions are similar to types like int and real. They can be bind to any symbol as would an integer.

From this point, we are going to use the ‘fun’ keyword.

That’s all for this lesson

This lesson is a little shorter but there’s lots of practice and try out yourself. There’s no learning without trying.

It’s exam time

I haven’t been blogging for a while as it’s exam period. It’s also been busy busy at 24seven. The new product which name you will never guess right.

Today’s paper is on programming, it’s (so-call) advanced concepts. Honestly the whole course’s been a mess but never mind that. I did 3 questions on Java, 2 question on SML and 1 question on Prolog. The Java questions are quite easy. Most of what I studied came out. I planned to do 3 questions out of the 5 Java questions and it went well.

What caught me in a surprise is Prolog which is quite hard, especially the last question which was basically some professor wanting to read some opinion. If I were a computing journalist, I probably choose that question.

SML isn’t that hard. What I studied came out, what I didn’t study I chose not to do. And I quickly went home and I try to see if the average function I wrote work in SML. And it did:

[code lang=”ocaml”]fun average x =
let
fun sum [] = 0.0
| sum (h::t) = h + sum t;
fun count [] = 0.0
| count (h::t) = 1.0 + count t;
in
sum x / count x
end;[/code]

I wrote this function very neatly on the paper. I never had so much sense of achievement writing an SML function.

An alternate way of writing would be probably:

[code lang=”ocaml”]fun average x = foldl op+ 0.0 x / real (length x);[/code]

You gotta love foldl sometimes.

Tomorrow’s paper is database. I probably won’t do too well for that. Lots of memorizing to do and my memory’s failing me.