1.(a) Theoretical reasons. Functional languages such as ml have sound mathematical basis, ie the lambda calculus. Practical reasons. Generally, when being used in their strict sense (ie without assignment statements) we have referential transparency. This means that we can evaluate arguments in any order. This gives us the opportunity to exploit parallel or distributed hardware. Also, when used strictly, we can prove programs to be correct via induction. 1.(b) The first function mult takes as arguments a pair (x,y). More generally, we might think of a function that takes n arguments (x1,x2,...,xn) as taking a single argument that is an n-tuple. The function prod again can be considered as taking a single argument. When prod is given that single argument a new function is delivered as a result, which can then be applied to the second argument. That is, we apply the function to an argument and this delivers as a result another function, and so on. This has been referred to as "Curried" or "partially applicable" functions, and is a powerful feature of ml. 1.(c) fun convert 0 = [0] |convert 1 = [1] |convert n = if (n mod 2) = 0 then 0::(convert (n div 2)) else 1::(convert (n div 2)); 1.(d) fun add_digits (0,0,0) = (0,0) |add_digits (1,0,0) = (1,0) |add_digits (0,1,0) = (1,0) |add_digits (1,1,0) = (0,1) |add_digits (0,0,1) = (1,0) |add_digits (1,0,1) = (0,1) |add_digits (0,1,1) = (0,1) |add_digits (1,1,1) = (1,1) I can now apply this to a list of arguments fun add_bin 0 [] y = y |add_bin 0 x [] = x |add_bin 1 [] y = add 0 [1] y |add_bin 1 x [] = add 0 x [1] |add_bin carry (x::xs) (y::ys) = let val (r,c) = add_bin (carry,x,y) in r::(add c xs ys) end;