170 likes | 276 Vues
This guide explores the features of SML97 including records, exception handling, and pattern matching. Records in SML, illustrated with examples, are structured types that support field names, type safety, and pattern matching for cleaner code. It explains the creation of custom exceptions and the use of predefined exceptions, focusing on robustness and error handling strategies. The interplay between references and assignments, as well as the implications of aliasing, is discussed to enhance understanding of mutable data structures in functional programming.
E N D
Remaining Features of SML97 Records Exceptions Reference Types Arrays L16rem
Records type component = { key : int, info : string }; val part = { key = 3, info = “nail” }; (* val part = {info="nail",key=3} : {info:string, key:int} *) #key part = 3; part = {info = “na” ^ “il”, key = 3-0 }; (* val it = true : bool *) • Order of fields is irrelevant when matching. (2, “two”) = { 1 = 2, 2 = “two” }; • Tuples are special records with numbers as field labels. L16rem
Pattern Matching for Records fun info_compare (p1:component) (p2:component) = String.compare (#info p1, #info p2); fun key_compare ({key=k1,info=_}:component) ({key=k2, ... }:component) = Int.compare (k1, k2); (* Wildcards : Type name mandatory with ellipses. *) fun check_info (c as {key,info}:component) = (#info c = info) ; (* Using a field name to refer to value *) (* maps a component to true *) L16rem
Exceptions • An exception name is a constructor of the built-in type exn. This datatype is special in that the set of constructors can be extended. - exception Overflow; - exception message of string; - exception outOfRange of int*int; - Overflow; (* val it = Overflow(-) : exn *) - message; (* val it = fn : string -> exn *) - outOfRange; (* val it = fn : int * int -> exn *) Predefined exceptions : Div, Empty, Match,... etc. L16rem
Operations on exception raiseexn-name; expressionhandleexn_name1 => … | exn_name2 => … ; Organization CLIENT SERVER Define exception “Trigger” exception Raise exception Handle exception L16rem
Motivation for incorporating exception • Robustness • Language/application specified behavior on error • Context-sensitive handling • Retry • to overcome transient errors • Attempt • another solution strategy (heuristic) on failure • Rescue • reinstate any required invariant (consistency) • Dynamic Handling via run-time call stack L16rem
Examples Server exception fail of string; fun search name [] = raise (fail name) | search name ((x,v)::xvs) = if (name = x) then v else search name xvs; datatype employers = wsu | wpafb | ncr | lexis_nexis ; val name_emp = [(“john”,wsu),(“jane”,ncr)]; (search “jill” name_emp) handle (fail elem) => wpafb; Client L16rem
(cont’d) val name_car = [(“john”, “acura”),(“jane”, “lexus”)]; (search “jill” name_car) handle (fail elem) => elem ^ “ owns Dodge Stealth!”; val name_tel = [(“john”,8501),(“jane”,1359)]; (search “jill” name_tel) handle (fail elem) => elem ^ “’s phone number is unlisted.”; (* type error *) L16rem
References and Assignments L16rem
(ML) Redefinition val x = [5]; val x = 2; (* The first list is inaccessible (garbage). *) • (ML) Shadowing letval x = 5 in (let val x = [2] in (hd x) end) + x end; • (Pascal) Assignment var i: int; begin i := 0; i := i + 1 end; Redefinition and shadowing are different from assignment. L16rem
References • ML supports a separate sub-language to deal with variables and assignments (that can change value bound to a variable). val x = ref 5; x := 6; x := 1 + !x; l-valuer-value (address of location) (contents of location) 5 x L16rem
var i, sum : int := 0; var n : int := 5; while i < n do begin i := i + 1; sum := sum + i end; The l-value of a variable on the rhs of an assignment is automatically coerced to its r-value. val i = ref 0; val sum = ref 0; val n = 5; while !i < n do ( i := !i + 1 ; sum := !sum + !i ); A reference variable on the rhs of an assignment must be explicitly dereferenced, to get its r-value. Iteration : Pascal vs ML L16rem
Aliasing problem fun rot3 (a,b,c) = letval t = !a in a := !b; b := !c; c := t end; val i = ref 0; val j = ref 1; val k = ref 2; (!i,!j,!k); rot3 (i,j,k); (!i,!j,!k); (* (0,1,2) , () : unit, (1,2,0) *) val i = ref 0; val j = ref 1; val k = ref 2; (!i,!j,!i); rot3 (i,j,i); (!i,!j,!i); (* (0,1,0) , () : unit, (0,1,0) *) L16rem
fun new_counter () = let val cnt = ref 0 fun tick () = ( cnt := !cnt + 1; !cnt ) fun reset () = ( cnt := 0 ) in { tick = tick, reset = reset } end; val c1 = new_counter(); val c2 = new_counter(); (#tick c1) (); (* 1 *) (#tick c2) (); (* 1 *) (#tick c1) (); (* 2 *) (#tick c2) (); (* 2 *) (#reset c1) (); (#tick c1) (); (* 1 *) (#tick c2) (); (* 3 *) (* Object-based programming *) Encapsulating State : Objects L16rem
open Array; val n = 3; val M = array(n, array(n,0)); (* Constructs array whose top-level elements share the same inner array. *) val i = ref 0; while (!i < n) do ( update(M,!i,array(n,0)); i := !i + 1 ); (* creates n x n locations initialized to 0 *) i := 0; val j = ref 0; while (!i < n) do ( while (!j < n) do ( update(sub(M,!i),!j,!i + !j); j := !j + 1 ); j := 0; i := !i + 1 ); (* initializesM[i,j]toi+j. *) (* M = [|[|0,1,2|],[|1,2,3|],[|2,3,4|]|] : int array array *) Mutable Data Structure :structure Array L16rem
Interaction with polymorphism • Normally, variables/functions can be polymorphic, but not values. fun id x = x; id 5; ( (fn x => (id x)) 5 ); ( (fn f => (f 5)) id ); (* (fn f => (f 5)) : (int -> 'a) -> 'a *) (id 5, id true); ( (fn f => (f 5, f true)) id ); (* Type error *) L16rem
Polymorphic references are banned. val fp = ref id; (* potential type: ’a->’a ref *) fp := not; !fp 5; Polymorphic exceptions are banned. exception fail of ’a; (raise fail true) handle (fail x) => 0 * x; Value Restriction for polymorphism L16rem