110 likes | 209 Views
Type Variables in ML. Until we know the type of a value (perhaps never!), we use a variable for its type Book uses, e.g. , t1, tx, tf PL literature uses Greek letters: l x.x : a → a (lambda (a b) b) : a*b → b ML uses 'a, 'b, 'c,. > fun foo(a,b) = b;
E N D
Type Variables in ML • Until we know the type of a value (perhaps never!), we use a variable for its type • Book uses, e.g., t1, tx, tf • PL literature uses Greek letters: • l x.x : a→a • (lambda (a b) b) : a*b→b • ML uses 'a, 'b, 'c, ... > fun foo(a,b) = b; val foo = fn : 'a * 'b -> 'b
sum: int list -> int • member: 'a * 'a list -> bool ML: Let's Write... Recall: fun reverse(nil) = nil | reverse(x::t) = reverse(t) @ [x];
ML: Local environments using let • Recall (?) mergesort: Divide-and-conquer for sorting a list • Split list into two sorted sublists, then merge them: merge([1,3,5], [2,4,6]) = [1,2,3,4,5,6] • So first write merge:
ML: Local environments using let > fun merge(nil,M) = M | merge(L,nil) = L | merge(x::xs, y::ys) = if (x:int) < y then x::merge(xs,y::ys) else y::merge(x::xs,ys); val merge = fn : int list * int list -> int list > merge([1,3,5], [2,4,6]); val it = [1,2,3,4,5,6] : int list Now write split:
> fun split(nil) = (nil,nil) | split([a]) = ([a], nil) | split(a::b::cs) = let val (M,N) = split(cs) in (a::M, b::N) end; val split = fn : 'a list -> 'a list * 'a list > split([1,2,3,4,5]); val it = ([1,3,5],[2,4]) : int list * int list Finally write mergeSort:
> fun mergeSort(nil) = nil | mergeSort([a]) = [a] | mergeSort(L) = let val (M,N) = split(L); val M = mergeSort(M); val N = mergeSort(N) in merge(M,N) end; val mergeSort = fn : int list -> int list > mergeSort([5,4,3,2,1]); val it = [1,2,3,4,5] : int list
ML: Polymorphism • Note that merge is the only function here explicitly declaring a type (int): fun merge(nil,M) = M | merge(L,nil) = L | merge(x::xs, y::ys) = if (x:int) < y then x::merge(xs,y::ys) else y::merge(x::xs,ys); • We can “factor out” the int comparison, and make merge and mergeSort polymorphic:
ML: Polymorphism > fun merge(nil,M,C) = M (* C is comparator fun *) | merge(L,nil,C) = L | merge(x::xs, y::ys,C) = if C(x,y) then x::merge(xs,y::ys,C) else y::merge(x::xs,ys,C); val merge = fn : 'a list * 'a list * ('a * 'a -> bool) -> 'a list
ML: Polymorphism > fun merge Sort(nil,C) = nil | mergeSort([a],C) = [a] | mergeSort(L,C) = let val (M,N) = split(L); val M = mergeSort(M,C); val N = mergeSort(N,C) in merge(M,N,C) end; val mergeSort = fn : 'a list * ('a * 'a -> bool) -> 'a list
ML: Polymorphism > fun intcmp(a:int, b) = a < b; val intcmp = fn : int * int -> bool > fun realcmp(a:real, b) = a < b; val realcmp = fn : real * real -> bool > mergeSort([5,4,3,2,1], intcmp); val it = [1,2,3,4,5] : int list > mergeSort([5.5, 4.4, 3.3, 2.2, 1.1], realcmp); val it = [1.1,2.2,3.3,4.4,5.5] : real list
ML: Polymorphism • Q.: What's the point? • A.: ML supports functions as first -class objects (as in Scheme), while also providing strong type-checking (as in Java) • Java supports this ability (abstract comparison) through the Comparable interface.