- fun f(x) = x; val f = fn : 'a -> 'aNow, suppose that we want to write the expression
not(f(true));in a language like C or Pascal you could eliminate the external parentheses, and write
not f(true);In ML, however, if we write such a thing, we get
- not f(true); stdIn:117.1-117.12 Error: operator and operand don't agree [tycon mismatch] operator domain: bool operand: 'Z -> 'Z in expression: not fThe reason is that ML tries to interpret the above expression as
(not f)(true)This is because in an expression of the form
op1 op2 constit might be the case that op1 is an higher order function, which takes as an argument a function op2, and gives as result something that can be applied to const. In a language without higher order this interpretation would not make sense, and therefore the rules of priority are usually defined so that op1 op2 const is parsed as op1 (op2 const)
In general, in ML, an expression of the form
e1 e2 e3 ... enis parsed as
(...((e1 e2) e3) ... en)Of course this is just the default. In ML, like in other languages, there are some priority rules (like the precedence of "*" over "+") that may alter this order. In general, if you are unsure of how an expression is parsed, just put explicit parentheses to make sure that it is parsed the way you want.
One simplification that is always allowed in ML, however, is the elimination of the innermost parentheses around a "token". In other words, we can always write f x instead of f(x). Hence, for instance, we can write the identity function as
fun f x = x;and the expression not(f(true)) as
not (f true);
For instance, consider the following function which gives the maximum of a list of integers:
fun maxlist [x] = x | maxlist (x::l) = let val y = maxlist l in if x < y then y else x end;if you compile this definition in SML, you'll get a non-exhaustive matching warning. In fact, the case of emptylist is missing: If you write the expression
maxlist you will get a run-time error.
When the missing cases corresponds to arguments for which the value of the function is undefined, the correct way to eliminate the warnings would be by introducing exceptions (ML allows to handle exceptions in a way similar to C++ and Java).
However, if you are sure that the missing cases do not correspond to interesting cases (i.e. to cases that may present in input) then you can just ignore the warnings of non-ehaustive matching.
For instance, suppose that you want to write a function which construct a balanced tree from a list, and that to this purpose you need to define an internal auxiliary function
half: int * 'a list -> 'a list * 'a listsuch that half(n,l) gives as result the two lists obtained by dividing l in two lists of equal length (plus or minus 1), and suppose that, in your intention, n represents the length of the original list l.
Then you probably will give a definition of the following form:
fun half(0, nil) = ... | half(n,(x::l)) = ... ;this will give you a non-exhaustive matching warning. (since the case (0,non-empty) is missing.) However, if you make sure that in your main program you always call your auxiliary function with an expression of the form
half(n,l);where n is defined as the length of l, then your program will never give a runtime error and you don't need to worry about the warning.
- fun same_length (nil,nil) = true | same_length (x::l,y::k) = same_length(l,k) | same_length(l,k) = false; val same_length = fn : 'a list * 'b list -> bool - - fun same_list(nil,nil) = true | same_list(x::l,y::k) = x=y andalso same_list(l,k) | same_list(l,k) = false; val same_list = fn : ''a list * ''a list -> boolAs we can see, the ML type inference mechanism gives for same_list a type different from the one given for same_length, i.e. we get a parameter ''a instead of 'a. This is due to the fact that, in the definition of same_list, we make an equality test between two elements of the lists (x and y). The double quote notation, in ML, is used to represent a type where equality is defined (equality type).
Examples of equality types are integers, booleans, characters, reals, and any other structure (predefined or used-defined) made by equality types. For instance, pairs of equality types, lists of equality types, trees of equality types etc.
Examples of types on which equality is not defined are functional types and everything constructed with functional types. Thus, if we call the two functions above with lists of functions as arguments, same_length will give an answer true or false while same_list will give a type error.
- same_length([1,2],[2,3]); val it = true : bool - same_list([1,2],[2,3]); val it = false : bool - fun f x = x; val f = fn : 'a -> 'a - fun g x = x; val g = fn : 'a -> 'a - same_length([f],[g]); val it = true : bool - same_list([f],[g]); stdIn:24.1-24.19 Error: operator and operand don't agree [equality type required] operator domain: ''Z list * ''Z list operand: ('Y -> 'Y) list * ('X -> 'X) list in expression: same_list (f :: nil,g :: nil)
datatype 'a btree = emptybt | consbt of 'a * 'a btree * 'a btree;
fun in_visit emptybt =  | in_visit (consbt(x,t1,t2)) = (in_visit t1) @ [x] @ (in_visit t2);
fun pre_visit emptybt =  | pre_visit (consbt(x,t1,t2)) = [x] @ (pre_visit t1) @ (pre_visit t2);
fun post_visit emptybt =  | post_visit (consbt(x,t1,t2)) = (post_visit t1) @ (post_visit t2) @ [x];
- in_visit (consbt(1,consbt(2,emptybt,emptybt),consbt(3,emptybt,emptybt))); val it = [2,1,3] : int list - pre_visit(consbt(1,consbt(2,emptybt,emptybt),consbt(3,emptybt,emptybt))); val it = [1,2,3] : int list - post_visit(consbt(1,consbt(2,emptybt,emptybt),consbt(3,emptybt,emptybt))); val it = [2,3,1] : int list
fun convert_to_0 emptybt = emptybt | convert_to_0 (consbt(x,t1,t2)) = let val u1 = convert_to_0(t1) val u2 = convert_to_0(t2) in if x < 0 then consbt(0,u1,u2) else consbt(x,u1,u2) end;