Bind values to names using val
- val pi = 3.1416; > val pi = 3.1416 : real - val a = pi * 2.0 * 2.0; > val a = 12.5664 : real
New bindings mask old bindings
- val a = a / 2.0; > val a = 6.2832 : real - a * 3.0; > val it = 18.8496 : real
Functions can be bound like other values
- val sqr = fn x => x * x; > val sqr = fn : int -> int - val area = fn r => pi * r * r; > val area = fn : real -> real
int
: the integers
0 1 ~1 2 ~2 0xff
…+ - * div mod
real
: the floating point numbers
0.0 ~1.414 2.0 3.94e~7
…+ - * /
The following are defined for both int
and
real
~ + - *
= < <= > >=
You must specify the type if it is otherwise ambiguous.
- fn x:real => x * x; > val it = fn : real -> real
Functions always have one argument and one return type, and
function types are specified using ->
- fn x => x + 1; > val it = fn : int -> int - fn x:real => x + x; > val it = fn : real -> real
To apply a function, give it an argument
- val double = fn x => 2 * x; > val double = fn : int -> int - double 19; > val it = 38 : int - (fn a => a mod 3) 13; > val it = 1 : int
Operators are just infix functions. To get a prefix function,
use op
- op+; > val it = fn : int * int -> int - it (4, 3); > val it = 7 : int - op div (4, 2); > val it = 2 : int
Use fun
to define a function and bind it to a
name in one step
- val makeeven = fn x => 2 * (x div 2); > val makeeven = fn : int -> int - fun makeodd x = 2 * (x div 2) + 1; > val makeodd = fn : int -> int
Functions defined with fun
can be recursive
- fun sumn n = if n = 0 then 0 else n + (sumn (n - 1)); > val sumn = fn : int -> int
A function can return another function
- val f = fn x => fn y => x + y; > val f = fn : int -> int -> int
Consider this function
val f = fn a => fn b => fn c => fn d => a + b + c + d
with type
int -> int -> int -> int -> int
Function types are right-associative, so we can rewrite this as
int -> (int -> (int -> (int -> int)))
Function application is left-associative, so we can use it like this
f 1 5 11 19
which is the same as
(((f 1) 5) 11) 19
This is called a curried function and it is a common way to
write a function with multiple arguments. fun
and
fn
give us a shortcut
fun f a b c d = a + b + c + d
This also allows partial evaluation
- fun adder a b = a + b; > val adder = fn : int -> int -> int - val add5 = adder 5; > val add5 = fn : int -> int - add5 7; > val it = 12 : int
Compare these two versions of compose
- fun compose f g = fn x => f (g x); > val ('a, 'b, 'c) compose = fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b - fun compose f g x = f (g x); > val ('a, 'b, 'c) compose = fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
(this is just the built-in infix operator o
)
Any name not defined as a formal parameter to a function is called a free variable and is resolved from an enclosing scope
- val pi = 3.1416; > val pi = 3.1416 : real - fun area r = pi * r * r; > val area = fn : real -> real - (fn x => fn y => x + y) 1 5; > val it = 6 : int
When a function is defined, it captures the values that are defined at the time the function is defined. The resulting function together with its captured values is called a lexical closure.
- val pi = 4.99; > val pi = 4.99 : real - area 1.0; > val it = 3.1416 : real
Type char
#"A" #"y" #" "
ord : char -> int
integer value of a characterchr : int -> char
= < <= > >=
Type string
"" "A" "Hello, world!\n"
size : string -> int
return the number of
characters in a strings1^s2
return the concatenation of strings
s1
and s2
= < <= > >=
Converting between char
and
string
str : char -> string
explode : string -> char list
implode : char list -> string
Type bool
false true
not : bool -> bool
if p then x else y
p andalso q
p orelse q
Type unit
()
Standard ML defines a library of useful functions, collected
into structures. We won't discuss structures in this course, but
you can use functions in a structure using
Structure.function
The basis library is automatically loaded in SML/NJ. You may need to load it explicitly with Moscow ML.
- load "Math"; > val it = () : unit - fun g u = (Math.sin u)/u; > val g = fn : real -> real
To load your own definitions
- use "myfile.sml";
Basis library functions are listed here:
Tuples allow convenient grouping
- (2, 3); > val it = (2, 3) : int * int - (2, 3.0, #"X"); > val it = (2, 3.0, #"X") : int * real * char
Tuples are another way to pass multiple values to a function
- fun max (a, b) = if a > b then a else b; > val max = fn : int * int -> int
Built-in binary operators (those with two arguments) take pairs
- op+; > val it = fn : int * int -> int - op mod; > val it = fn : int * int -> int - op^; > val it = fn : string * string -> string
Tuples also offer an easy way to return multiple values from a function
- fun split a = (a div 2, (a + 1) div 2); > val split = fn : int -> int * int