ML means "Meta Language." It is a "purely" functional/applicative language (this is actually a white lie, but we'll focus primarily on the functional part).
Application areas:
- val someNumber = 16; (* this is what we type *) > val someNumber = 16 : int (* this is what the interpreter spits back *) - val x = true; > val x = true : bool - val y = (13 + 5) div 2; > val y = 9 : int - y; > val it = 9 : intNotice in the above examples, that ML infers the type of the variable for us. Also, when we evaluate an arbitrary expression, the interpreter names the returned value "it."
Tuples
- val t = (44.0, true, "foo"); (* comment: build a tuple *) > val t = (44.0, true, "foo") : real * bool * string - #3t; (* access the third element of t *) > val it = "foo" : stringNotice that the
*
used in the type value of the above
tuple has a different meaning than *
for numbers. Above,
it should be read as "cross-product." For example, the tuple (1,
10.0) has the type int * real
, or the set of integers
crossed with the set of real numbers.
Lists
- val mylist = [44.0, 34.0, 24.0] (* makes a list of type real list. *) - val mylist = 44.0::34.0::24.0::nil (* ditto *) - val mylist = [44.0]@[34.0]@[24.0] (* ditto *) - mylist; > val it = [44.0, 34.0, 24.0] : real list
hd(mylist)
corresponds to (first mylist)
in Scheme.
tl(mylist)
corresponds to (rest mylist)
in Scheme.
The ::
(cons operator) is right-associative. @
is the append operator for lists.
fun function-name (arg1, arg2, ...) = expression; (* general form *) - fun addtwo (x) = x+2.0; (* define a simple function *) > val addtwo = fn : real -> realNotice that ML deduced the type of the function. How? Why might ML complain about the following function?
- fun myadd (x, y) = x+y;ML uses sophisticated type inference mechanisms that allow us to avoid explicitly declaring most types. However, in the above declaration, it could not determine the types of the arguments or result type. Here's a fix:
- fun myadd (x:real, y) = x+y; > val myadd = fn: real * real -> realNotice that the type of myadd is actually a function that maps tuples of two reals to reals. In fact, functions in ML can only take one argument. We can write functions which take multiple arguments by having them take tuples of arguments. Here is an example of a recursive function. As with Scheme, IF together with recursion provide our main control construct.
- fun append (x,y) = if x=nil then y else hd(x)::append(tl(x), y); > val append = fn : ''a list * ''a list -> ''a list
int list list of integers [int] int * int list a tuple (int, [int]) (int * int) list a list of tuples [(int, int)] int * int list -> real a fun mapping tuples (ints, [int]) to reals Here is the order of precidence from high to low: list * ->
;; Scheme version of adding one to every element of a list: (define (add1-to-list lst) (cond ((null? lst) lst) (#t (cons (+ 1 (car lst)) (add1-to-list (cdr list)))))) (* ML version of adding one to every element of list: *) - fun add1_to_list (nil) = nil | add1_to_list (x::xs) = (1+x) :: add1_to_list(xs); > val add1_to_list = fn: int list -> int list (* General pattern matching function form *) fun functionName (pattern1) = expression1 | functionName (pattern2) = expression2 ... | functionName (patternN) = expressionN;How does pattern matching work?
1. If eNode or pNode are variables then the trees match. 2. If eNode and pNode are constants and eNode == pNode then the trees match. 3. If eNode and pNode are operators AND eNode == pNode AND the children of eNode match the children of pNode then the trees match. 4. Otherwise declare a mismatch.Which of the following are matches?
x::(a,b,c)::z with [(1,2,3), (4,5,6), (7,8,9)] x::y::qs with [1,2] x::y::qs with [1] x@y with [1,2,3] x+2 with 2_ is a special wildcard symbol which can appear in a pattern. It differs from a normal identifier in that it can appear multiple times in a pattern and cannot be referenced in the associated expression. The special keyword "as" allows us to reuse whole patterns. Examples:
fun foo (x, _, _) = x*x*x; fun append (nil, y) = y | append (x, nil) = x | append (x::xs, y) = x::append(xs,y); fun merge (nil, L2) = L2 | merge (L1, nil) = L1 | merge (L1 as x::xs, L2 as y::ys) = if (x < y) then x::merge(xs, L2) else y::merge(L1, ys); (* common compile time error: *) fun sumList (nil) = 0; | sumList (a::as) = a + sumList(as);
(* general form: *) let val <var1> = <expr1>; val <var2> = <expr2>; ... val <varN> = <exprN> in <expression> end (* an example of a fast exponentiator *) fun fastexp(x, 0) = 1 | fastexp(x, n) = let val temp = fastexp(x,n div 2) in if (n mod 2 = 0) then temp * temp else temp * temp * x end; (* you can also declare temporary functions *) (* here is a fast version of reverse. *) fun reverse(x) = let fun help(nil, result) = result | help(x::xs, result) = help(xs, x::result) in help(x, nil) end; (* here is a slow version of reverse, why is it slower? *) fun reverse (nil) = nil | reverse (x::xs) = reverse(xs)@[x];
dugan@cs.washington.edu (Last Update: )