- 7+5 ; val it = 12 : int Assign value to variable "it" if nothing else specified - val x = 7+5; val x = 12 : intThe interpreter keeps reading an expression until you type a semicolon, so you can spread it over several lines. The prompt changes from - to =
- 7 = + = 5; val x=12 : intComments are bracketed by (* ... *)
- 2.0+3.5; val it = 5.5 : real - 2 + 3.5; stdIn:4.1-10.7 Error: operator and operand don't agree [literal] operator domain: int * int operand: int * real in expression: 2 + 3.5
- [2,3,4,5]; val it = [2,3,4,5] : int list - [[2.0,3.2], [], [0.0]]; val it = [[2.0,3.2],[],[0.0]] : real list listUnlike LISP the elements of a list must all be of the same type.
- [2.2,5]; stdIn:8.4-9.8 Error: operator and operand don't agree [literal] operator domain: real * real list operand: real * int list in expression: 2.2 :: 5 :: nil - [1,[2,4]]; stdIn:1.1-4.2 Error: operator and operand don't agree [literal] operator domain: int * int list operand: int * int list list in expression: 1 :: (2 :: 4 :: nil) :: nilBasic list operations:
- 2::[3,5]; (* Corresponds to LISP CONS) val it = [2,3,5] : int list - hd([2,3,5])); (* Corresponds to LISP car) val it = 2 : int; - tl([2,3,4]); (* Corresponds to LISP cdr *) val it = [3,4] : int list - [2,3]@[5,7]; (* Appending two lists *) val it = [2,3,5,7] : int list - []; val it = [] : 'a list (* 'a is a type variable. More below).
- val x = (2, 7.5, "Bob"); val x = (2,7.5,"Bob") : int * real * string #2 x; val it = 7.5 : real
- val bob = { age=32, firstName="Robert", lastName="Jones", weight=183.2}; val bob = {age=32,firstName="Robert",lastName="Jones",weight=183.2} : {age:int, firstName:string, lastName:string, weight:real} - #age bob; val it = 32 : int
- if (1 < 2) then 3 else 4; --> val it = 3 : int - if (1 < 2) then 3 else 4.0; --> stdIn:38.26-39.26 Error: types of if branches do not agree [literal] then branch: int else branch: real in expression: if 1 < 2 then 3 else 4.0 -->-
- fun add3 I = I+3; val add3 = fn : int -> intNote the type: add3 is a function that maps an integer to an integer.
- add3 7; val it = 10 : int - add3 (7); val it = 10 : intFunctions of multiple arguments are actually functions of the corresponding tuple.
- fun times(X,Y) = X*Y; val times = fn : int * int -> int - times(2,3); val it = 6 : int - val t = (2,3); val t = (2,3) : int * int - times t; val it = 6 : int
- fun sumTo(N) = if (N=0) then 0 else N+sumTo(N-1); val sumTo = fn : int -> int - sumTo 10; val it = 55 : int fun member(X,L) = if (L=[]) then false else if hd(L)=X then true else member(X,tl(L)); stdIn:49.24 Warning: calling polyEqual stdIn:49.54 Warning: calling polyEqual val member = fn : ''a * ''a list -> bool - member(5, [1,5,3]); val it = true : bool - fun nth(I,L) = if (I=1) then hd(L) else nth(I-1,tl(L)); val nth = fn : int * 'a list -> 'a - nth (3, ["When", "in", "the", "course"]); val it = "the" : stringMutually recursive functions have to be defined together:
fun f(X) = if X <= 2 then 1 else g(X-1) + g(X-2) and g(X) = if X <= 3 then 1 else f(X-1) + f(X-3);
val (x,y) = (1,2); val x = 1 : int val y = 2 : intMatching lists:
- val x::y = [1,2,3]; stdIn:1.4-1.19 Warning: binding not exhaustive x :: y = ... val x = 1 : int val y = [2,3] : int list - val x::y::z = [1,2,3]; stdIn:1.4-6.3 Warning: binding not exhaustive x :: y :: z = ... val x = 1 : int val y = 2 : int val z = [3] : int list
- fun fib 1=1 | fib 2=1 | fib N = fib(N-1) + fib(N-2); val fib = fn : int -> int - fib 10; val it = 55 : int - fun doubleList [] = [] | doubleList L = 2*hd(L)::doubleList(tl(L)); val doubleList = fn : int list -> int list - doubleList [2,3,5]; val it = [4,6,10] : int list - fun last [h] = h | last (h::t) = last t; stdIn:18.3-18.38 Warning: match nonexhaustive h :: nil => ... h :: t => ... val last = fn : 'a list -> 'a - last [2,3,5]; val it = 5 : int
fun twoToN(N) = if N=0 then 1 else let val p = twoToN(N-1) (* declaration and initialization *) in p+p (* Body of let *) end; (* end of let *) twoToN = fn : int -> int - twoToN 5; val it = 32 : intBinding is sequential:
let val J=5 val I=J+1 val L=[I,J] val Z=(J,L) in (J,I,L,Z) end; val it = (5,6,[6,5],(5,[6,5])) : int * int * int list * (int * int list)
fun reverse(L) = let fun reverse1(L,M) = if L=[] then M else reverse1(tl(L),hd(L)::M); in reverse1(L,[]) end; val reverse = fn : ''a list -> ''a list - reverse [3,4,5]; val it = [5,4,3] : int listVariables are lexically scoped
fun applyList(f,[]) = [] | applyList(f,h::t) = f(h)::applyList(f,t); val applyList = fn : ('a -> 'b) * 'a list -> 'b list fun add3(n) = n+3; val add3 = fn : int -> int - applyList(add3,[2,3,5]); val it = [5,6,8] : int list fun sum(f,l,u) = if u<=l then 0 else f(u) + sum(f,l,u-1); val sum = fn : (int -> int) * int * int -> int
- sum(fn x => x*x*x, 0,10); val it = 3025 : int - (fn x => x*x*x) (6); val it = 216 : int
- fun adder(N) = fn X => X+N; val adder = fn : int -> int -> intThis gives an alternative method for defining multi-variable functions (known as "Currying"):
- adder 7 5 val it = 12 : int - val add3 = adder 3; val add3 = fn : int -> int - add3 7; val it = 10 : int
val add3 = let val I=3 in fn X => X+I end; val add3 = fn : int -> int add3 7; val it = 13 : int;
- fun add3 I = I+3; val add3 = fn : int -> int - fun times(X,Y) = X*Y; val times = fn : int * int -> int fun member(X,L) = if (L=[]) then false else if hd(L)=X then true else member(X,tl(L)); val member = fn : ''a * ''a list -> bool NOTE: member(X,L) takes as argument a tuple (X,L) where X has type ''a and L has type ''a list and returns a Boolean - fun nth(I,L) = if (I=1) then hd(L) else nth(I-1,tl(L)); val nth = fn : int * 'a list -> 'a nth takes as argument a tuple (I,L) where I is an integer and L is a list of objects of type 'a and returns an object of type 'a. fun applyList(f,[]) = [] | applyList(f,h::t) = f(h)::applyList(f,t); val applyList = fn : ('a -> 'b) * 'a list -> 'b list fun sum(f,l,u) = if u<=l then 0 else f(u) + sum(f,l,u-1); val sum = fn : (int -> int) * int * int -> int fun adder(N) = fn X => X+N; val adder = fn : int -> int -> int That is int -> (int -> int). adder takes an integer as argument and returns a function from integers to integers. fun reversePair(x,y) = (y,x); val reversePair = fn : 'a * 'b -> 'b * 'a
Each different occurrence of [] in the program requires a different type parameter. (One may be the base of a list of ints, and the other may be the base of a list of strings.)
fun sum(f,l,u) = if u<=l then 0 else f(u) + sum(f,l,u-1); val sum = fn : (int -> int) * int * int -> int
Assign type(u) = 'a, type(l)='b, type(f)=fun:'c -> 'd,
type(sum) = fun : 'g -> 'h.
(Syntactically, f has to be a function.)
Since sum can return 0, of type int, 'h=int.
Using the expression u-1, since - is either of type int*int -> int or real*real -> real, and type(1)=int, infer 'a=int.
Using the expression f(u), infer 'c=int.
Using the expression u < l, since < is either of type int*int -> bool or real*real -> bool, and type(u)=int, infer 'b=int.
Using the expression f(u) + sum(f,l,u-1), since + is either of type int*int -> int or real*real -> real, and 'h = type(sum(...)) = int, 'd=int.
Note that 'g=type(f,l,u) = (fn : int -> int) * int * int.
fun applyList(f,l) = if (l=[]) then [] else f(hd(l))::applyList(tl(l));Assign type(l) = 'a, type(f) = fun: 'b -> 'c, type(applyList) = fun: 'd -> 'e.
From the expression l=[], since [] is of type 'g list, deduce that 'a = 'g list.
Since hd is of type fun: 'h list -> 'h, and l is of type 'g list, infer the expression hd(l) has type 'g. Hence infer that 'b = 'g.
Since tl is of type fun: 'i list -> 'i list and l is of type 'g list, infer that tl(l) is of type 'g list.
Since :: is of type fun: 'j * 'j list -> 'j list, and f(hd(L)) is of type 'c, infer that f(hd(l))::tl(l) is of type 'c list.
Since applyList may return f(hd(l))::tl(l), infer that 'e = 'c list.
Thus applyList is of type (fun 'g -> 'c) * 'g list -> 'c list
fun largeType(w,x,y,z) = x=(w,w) andalso y=(x,x) andalso z=(y,y); val largeType = fn : ''a * (''a * ''a) * ((''a * ''a) * (''a * ''a)) * (((''a * ''a) * (''a * ''a)) * ((''a * ''a) * (''a * ''a))) -> boolNote that the size of a type may be exponential in the size of the function.
For a detailed discussion of type inference in ML, see Ben Goldberg's class notes (for Honors PL), Polymorphic Type Inference
let val a=[] val b=a val c=1::a val d=1.0::b in (a,b,c,d) end; stdIn:1.1-5.32 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...) val it = ([],[],[1],[1.0]) : ?.X1 list * ?.X2 list * int list * real listNote that "a=b" does not force a and b to have the same type, and that "c=1::a" does not force a to have type int list.
- fun foo(X) = #1 X; stdIn:5.34-8.17 Error: unresolved flex record (can't tell what fields there are besides #1)- fun foo(f,L,M) = (f(L),f(M)); val foo = fn : ('a -> 'b) * 'a * 'a -> 'b * 'b - foo(hd,[1,2],[3,4,5]); val it = (1,3) : int * int - foo(hd,[1,2],[3.0,4.0]); stdIn:1.30-2.24 Error: operator and operand don't agree [literal] operator domain: (int list -> int) * int list * int list operand: (int list -> int) * int list * real list in expression: foo (hd,1 :: 2 :: nil,3.0 :: 4.0 :: nil)