![]() |
![]() | |||||||||||||||||||||||||||||||||||
|
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]()
|
Introduction and OverviewML (which stands for Meta Language) is a family of applicative programming languages with functional control structures, strict semantics, a strict polymorphic type system, and parametrized modules. It includes Standard ML, Lazy ML, CAML, CAML Light, and various research languages. Implementations are available on many platforms, including PCs, mainframes, most models of workstation, multi-processors and supercomputers.The version used in this course is Standard ML of New Jersey (SML/NJ). SML/NJ is a compiler, interactive system (interpreter), and programming environment for the Standard ML language. History and StandardsML (Meta Language) was originally developed in the late 1970's at the University of Edinburgh in Scotland by Mike Gordon and a handful of collaborators. The language ML was part of a theorem proving system, and was intended for describing and implementing proof strategies. The system was called LCF (The Logic of Computable Functions) and was designed to prove facts about programs written in a relatively simple language called PCF. ML was not originally intended by it's designers as a general-purpose stand-alone language.Standard ML is one of the few languages with a full formal definition. It was first designed in 1983-1988 and was based on an older language (also called ml) but with many improvements, simplifications and extensions. (Robin Milner, Mads Tofte and Robert Harper, The Definition of Standard ML, MIT Press, 1990). There was a small revision to the language in 1997 along with the introduction of a standardised library and finalised as SML 97. (see Robin Milner, Mads Tofte and Robert Harper, The Definition of Standard ML (revised), MIT Press, 1997, ISBN: 0-262-63181-4). The name of the revised language remains Standard ML, but is also refered to as Standard ML '97 or SML '97 to distinguish it from the 1990 version, which is referred to as SML '90. It should be noted that the Standard ML core language is not a pure applicative programming language, it is a higher-order procedural language with an applicative subset. ![]() Standard ML of New Jersey (SML/NJ) is an implementation of ML which was developed jointly at Bell Laboratories, Princeton University, and recently Yale University. It is under continuing development by researchers in the Languages and Applied Logic group at Bell Labs (Lucent) together with researchers at Princeton University and the University of Pennsylvania. The SML/NJ distribution includes CM (a separate compilation manager), ML-Yacc, ML-Lex, ML-Burg, the SML/NJ Library, Concurrent ML, eXene (a multithreaded X-windows toolkit), and the SML/NJ-C interface library. Version 110 of SML/NJ (released in February 1998), is largely compliant with the new SML '97 Definition, including the new Basis library. Functional Programming and SML/NJFunction evaluation is the basic concept for a programming paradigm that has been implemented in the SML/NJ programming language (and its relatives).The basic mode of computation in SML/NJ, as in other functional languages, is the use of the definition and application of functions. In functional programming, the following are true:
SML Terminology
Identifiers and Reserved WordsThe following table lists the reserved words in SML:
Identifiers are strings of symbols starting with a letter or an apostrophe (') and containing letters, digits, and the underscore character ( _ ). Identifiers beginning with the apostrophe are considered type identifiers. SML BasicsIn general terms, SML programs are written in the style of C or Pascal programs.The latest SML interpreter has been installed on the et105 server. You can telnet to your account on this machine by using et105.ni.utoledo.edu. Once connected to et105, activate the SML interpreter by typing sml at the UNIX prompt. The example below illustrates the activation of the SML interpreter on et105: et105:~/sml$ sml Standard ML of New Jersey, Version 110.0.6, October 31, 1999 -Here are some basics:
Using New Jersey Standard ML (SML/NJ) the following example illustrates a simple "Hello World" entry: - "Hello World"; val it = "Hello World" : stringAs mentioned above, when used normally the ML interpreter accepts expressions and evaluates them. The result is printed to the screen together with the type of the result. The last result calculated may be referred to as it. In the "Hello World" example above the interpreter does not have to do any work to calculate the value of the expression entered as the expression is already in its simplest (or normal) form. Here is a numeric expression that is also in its simplest form: - 456; val it = 456 : intThe string val indicates that the expression denoted a value (rather than a datatype). The string it = indicates that the interpreter has taken the liberty of assigning the result to a variable it for you just in case you want to use the value again. The string 456 denotes the value of the expression and the string : int indicates that the value is of the integer type. You can type ML expressions to your heart's content and the interpreter will faithfully read them, evaluate them and print the result as shown above. When you are all through you can exit SML/NJ and return to the Unix prompt by typing Ctrl-D at the prompt. The following example illustrates a calculation. The expression 3+4 is evaluated to the value 7 and this result is displayed: - 3+4; val it = 7 : intNotice that the expression to be evaluated is terminated by a semicolon. The interpreter allows expressions to go over more than one line. Where this happens the prompt changes to the equal symbol (=), for example: - 4 + 4 + = 4; val it = 12 : intHere is an example comparison: - 2=3; val it = false : bool
print("hi\n");
hi
val it = () : unit
The syntax of the SML print is similar to the Perl print
statement.
The following is a variation on the "Hello World" example which uses print:
- print("hello world!\n");
hello world!
val it = () : unit
Taking this a step further, the following example is taken from your text,
and illustrates yet another variation of the "Hello World" program:
- fun PrintHello(x:int) = if x=2
= then print("Hello World\n")
= else print("Goodbye World\n");
val PrintHello = fn : int -> unit
To run this program (actually, call this function), do the following:
- PrintHello(1); Goodbye World val it = () : unit - PrintHello(2); Hello World val it = () : unitYou can also build a new function from an old one (as shown in your text): - fun Doit() = PrintHello(2); val Doit = fn : unit -> unit - Doit(); Hello World val it = () : unit - fun Notit() = PrintHello(3); val Notit = fn : unit -> unit - Notit(); Goodbye World val it = () : unit Simple TypesThe examples below illustrate simple data types in SML:4, 22 int 4.3, 10.5E3 real "hello", "12", "\n" string #"w" char true, false boolEach of these data types is an expression and can be typed at the SML prompt, allowing you to get the system to confirm its format and type. The following two examples illustrate this fact: - 10.5E3; val it = 10500.0 : real - #"w"; val it = #"w" : charSML never performs implicit coercions, all coercions must be explicit. For example: - SML90.sqrt(4.0); val it = 2.0 : real - floor(it); val it = 2 : int - real(it); val it = 2.0 : real - SML90.exp(2.0); val it = 7.38905609893 : real - floor(it); val it = 7 : intThis causes special difficulty with respect to operations +,-,*,/, they are overloaded but uses must not be ambiguous: if one operand is an integer the other must be as well. Explicit coersions between lists and strings are accomplished with the built-in functions explode and implode. The explode function converts from a string into a list and the implode function takes a string list and returns a single string. LetSML includes a let expression. The syntax of the let allows for local variable declarations. The syntax for the let is:let val x = ... in ... end;This is somewhat analogous to local variable definition
{
int x;
...
}
in C. However, in SML the variable x is given a value that is
immutable (i.e., it can never change). The following example is illustrative:
- let val x = 4 in x+3 end; val it = 7 : intDeclarations typed in at the top level are like an open-ended let: - val x=4; val x = 4 : int - val y = x+3; val y = 7 : int - x*x; val it = 16 : int - x*y; val it = 28 : int - val z=x+2*y; val z = 18 : intNotice how the types are being inferred. SML Operators & Built-in FunctionsSML has a number of built-in operators, functions and data types. For instance, it provides the standard arithmetic operators, math functions, and trigonometric functions. Take some time to study the examples in the following runtime sequence. They illustrate most of the common arithmetic operators and a few built-in functions:- 3+2; val it = 5 : int - 8-2; val it = 6 : int - 7*6; val it = 42 : int - 4 div 2; val it = 2 : int - 5 mod 2; val it = 1 : int - 12.0/3.0; val it = 4.0 : real - chr(65); val it = #"A" : char - SML90.sqrt(2.0); val it = 1.41421356237309 : real - SML90.sin 3.14159; val it = 2.65358979323E~06 : real - abs(2); val it = 2 : int - 3-7; val it = ~4 : int - abs(it); val it = 4 : int - abs(~7); val it = 7 : intNote that the tilde (~) character is used for negative numbers. Thus, typing -4; produces an error since the dash (-) indicates subtraction and not negation. As illustrated in the example interaction above, negative 4 is entered as ~4; in SML. Also, note that a few of the built-in functions are prefixed with SML90., an object-oriented notation. This notation is necessary because these functions are no longer available (globally) at the top-level of the interpreter. The ^ operator is used for string concatenation, for example: - "hello" ^ "world"; val it = "helloworld" : stringThe Boolean values true and false are available,as are logical operators such as not (negation), andalso (conjunction), and orelse (disjunction). - not(true); val it = false : bool - true andalso false; val it = false : boolSML is a strongly typed language in that all (well-formed) expressions have a type that can be determined by examining the expression. As part of the evaluation process, SML determines the type of the output value. Infix IdentifiersThe top-level environment has the following infix identifiers:infix 7 * / div mod infix 6 + - ^ infixr 5 :: @ infix 4 = <> > >= < <= infix 3 := o infix 0 beforeAll of the above are infix operators. That is, the operator appears between the two arguments. Binding Names to ValuesIn SML one can associate identifiers with values, e.g., assignment. The example below illustrates a simple assignment:- val three = 3; val three = 3 : intand thereby establish a new value binding, - three; val it = 3 : intMore complex expressions can also be used to bind values to names, - val five = 3+2; val five = 5 : intNames can then be used in other expressions, - three + five; val it = 8 : int Expression-BasedSML is expression-based, there are no pure "commands" or "statements" as there are in Pascal and C. In SML commands/statements are actually expressions in that they return values. For example, the following is called an if expression:- (if (2=3) then 5 else 6) + 1; val it = 7 : intOne (sometimes annoying) consequence of the above is that the two branches of the if expression must to return the same type. To illustrate this point, look at the following example:
- if (2=3) then 5 else 6.5;
stdIn:49.1-49.25 Error: types of rules don't agree [literal]
earlier rule(s): bool -> int
this rule: bool -> real
in rule:
false => 6.5
GC #0.0.0.0.2.49: (10 ms)
Defining FunctionsA function may be defined using the keyword fun. Simple function definitions take the form:fun <fun_name> <parameter> = <expression>;For example, each of the following defines a function: fun double x = 2*x; fun inc x = x+1; fun adda s = s ^ "a";These functions may be entered just as shown above. To call (execute) a function, at the SML prompt simply type the function name followed by the actual argument and a semicolon (;). For example, the functions defined above can be called as follows: double 5; inc 99; adda "mom";The SML/NJ system should give you the values 10 : int and 100 : int and "moma" : string for the expressions above. The following example provides a more detailed example of calling one of these functions: - fun double x = 2*x; val double = fn : int -> int - double 4; val it = 8 : int - double(4); val it = 8 : intHere is another example of how you can define and call a simple SML function: - fun triple(x) = 3*x; val triple = fn : int -> intdeclares triple as a function from integers to integers. We can call this function as follows: - triple(222); val it = 666 : intYou should have noticed that parentheses ( ) can be used in defining and calling a function ... or NOT. Both methods work equally well. If we call a function with an inappropriate argument (one of the wrong type), we will get an error message, as shown below: - triple(2.0); Error: operator and operand don't agreeYou may also explicitly specify types: - fun max(x:int,y:int,z:int) = = if ((x>y) andalso (x>z)) then x = else (if (y>z) then y else z); val max = fn : int * int * int -> int - max(3,2,2); val it = 3 : intHere is another function, saved as a file named power.sml:
fun power (x,k) : real =
if k=1 then x
else if k mod 2 = 0
then power(x*x, k div 2)
else x*power(x*x, k div 2);
When you run this program, you will see the following:
- use "power.sml"; [opening power.sml] GC #0.0.0.0.1.9: (20 ms) val power = fn : real * int -> real val it = () : unitAnd then, calling the function: - power(3.4,3); val it = 39.304 : real Recursive DefinitionsThe use of recursive definitions is a main characteristic of functional programming languages.These languages strongly encourage the use of recursion as a structuring mechanism in preference to iterative constructs such as while-loops. Here is an example of a recursive function: - fun factorial(x) = if x=0 then 1 = else x*factorial(x-1); val factorial = fn : int -> intThe definition is used by SML to evaluate applications of the function to specific arguments. - factorial(7); val it = 5040 : int - factorial(12); val it = 479001600 : int ListsAnother built-in data structure in SML are lists.A list in SML is essentially a finite sequence of objects, all of the same type:
- [1,2,3]; val it = [1,2,3] : int list - [true,false, true]; val it = [true,false,true] : bool list - [[1,2,3],[4,5],[6]]; val it = [[1,2,3],[4,5],[6]] : int list listThe last example is a list of lists of integers, in SML notation int list list. All objects in a list must be of the same type: - [1,[2]]; Error: operator and operand don't agree The empty list is denoted by the following symbol: - []; val it = [] : 'a listNote that the type is described in terms of a type variable 'a, as a list of objects of type 'a. Instantiating the type variable, by types such as int, results in (different) empty lists of corresponding types. Operations on ListsSML provides some predefined functions for manipulating lists.The function hd returns the first element of its argument list. - hd[1,2,3]; val it = 1 : int - hd[[1,2],[3]]; val it = [1,2] : int listApplying this function to the empty list will result in an error. The function tl removes the first element of its argument lists, and returns the remaining list. - tl[1,2,3]; val it = [2,3] : int list - tl[[1,2],[3]]; val it = [[3]] : int list listThe application of this function to the empty list will also result in an error. The types of the two functions are as follows: - hd; val it = fn : 'a list -> 'a - tl; val it = fn : 'a list -> 'a list More List OperationsLists can be constructed by the (binary) function :: that adds its first argument to the front of the second argument.- 5::[]; val it = [5] : int list - 1::[2,3]; val it = [1,2,3] : int list - [1,2]::[[3],[4,5,6,7]]; val it = [[1,2],[3],[4,5,6,7]] : int list listAgain, the arguments must be of the right type: - [1]::[2,3]; Error: operator and operand don't agree List can also be compared for equality: - [1,2,3]=[1,2,3]; val it = true : bool - [1,2]=[2,1]; val it = false : bool - tl[1] = []; val it = true : bool Defining List FunctionsRecursion is particularly useful for defining list processing functions.For example, consider the problem of defining an SML function, call it concat, that takes as arguments two lists of the same type and returns the concatenated list. (This function is actually defined in the next section.) For example, the following applications of the function concat should yield the indicated responses. - concat([1,2],[3]); val it = [1,2,3] : int list - concat([],[1,2]); val it = [1,2] : int list - concat([1,2],[]); val it = [1,2] : int listIn defining such list processing functions, it is helpful to keep in mind that a list is either
- [1,2,3]=1::[2,3]; val it = true : bool Concatenation of ListsIn designing a function for concatenating two lists x and y we thus distinguish two cases, depending on the form of x:
This suggests the following recursive definition. - fun concat(x,y) = if x=[] then y = else hd(x)::concat(tl(x),y); val concat = fn : ''a list * ''a list -> ''a listThis seems to work (at least on some examples): - concat([1,2],[3,4,5]); val it = [1,2,3,4,5] : int list - concat([],[1,2]); val it = [1,2] : int list - concat([1,2],[]); val it = [1,2] : int list - concat([],[]); val it = [] : ''a list Array Summing ProgramsThe following program is a modification of the program found in your book (page 602). The modifications allow this program to run using the SML/NJ system.
(* original version of summing program *)
fun digit(c:char):int =
ord(c) - ord(#"0");
fun SumNext(V:char list) =
if V=[] then
(
print ("\n Sum=");
0
)
else
(
print(Char.toString( hd(V) ));
SumNext(tl(V)) + digit(hd(V))
);
fun SumValues(x:string):int =
SumNext(explode(x));
fun ProcessData() =
(
let
val infile = SML90.open_in("data.sml");
val count = digit(hd(explode(SML90.input(infile,1))))
in
print (Int.toString (SumValues(SML90.input(infile,count))))
end;
print("\n")
);
Note that the SML90. is objected-oriented notation which allows
the use of depreciated functionality from an earlier version of ML.
Below, is another version of the program above which does not use depreciated functionality:
(* uses no depreciated elements *)
fun digit(c:char):int =
ord(c) - ord(#"0");
fun SumNext(V:char list) =
if V=[] then
(
print ("\n Sum=");
0
)
else
(
print(Char.toString( hd(V) ));
SumNext(tl(V)) + digit(hd(V))
);
fun SumValues(x:string):int =
SumNext(explode(x));
fun ProcessData() =
(
let
val infile = TextIO.openIn("data.sml");
val myline = TextIO.inputLine(infile);
val count = digit(hd(explode(myline)))
in
print (Int.toString (SumValues(substring(myline,1,count))))
end;
print("\n")
);
Try running both versions of this program using a data file which contains
the following numbers:
555533... where the first number in the file indicates how many numbers follow and are to be added. The first program should yield the following runtime sequence: - use "prog.sml"; [opening prog.sml] val digit = fn : char -> int val SumNext = fn : char list -> int val SumValues = fn : string -> int val ProcessData = fn : unit -> unit val it = () : unit - ProcessData(); 55533 Sum=21 val it = () : unitYou will need to type two commands (shown in red above) to get this sequence. The first command use "prog.sml"; loads the program. The second command ProcessData(); calls the primary function defined in the program. As with the first version, the second program will yield the following (or similar) runtime sequence: - use "prognew.sml"; [opening prognew.sml] val digit = fn : char -> int GC #0.0.0.0.1.14: (10 ms) val SumNext = fn : char list -> int val SumValues = fn : string -> int val ProcessData = fn : unit -> unit val it = () : unit - ProcessData(); 55533 Sum=21 val it = () : unitAs before, you will need to load (use) the program and call the primary function that it defines. To assure that you understand what these programs are doing, modify the data file and run each of the programs again. AssignmentComplete the following assignment before our next meeting:
- fun PrintHello(x:int) = if x=2
= then print("Hello World\n")
= else print("Goodbye World\n");
val PrintHello = fn : int -> unit
| |||||||||||||||||||||||||||||||||||
| |
| Added to the Web: November 28, 1999. Last updated: November 29, 1999. Web page design by Dan Solarek. |
![]() http://cset.sp.utoledo.edu/cset4250/ |