fun sign x = if (x>0) then 1; // syntax error
fun sign x = if (x>0) then 1; // needs both "then" and "else"
else ~1;
fun sign x = case (x>0) of // equivalent to "if" expression
true => 1 |
false => ~1;
fun sign x = case (x>0) of
true => 1 ;
What does the interpreter say?
Warning: match nonexhaustiveHow does it behave?
- sign(~10); uncaught Match exception tests.ml:1.14-2.17
fun sign x = case (x>0) of
true => 1 |
false => ~1.0;
tests.ml:1.14-3.21 Error: rules don't agree (tycon mismatch)
expected: bool -> int
found: bool -> real
rule:
false => ~1.0
<pattern 1> => <expression 1> |
<pattern 2> => <expression 2> |
....
<pattern n> => <expression n>
All the expressions should be of the same type. Matches can appear in
function expressions, case expressions, and exception handlers. If there
exist values that do not match any of the patterns, ML gives a warning.
There are some cases where pattern matching does not work as expected.
Consider the defintion of the combinations function. The following definition
leads to an error.
fun comb(_,0) = 1 | comb(n, n) = 1 // invalid pattern | comb(n, m) = comb(n-1, m-1) + comb(n-1, m);Prolog, for example, accepts such a pattern.
and as shown
below.
fun odd n = if (n=0) then false
else even(n-1)
and
even n = if (n=0) then true
else odd(n-1);
function power(m, n)
if (0 == n mod 2) return power(m, n/2)*power(m, n/2);
else return power(m, n/2)*power(m, n/2)*n;
Note that this is inefficient since the subexpression
power(m, n/2) is calculated twice. The code becomes difficult to
read if the subexpressions have a complicated structure. ML allows
local environment using let expression.
fun power(m, n) =
if (n=0) then 1
else if (m=0) then 0
else
let
val a = power(m, n div 2);
in if (n mod 2 = 0) then a*a
else a*a*m
end;
fun printList(nil) = print("\n")
| printList(n::rest) = (
print(n:int);
print(" ");
printList(rest));
[1, 2, 3, 4] -> [1, 4, 9, 16]The built-in function
map does this. But we can write our
own definition.
fun myMap(F, nil) = nil | myMap(F, a::rest) = F(a)::myMap(F, rest); fun square(x:int) = x*x; myMap(square, [1, 2, 3, 4]); // result is [1, 4, 9, 16]
exception EmptyList; fun myReduce(F, nil) = raise EmptyList | myReduce(F, [a]) = a | myReduce(F, a::rest) = F(a, myReduce(F, rest)); fun sum(x, y:int) = x+y; myReduce(sum, [1, 2, 3, 4]); // result is 10
odd which does this. We need to apply this over
the list as shown below.
fun myFilter(P, nil) = nil
| myFilter(P, a::rest)
= if P(a) then a::myFilter(P, rest)
else myFilter(P, rest);
- myFilter(odd, [1, 2, 3, 4]);
val it = [1,3] : int list
fun sum(x, y:int) = x+y; fun sumc x y:int = x+y;Both are valid function definitions. What does ML think of them?
val sum = fn : int * int -> int val sumc = fn : int -> int -> int
sum takes a tuple of type int*int as argument and
returns int. But sumc is more interesting.
It takes an int as argument and returns a function
of type int -> int as result.
- val sumc3 = sumc 3;
val sumc3 = fn : int -> int
- val sum3 = sum 3;
std_in:47.1-47.16 Error: operator and operand don't agree (tycon mismatch)
operator domain: int * int
operand: int
in expression:
sum 3
- sumc3(0);
val it = 3 : int
-
This is called partial instantiation. Can we partially instantiate the right
side arguments? Consider the examples.
- fun sumc1 x = sumc x 1; val sumc1 = fn : int -> int - fun sum1 x = sum(x, 1); val sum1 = fn : int -> int - sumc1(2); val it = 3 : int - sum1(2); val it = 3 : int -
void of C. This type has exactly one
value. unit is the returned type of print and also the
input type of zero argument functions.
option. The
type option T has values NONE or
SOME t where t is a value of type T
(, ) pairs.
Ideally, the evaluation of an expression should not change the
environment. This property is called referential transparency.
The expressins also said to be side-effect free. The C expression
a++ changes the value of the environment and hence is not
referentially transparent.
ML has the facility for function creation. Such functions are associated with two environments: definition environment and activation environment. So a function should always carry its definition environment. The pair (function, defintion environment) is called a closure.
A statement is evaluated mainly for the change of binding it effects.
So a statement is evaluated for the side effect. Conside the assignment
in ML.
val x=10;
Generally the value returned is not of interest. What is of interest is the
binding created. ML minimizes the severity of side effects by making
assignment non-destructive.