sml modules and
play

SML Modules and Abstract Data Types (ADTs) Hiding implementa-on - PowerPoint PPT Presentation

Overview of Modules and ADTs SML Modules and Abstract Data Types (ADTs) Hiding implementa-on details is the most important strategy for wriCng correct, robust, reusable soEware. Topics: These slides are lightly edited versions of Ben Woods


  1. Overview of Modules and ADTs SML Modules and Abstract Data Types (ADTs) Hiding implementa-on details is the most important strategy for wriCng correct, robust, reusable soEware. Topics: These slides are lightly edited versions of Ben Wood’s Fall ‘15 slides, some of • ML structures and signatures. which are based on Dan Grossman’s material from the University of Washington. • AbstracCon for robust library and client+library code. AbstracCon for easy change. • ADTs and funcCons as data. • CS251 Programmi mming Languages Spring Spring 2019, 2019, Lyn yn Turbak urbak Departme ment of Comp mputer Science Wellesley College We SML Modules and ADTS 2 structure Name = struct bindings end structure (module) Hiding with funcLons namespace management and code organizaLon procedural abstrac.on structure MyMathLib = Hiding implementaCon details is the most important strategy for struct wriCng correct, robust, reusable soEware. fun fact 0 = 1 | fact x = x * fact (x-1) fun double x = x*2 Can you tell the difference? fun double x = x+x val half_pi = Math.pi / 2 val y = 2 fun double x = x*y - double 4; fun doubler x = x * 2 fun double x = val it : int = 8 let fun help 0 y = y val twelve = doubler (fact 3) | help x y = end help (x-1) (y+1) outside: in help x x end val facts = List.map MyMathLib.fact “Private” top-level funcCons would also be nice... [1,4,MyMathLib.doubler 3, • share a "private" helper funcCon MyMathLib.twelve] SML Modules and ADTS 3 SML Modules and ADTS 4

  2. signature NAME = structure Name :> NAME = ascripLon sig binding-types end struct bindings end signature (opaque – will ignore other kinds) type for a structure (module) Ascribing a signature to a structure • Structure must have all bindings with types as declared in signature. List of bindings and their types: signature MATHLIB = variables (incl. funcCons), type synonyms, datatypes, excepCons sig val fact : int -> int Separate from specific structure. Real power: val half_pi : real Abstrac-on and Hiding val doubler : int -> int signature MATHLIB = val twelve : int end sig val fact : int -> int val half_pi : real structure MyMathLib :> MATHLIB = val doubler : int -> int struct val twelve : int fun fact 0 = 1 | fact x = x * fact (x-1) end val half_pi = Math.pi / 2 fun doubler x = x * 2 val twelve = doubler (fact 3) end SML Modules and ADTS 5 SML Modules and ADTS 6 Hiding with signatures Abstract Data Type type of data and operaLons on it MyMathLib.doubler unbound (not in environment) outside module. Example: raConal numbers supporCng add and toString signature MATHLIB2 = sig structure Rational = val fact : int -> int struct val half_pi : real datatype rational = Whole of int val twelve : int | Frac of int*int end exception BadFrac structure MyMathLib2 :> MATHLIB2 = (* see rationals.sml for full code *) struct fun fact 0 = 1 fun make_frac (x,y) = ... | fact x = x * fact (x-1) fun add (r1,r2) = ... val half_pi = Math.pi / 2.0 fun toString r = ... fun doubler x = x * 2 end fun twelve = doubler (fact 3) end SML Modules and ADTS 7 SML Modules and ADTS 8

  3. Library spec and invariants More on invariants External properCes [externally visible guarantees, up to library writer] Our code maintains (and relies) on invariants. • Disallow denominators of 0 • Return strings in reduced form (“4” not “4/1”, “3/2” not “9/6”) • No infinite loops or excepCons Maintain: • make_frac disallows 0 denominator, removes negaCve denominator, and ImplementaCon invariants [not in external specifica9on] reduces result • add assumes invariants on inputs, calls reduce if needed • All denominators > 0 • All rational values returned from funcCons are reduced Rely: • gcd assumes its arguments are non-negaCve Signatures help enforce internal invariants. • add uses math properCes to avoid calling reduce • toString assumes its argument is in reduced form SML Modules and ADTS 9 SML Modules and ADTS 10 A first signature Problem: clients can violate invariants With what we know so far, this signature makes sense: Create values of type Rational.rational directly. • Helper funcCons gcd and reduce not visible outside the module. signature RATIONAL_CONCRETE = signature RATIONAL_CONCRETE = A7empt #1 sig sig datatype rational = Whole of int datatype rational = Whole of int | Frac of int*int | Frac of int*int ... exception BadFrac end val make_frac : int * int -> rational val add : rational * rational -> rational Rational.Frac(1,0) val toString : rational -> string Rational.Frac(3,~2) end Rational.Frac(40,32) structure Rational :> RATIONAL_OPEN = ... SML Modules and ADTS 11 SML Modules and ADTS 12

  4. SoluLon: hide more! Abstract the type! (Really Big Deal!) (R eally Big Deal!) Client can pass them around, but can ADT must hide concrete type defini5on so clients cannot Type rational exists, manipulate them only through module. create invariant-viola5ng values of type directly. but representa-on absolutely hidden. signature RATIONAL = Success! (#3) This agempt goes too far: type rational is not known to exist sig type rational Only way to make 1 st rational . exception BadFrac signature RATIONAL_WRONG = A7empt #2 val make_frac : int * int -> rational Only opera-ons sig val add : rational * rational -> rational on rational . exception BadFrac val toString : rational -> string val make_frac : int * int -> rational end val add : rational * rational -> rational structure Rational :> RATIONAL = ... val toString : rational -> string end Module controls all opera-ons with rational , structure Rational :> RATIONAL_WRONG = ... so client cannot violate invariants. SML Modules and ADTS 13 SML Modules and ADTS 14 Abstract Data Type Abstract type of data + operaLons on it Abstract Data Types: two key tools Outside of implementaCon: • Values of type rational can be Powerful ways to use signatures for hiding: created and manipulated only through ADT opera-ons. • Concrete representa-on of values of type rational 1. Deny bindings exist. is absolutely hidden. Especially val bindings, fun bindings, constructors. signature RATIONAL = sig 2. Make types abstract. type rational Clients cannot create or inspect values of the type directly. exception BadFrac val make_frac : int * int -> rational val add : rational * rational -> rational val toString : rational -> string end structure Rational :> RATIONAL = ... SML Modules and ADTS 15 SML Modules and ADTS 16

  5. A cute twist Signature matching rules In our example, exposing the Whole constructor is no problem structure Struct :> SIG type-checks if and only if: In SML we can expose it as a funcCon since the datatype binding in the • Every non-abstract type in SIG is provided in Struct , as specified module does create such a funcCon • SCll hiding the rest of the datatype • Every abstract type in SIG is provided in Struct in some way • SCll does not allow using Whole as a pagern • Can be a datatype or a type synonym signature RATIONAL_WHOLE = • Every val-binding in SIG is provided in Struct , possibly with a more general and/or less abstract internal type sig • 'a list -> int more general than string list -> int type rational • example soon exception BadFrac val Whole : int -> rational • Every excepCon in SIG is provided in Struct. val make_frac : int * int -> rational val add : rational * rational -> rational Of course Struct can have more bindings (implicit in above rules) val toString : rational -> string end SML Modules and ADTS 17 SML Modules and ADTS 18 PairRaLonal (alternaLve concrete type) Allow different implementa.ons to be equivalent A key purpose of abstracCon: structure PairRational = • No client can tell which you are using struct • Can improve/replace/choose implementaCons later type rational = int * int • Easier with more abstract signatures (reveal only what you must) exception BadFrac UnreducedRational in adts.sml . fun make_frac (x,y) = … fun Whole i = (i,1) (* for RATIONAL_WHOLE *) • Same concrete datatype. fun add ((a,b)(c,d)) = (a*d + b*c, b*d) • Different invariant : reduce fracCons only in toString . fun toString r = ... (* reduce at last minute *) • Equivalent under RATIONAL and RATIONAL_WHOLE , but not under RATIONAL_CONCRETE . end PairRational in adts.sml . • Different concrete datatype. • Equivalent under RATIONAL and RATIONAL_WHOLE , but cannot ascribe RATIONAL_CONCRETE . SML Modules and ADTS 19 SML Modules and ADTS 20

Recommend


More recommend