Exercises on ML Programming Languages Chanseok Oh chanseok@cs.nyu.edu
Dejected by an arcane type error? - foldr; val it = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b - foldr (fn x=> fn y => fn z => (max x y, min x z)) [1,2,3] (0, 10); stdin:66.1-66.70 Error: operator and operand don't agree [tycon mismatch] operator domain: 'Z * 'Y -> 'Y operand: in expression: int -> int -> int -> int * int foldr (fn x => (fn <pat> => <exp>))
Standard ML Thanks to the type inference, - Strongly typed - cf. C++ is arguably weakly typed. - Statically typed
Type of an expression Expressions that are not functions - ~1; ~ : unary minus val it = ~1 : int - true orelse false; val it = true : bool - 1.0 + 4.5; val it = 5.5 : real - #"c"; #"ch" error val it = #"c" : char - "some" ^ "string"; val it = "somestring" : string
Type of an expression Lists (still not functions) - []; val it = [] : 'a list (polymorphic, 'a can be any type) - nil; val it = [] : 'a list - [ #"a", #"b" ]; val it = [ #"a", #"b" ] : char list - [ [ 1, 2 ], [], [3] ]; val it = [ [1,2], [], [3] ] : int list list = ((int list) list)
Then, what about the function expressions? Remember, functions are fist-class citizens in ML. Can be assigned to (mutable) variables, passed to a function as arguments, or returned by a function, etc. They can be treated like other values. They are really values. - fn x => 2 * x; val it = fn : int -> int - print; val it = fn : string -> unit They also have types!
Binding names to function expressions - Functions (or values) can be bound to names. - val x = 3; val x = 3 : int - val double = fn x => 2 * x; val double = fn : int -> int Alternative syntax to define a function: - fun double x = 2 * x; val double = fn : int -> int
Function Type - fun max a b = if a > b then a else b; val max = fn : int -> int -> int Why two arrows? One might just say: Well, that looks like a stupid notation. I ll just think of max as a function taking 2 integer arguments and returning an integer.
Function Type - fun max (a, b) = if a > b then a else b; val max = fn : int * int -> int Why an asterisk now? One might just say: That s annoying. I don t care. I ll just think of max as a function with 2 arguments.
Tuples - (10, #"c"); val it = (10, #"c") : int * char - (1, [[ ]], "str"); val it = (1,[[]],"str") : int * 'a list list * string - fun max (a, b) = if This is not a function taking 2 arguments. Why? val max = fn : int * int -> int = (int * int) -> int
Function Type - fun max a b = if - val max = fn a => fn b => if - val max = (fn a => (fn b => if )) - fun max a = fn b => if val max = fn : int -> int -> int int -> (int -> int)
Currying - val max a b = if a > b then a else b; val max = fn : int -> int -> int λa. ( λb. ( if a > b then a else b )) - val at_least_two = max 2; val at_least_two = fn : int -> int (λa. λb. ( if a > b then a else b )) 2 = λb. ( if 2 > b then 2 else b ) - val two = at_least_two 0; val two = 2 : int (λb. ( )) 0 = ( if 2 > 0 then 2 else 0 ) = 2
Currying - 2 + 3; val it = 5 : int - op+; val it = fn : int * int -> int - op+ (2, 3); val it = 5 : int - plus 2 3; - plus 2;
Currying - fun curry f a b = f (a, b); val curry = fn : ('a * 'b -> 'c) -> 'a -> 'b -> 'c - op+; val it = fn : int * int -> int - curry op+; val it = fn : int -> int -> int - val iszero = curry op= 0; val iszero = fn : int -> bool - List.filter (iszero o (curry op+ 1)) [0,1,0,~1,0,~1]; val it = [~1,~1] : int list
Frequent mistakes - max -1 3; Error: operator and operand don't agree (max - 1) 3 - max ~1 3; val it = 3 : int ~ is also a function of int -> int. - ~; val it = fn : int -> int
Frequent mistakes - fun op o = Error: - 4 / 2; Error: operator and operand don't agree - op /; val it = fn : real * real -> real - 8.0 / 2.5; - op div; val it = fn : int * int -> int - 4 div 2;
Frequent mistakes (Cont d) - fun fact n = if n=0 then 1 else fact n-1; val fact = fn : int -> int - fact 0; val it = 1 : int - fact 3; ( does not terminate.) - fun fact n = if n=0 then 1 else (fact n)-1; - fun fact n = if n=0 then 1 else fact (n-1);
Beware! - fun fact n = if n=0 then 1 else fact n-1; val fact = fn : int -> int At this point, you ve noticed the mistake, and you decide to redefine the function. - val fact = fn n => if n=0 then 1 else fact (n-1); - fact 3; ( still does not terminate.)
Beware! (Cont d) - val fact = fn n => if n=0 then 1 else fact (n-1); This did not generate an error only because fact was previously defined. - val f = fn () => f (); : unbound variable or constructor: f - val rec fact = fn n => if n=0 then 1 else fact (n-1); fun f n =... is like val rec f = fn n =>
Exercises 'a list -> int -> 'a list -> int ('a list -> int) -> 'a list -> int ('a -> 'a -> int) -> 'a list -> 'a list 'a -> ('a * 'b) list -> 'b map : ('a -> 'b) -> 'a list -> 'b list map ord [#"A", #"B"];
Exercises filter : ('a -> bool) -> 'a list -> 'a list filter even [1, 2, 3, 4] [2, 4] filter (not o even) [1, 2, 3, 4] [1, 3] fun lt = curry op< fun gt a b = curry op> filter (lt 3) [1, 2, 3, 4, 5] [4, 5] filter (not o (lt 3)) [1, 2, 3, 4, 5] [1, 2, 3] filter (gt 3) [1, 2, 3, 4, 5] [1, 2] filter (not o (gt 3)) [1, 2, 3, 4, 5] [3, 4, 5]
Exercises foldl : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b foldl op+ 0 [~5, 5, 10]; foldl op+ 0 (map abs [~5, 5, 10]); foldl (fn (lst, len) => length lst + len) 0 [[], [1], [1,2,3]];
Quicksort (Polymorphic) fun qsort _ [] = [] qsort lt (x :: xs) = (qsort lt (filter (not o (lt x)) xs)) @ [x] @ (qsort lt (filter (lt x) xs)); <= x sorted x > x sorted
Dejected by an arcane type error? - foldr; val it = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b - foldr (fn x=> fn y => fn z => (max x y, min x z)) [1,2,3] (0, 10); stdin:66.1-66.70 Error: operator and operand don't agree [tycon mismatch] operator domain: 'Z * 'Y -> 'Y operand: in expression: int -> int -> int -> int * int foldr (fn x => (fn <pat> => <exp>))
Pattern Matching let val (fst, _, trd) = some_tuple val e1 :: e2 :: tail = [3] @ some_list in if [1 + 4, e1] <> [1 + e1, e2] then ( case (e2, fst) :: [(10, trd)] of [(3, _), _] => 0 _ :: [(10, "str")] => 1 (e2, "name") :: [_] => 2 _ => 3 ) else 10 end
Pattern Matching (Cont d) fun silly [] = 0 silly (2 :: _ :: 3 :: xs) = 3 + silly xs silly (_ :: xs) = 2 * silly xs