Type sml
on Unix machines to get a read-eval-print loop:
- 3 + 5;
val it = 8 : int
- it * 2;
val it = 16 : int
- val six = 3 + 3;
val six = 6 : int
Or pipe a file into the interpreter: sml < file.ml
- fun succ x = x + 1;
val succ = fn : int -> int
- succ 12;
val it = 13 : int
- 17 * (succ 3);
val it = 68 : int;
Or using lambda-notation:
- val succ = fn x => x + 1;
val succ = fn : int -> int
All functions written using recursion and if ... then ... else
(and patterns):
- fun fact n = if n = 0 then 1 else n * fact (n-1);
if ... then ... else
is an expression:
- if 3<4 then "moo" else "cow";
val it = "moo" : string
The types of branches must match in type!
- if 3<4 then "moo" else 2;
stdIn:2.1-2.25 Error: types of if branches do not agree
- fun cylinderVolume diameter height =
let val radius = diameter / 2.0;
fun square y = y * y
in
3.14 * square(radius) * height
end;
val cylinderVolume = fn : real -> real -> real
- cylinderVolume 6.0 6.0;
val it = 169.56 : real
unit
()
bool
true
, false
int
..., ~2, ~1, 0, 1, 2, ...
+
, -
, *
, div
, mod
, abs
=
, <
, <=
, etc.real
3.17, 2.2, ...
+
, -
, *
, /
<
, <=
, etc.int
to real
: 2 + 3.3
is badstring
"moo"
"moo" ^ "cow"
+
,-
, etc. are defined on both int
and real
.
Which one to use depends on operands:
- fun succ x = x + 1
val succ = fn : int -> int
- fun double x = x * 2.0
val double = fn : real -> real
- fun double x = x + x
val double = fn : int -> int
Can add types when type inference does not work.
- fun double (x:real) = x + x;
val double = fn : real -> real
- fun double (x:real) : real = x + x;
val double = fn : real -> real
Example: (14, "moo", true): int * string * bool
Functions can take tuple argument:
- fun power (exp,base) =
if exp = 0 then 1
else base * power(exp-1,base);
val power = fn : int * int -> int
- power(3,2);
Curried power function:
- fun cpower exp =
fn base => if exp = 0 then 1
else base * cpower (exp-1) base;
val cpower = fn : int -> (int -> int)
Or:
- fun cpower exp base =
if exp = 0 then 1
else base * cpower (exp-1) base;
val cpower = fn : int -> (int -> int)
Why is this useful?
- fun cpower exp base =
if exp = 0 then 1
else base * cpower (exp-1) base;
val cpower = fn : int -> (int -> int)
- val square = cpower 2;
val square = fn : int -> int
- square 3;
val it = 9 : int
Like tuple, but with labeled elements.
Example: { name="Gus", salary=3.33, id=11 }:{ name:string, salary:real, id:int };
Selector operator:
- val x = { name="Gus", salary=3.33, id=11 };
- #salary(x);
val it = 3.33 : real
- #name(x);
val it = "Gus" : string
Examples: [1, 2, 3, 4]
, ["wombat", "numbat"]
nil
is empty list (sometimes written []
)
Operations
- length [1,2,3];
val it = 3 : int
(* append: *)
- [1,2] @ [3,4];
val it = [1,2,3,4]: int list
(* cons: *)
- 1::[2,3];
val it = [1,2,3] : int list
(* "mapcar": *)
- map succ [1,2,3];
val it = [2,3,4] : int list
Deconstructing list with hd
and tl
:
- fun product (nums) =
if (nums = nil)
then 1
else (hd nums) * product(tl nums);
val product = fn : int list -> int
- product([5, 2, 3]);
val it = 30 : int;
Avoid this! Use pattern matching instead.
A list is one of two things:
nil
[1, 2, 3] = 1::[2,3] = 1::2::[3] = 1::2::3::nil
We can define function with these two cases:
fun product (nil) = 1
| product (x::xs) = x * product (xs);
fun listInts 0 = [0]
| listInts n = n::listInts(n-1);
listInts 3 [3, 2, 1, 0];
More on patterns for other data types later...
1::2::nil : int list;
"wombat"::"numbat"::nil : string list;
What type of list is nil
?
- nil;
val it = [] : 'a list
'a
is a type variable that represents any type:
1::nil : int list
"a"::nil : string list
fun length (nil) = 0
| length (x::xs) = 1 + length (xs);
What is the type of length
?
How about this one?
fun id x = x;
Patterns can be used in in val
declarations too:
val <pattern> = <exp>;
Examples:
val x = 3;
val tuple = ("moo", "cow");
val (x,y) = tuple;
val myList = [1, 2, 3];
val w::rest = myList;
val v::_ = myList;