490 likes | 613 Views
Data Abstraction, State, and Objects Abstract Data Types (VRH 3.7) State / Data Abstraction(VRH 6.1, 6.3, 6.4.1, 6.4.2) Object System Implementation (VRH 7.6). Carlos Varela RPI Adapted with permission from: Seif Haridi KTH Peter Van Roy UCL. Abstract data types.
E N D
Data Abstraction, State, and ObjectsAbstract Data Types (VRH 3.7)State / Data Abstraction(VRH 6.1, 6.3, 6.4.1, 6.4.2)Object System Implementation (VRH 7.6) Carlos Varela RPI Adapted with permission from: Seif Haridi KTH Peter Van Roy UCL C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Abstract data types • A datatype is a set of values and an associated set of operations • A datatype is abstract if only it is completely described by its set of operations regradless of its implementation • This means that it is possible to change the implementation of the datatype without changing its use • The datatype is thus described by a set of procedures • These operations are the only thing that a user of the abstraction can assume C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Example: A Stack • Assume we want to define a new datatype stack T whose elements are of any type T fun {NewStack}: Stack T fun {Push Stack TT }: Stack T fun {Pop Stack TT }: Stack T fun {IsEmpty Stack T }: Bool • These operations normally satisfy certain conditions: {IsEmpty {NewStack}} = true for any E and S0, S1={Push S0E} and S0 ={Pop S1 E} hold {Pop {NewStack} E} raises error C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Stack (implementation) fun {NewStack} nil end fun {Push S E} E|S end fun {Pop S E} case S of X|S1 then E = X S1 end end fun {IsEmpty S} S==nil end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Stack (another implementation) fun {NewStack} nil end fun {Push S E} E|S end fun {Pop S E} case S of X|S1 then E = X S1 end end fun {IsEmpty S} S==nil end fun {NewStack} emptyStack end fun {Push S E} stack(E S) end fun {Pop S E} case S of stack(X S1) then E = X S1 end end fun {IsEmpty S} S==emptyStack end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Dictionaries • The datatype dictionary is a finite mapping from a set T to value, where T is either atom or integer • fun {NewDictionary} • returns an empty mapping • fun {Put D Key Value} • returns a dictionary identical to D except Key is mapped to Value • fun {CondGet D Key Default} • returns the value corresponding to Key in D, otherwise returns Default • fun {Domain D} • returns a list of the keys in D C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Implementation fun {Put Ds Key Value} case Ds of nil then [Key#Value] [] (K#V)|Dr andthen Key==K then (Key#Value) | Dr [] (K#V)|Dr andthen K>Key then (Key#Value)|(K#V)|Dr [] (K#V)|Dr andthen K<Key then (K#V)|{Put Dr Key Value} end end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Implementation fun {CondGet Ds Key Default} case Ds of nil then Default [] (K#V)|Dr andthen Key==K then V [] (K#V)|Dr andthen K>Key then Default [] (K#V)|Dr andthen K<Key then {CondGet Dr Key Default} end end fun {Domain Ds} {Map Ds fun {$ K#_} K end} end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Further implementations • Because of abstraction, we can replace the dictionary ADT implementation using a list, whose complexity is linear (i.e., O(n)), for a binary tree implementation with logarithmic operations (i.e., O(log(n)). • Data abstraction makes clients of the ADT unaware (other than through perceived efficiency) of the internal implementation of the data type. • It is important that clients do not use anything about the internal representation of the data type (e.g., using {Length Dictionary} to get the size of the dictionary). Using only the interface (defined ADT operations) ensures that different implementations can be used in the future. C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract data types:Stack is not secure fun {NewStack} nil end fun {Push S E} E|S end fun {Pop S E} case S of X|S1 then E=X S1 end end fun {IsEmpty S} S==nil end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract data types II • The representation of the stack is visible: [a b c d] • Anyone can use an incorrect representation, i.e., by passing other language entities to the stack operation, causing it to malfunction (like a|b|X or Y=a|b|Y) • Anyone can write new operations on stacks, thus breaking the abstraction-representation barrier • How can we guarantee that the representation is invisible? C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract data types III • The model can be extended. Here are two ways: • By adding a new basic type, an unforgeable constant called a name • By adding encapsulated state. • A name is like an atom except that it cannot be typed in on a keyboard or printed! • The only way to have a name is if one is given it explicitly • There are just two operations on names: N={NewName} : returns a fresh name N1==N2 : returns true or false C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract datatypes IV proc {NewWrapper ?Wrap ?Unwrap} Key={NewName} in fun {Wrap X} fun {$ K} if K==Key then X end end end fun {Unwrap C} {C Key} end end • We want to « wrap » and « unwrap » values • Let us use names to define a wrapper & unwrapper C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract data types:A secure stack With the wrapper & unwrapper we can build a secure stack local Wrap Unwrap in {NewWrapper Wrap Unwrap} fun {NewStack} {Wrap nil} end fun {Push S E} {Wrap E|{Unwrap S}} end fun {Pop S E} case {Unwrap S} of X|S1 then E=X {Wrap S1} end end fun {IsEmpty S} {Unwrap S}==nil end end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Capabilities and security • We say a computation is secure if it has well-defined and controllable properties, independent of the existence of other (possibly malicious) entities (either computations or humans) in the system • What properties must a language have to be secure? • One way to make a language secure is to base it on capabilities • A capability is an unforgeable language entity (« ticket ») that gives its owner the right to perform a particular action and only that action • In our model, all values are capabilities (records, numbers, procedures, names) since they give the right to perform operations on the values • Having a procedure gives the right to call that procedure. Procedures are very general capabilities, since what they do depends on their argument • Using names as procedure arguments allows very precise control of rights; for example, it allows us to build secure abstract data types • Capabilities originated in operating systems research • A capability can give a process the right to create a file in some directory C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract datatypes V • We add two new concepts to the computation model • {NewChunk Record} • returns a value similar to record but its arity cannot be inspected • recall {Arity foo(a:1 b:2)} is [a b] • {NewName} • a function that returns a new symbolic (unforgeable, i.e. cannot be guessed) name • foo(a:1 b:2 {NewName}:3) makes impossible to access the third component, if you do not know the arity • {NewChunk foo(a:1 b:2 {NewName}:3) } • Returns what ? C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract datatypes VI proc {NewWrapper ?Wrap ?Unwrap} Key={NewName} in fun {Wrap X} {NewChunk foo(Key:X)} end fun {Unwrap C} C.Key end end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Secure abstract data types:Another secure stack With the new wrapper & unwrapper we can build another secure stack (since we only use the interface to wrap and unwrap, the code is identical to the one using higher-order programming) local Wrap Unwrap in {NewWrapper Wrap Unwrap} fun {NewStack} {Wrap nil} end fun {Push S E} {Wrap E|{Unwrap S}} end fun {Pop S E} case {Unwrap S} of X|S1 then E=X {Wrap S1} end end fun {IsEmpty S} {Unwrap S}==nil end end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
What is state? • State is a sequence of values in time that contains the intermediate results of a desired computation • Declarative programs can also have state according to this definition • Consider the following program fun {Sum XsA} case Xs of X|Xr then {Sum XrA+X} [] nil then A end end {Browse {Sum [1 2 3 4] 0}} C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
What is implicit state? The two arguments Xs and A represent an implicit state Xs A [1 2 3 4] 0 [2 3 4] 1 [3 4] 3 [4] 6 nil 10 fun {Sum XsA} case Xs of X|Xr then {Sum XrA+X} [] nil then A end end {Browse {Sum [1 2 3 4] 0}} C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
What is explicit state: Example? An unbound variable X C A cell C is created with initial value 5 X is bound to C 5 X C The cell C, which X is bound to, is assigned the value 6 6 X C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
What is explicit state: Example? An unbound variable X • The cell is a value container with a unique identity • X is really bound to the identity of the cell • When the cell is assigned, X does not • change C A cell C is created with initialvalue 5 X is bound to C 5 X C The cell C, which X is bound to, is assigned the value 6 6 X C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
What is explicit state? • X = {NewCell I} • Creates a cell with initial value I • Binds X to the identity of the cell • Example: X = {NewCell 0} • {Assign X J} • Assumes X is bound to a cell C (otherwise exception) • Changes the content of C to become J • Y = {Access X} • Assumes X is bound to a cell C (otherwise exception) • Binds Y to the value contained in C C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Examples • X = {NewCell 0} • {Assign X 5} • Y = X • {Assign Y 10} • {Access X} == 10 % returns true • X == Y% returns true 0 X 5 X Y 10 X Y C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Examples • X = {NewCell 10}Y = {NewCell 10} • X == Y% returns false • Because X and Y refer to different cells, with different identities • {Access X} == {Access Y}returns true 10 X 10 Y C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The model extended with cells Semantic stack w= f(x) z = person(a:y) y = 1 u = 2 x 1: w 2: x .... .... single assignment store mutable store C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The stateful model s::= skip empty statement|s1s2statement sequence | ... |{NewCellxc}cell creation |{Exchange cx y} cell exchange Exchange: bind xto the old content of cand set the content of the cell cto y C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The stateful model |{NewCellxc}cell creation |{Exchange cx y} cell exchange Exchange: bind xto the old content of cand set the content of the cell cto y proc {Assign C X} {Exchange C _ X} end fun {Access C} X in{Exchange C X X}X end C := X is syntactic sugar for {Assign C X} @C is syntactic sugar for {Access C} X=C:=Y is syntactic sugar for {Exchange C X Y} C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Abstract data types (revisited) • For a given functionality, there are many ways to package the ADT. We distinguish three axes. • Open vs. secure ADT: is the internal representation visible to the program or hidden? • Declarative vs. stateful ADT: does the ADT have encapsulated state or not? • Bundled vs. unbundled ADT: is the data kept together from the operations or is it separable? • Let us see what our stack ADT looks like with some of these possibilities C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Stack:Open, declarative, and unbundled • Here is the basic stack, as we saw it before:fun {NewStack} nil endfun {Push S E} E|S endfun {Pop S E} case S of X|S1 then E=X S1 end endfun {IsEmpty S} S==nil end • This is completely unprotected. Where is it useful? Primarily, in small programs in which expressiveness is more important than security. C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Stack:Secure, declarative, and unbundled • We can make the declarative stack secure by using a wrapper:local Wrap Unwrapin {NewWrapper Wrap Unwrap}fun {NewStack} {Wrap nil} endfun {Push S E} {Wrap E|{Unwrap S}}endfun {Pop S E} case{Unwrap S}of X|S1 then E=X {Wrap S1}end endfun {IsEmpty S} {Unwrap S} ==nil endend • Where is this useful? In large programs where we want to protect the implementation of a declarative component. C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Stack:Secure, stateful, and unbundled • Let us combine the wrapper with state:local Wrap Unwrapin {NewWrapper Wrap Unwrap}fun {NewStack} {Wrap {NewCell nil}}endproc {Push W X} C={Unwrap W} in{Assign C X|{Access C}} endfun {Pop W} C={Unwrap W} incase {Access C} of X|S then {Assign C S} X endendfun {IsEmpty S} {Access {Unwrap W}}==nil endend • This version is stateful but lets us store the stack separate from the operations. The same operations work on all stacks. C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Stack:Secure, stateful, and bundled • This is the simplest way to make a secure stateful stack:proc {NewStack ?Push ?Pop ?IsEmpty} C={NewCell nil}inproc {Push X} {Assign C X|{Access C}} endfun {Pop} case{Access C}of X|S then{Assign C S} X end endfun {IsEmpty} {Access C} ==nil endend • Compare the declarative with the stateful versions: the declarative version needs two arguments per operation, the stateful version uses higher-order programming (instantiation) • With some syntactic support, this is object-based programming C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Four ways to package a stack • Open, declarative, and unbundled: the usual declarative style, e.g., in Prolog and Scheme • Secure, declarative, and unbundled: use wrappers to make the declarative style secure • Secure, stateful, and unbundled: an interesting variation on the usual object-oriented style • Secure, stateful, and bundled: the usual object-oriented style, e.g., in Smalltalk and Java • Other possibilities: there are four more possibilities! Exercise: Try to write all of them. C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Encapsulated stateful abstract datatypes ADT • These are stateful entities that can be accessed only by the external interface • The implementation is not visible outside • We show two methods to build stateful abstract data types: • The functor based approach (record interface) • The procedure dispatch approach C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The functor-based approach fun {NewCounter I} S = {NewCell I} proc {Inc}S := @S + 1end proc {Dec}S := @S - 1end fun {Get} @S end proc {Put I} S := I end proc {Display} {Browse @S} end ino(inc:Incdec:Decget:Getput:Putdisplay:Display) end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The functor-based approach fun {NewCounter I} S = {NewCell I} proc {Inc}S := @S + 1end proc {Dec}S := @S - 1end fun {Get} @S end proc {Put I} S := I end proc {Display} {Browse @S} end ino(inc:Incdec:Decget:Getput:Putbrowse:Display) end The state is collected in cell S The state is completely encapsulated i.e. not visible outside C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The functor-based approach fun {NewCounter I} S = {NewCell I} proc {Inc}S := @S + 1end proc {Dec}S := @S - 1end fun {Get} @S end proc {Put I} S := I end proc {Display} {Browse @S} end ino(inc:Incdec:Decget:Getput:Putdisplay:Display) end The interface is created for each instance Counter C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The functor-based approach fun {NewCounter I} S = {NewCell I} proc {Inc}S := @S + 1end proc {Dec}S := @S - 1end fun {Get} @S end proc {Put I} S := I end proc {Display} {Browse S.v} end ino(inc:Incdec:Decget:Getput:Putdisplay:Display) end functions that access the state by lexical scope C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Call pattern declare C1 C2 C1 = {NewCounter 0} C2 = {NewCounter 100} {C1.inc} {C1.display} {C2.dec} {C2.display} C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Defined as a functor functor Counter export inc:Incdec:Decget:Getput:Putdisplay:Display init:Init define S proc {Init init(I)} S = {NewCell I}end proc {Inc}S := @S + 1end proc {Dec}S := @S - 1 end fun {Get} @S end proc {Put I} S := I end proc {Display} {Browse @S} end end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Functors • Functors have been used as a specification of modules • Also functors have been used as a specification of abstract datatypes • How to create a stateful entity from a functor? C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Explicit creation of objects from functors • Given a variable F that is bound to a functor • [O] = {Module.apply [F]}creates stateful ADT object O that is an instance of F • Given the functor F is stored on a file ’f.ozf’ • [O] = {Module.link [’f.ozf’]}creates stateful ADT object O that is an instance of F C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Defined as a functor functor Counter export inc:Incdec:Decget:Getput:Putdisplay:Display init:Init define S proc {Init init(I)} S = {NewCell I}end proc {Inc}S := @S + 1end proc {Dec}S := @S - 1 end fun {Get} @S end proc {Put I} S := I end proc {Display} {Browse @S} end end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Pattern of use fun {New Functor Init} M in [M] = {Module.apply [Functor]} {M.init Init} M End declare C1 C2 C1 = {New Counter init(0)} C2 = {New Counter init(100)} {C1.inc} {C1.put 50} {C1.display} {C2.dec} {C2.display} Generic function to create objects from functors Object interface is a record with procedure values inside fields C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The procedure-based approach fun {Counter} S proc {Inc inc(Value)}S := @S + Value end proc {Display display} {Browse @S} end proc {Init init(I)} S = {NewCell I} end D = o(inc:Inc display:Display init:Init) inproc{$ M} {D.{Label M} M} end end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
The procedure-based approach fun {Counter} S ... D = o(inc:Inc display:Display init:Init) inproc{$ M} {D.{Label M} M} end end fun {New Class InitialMethod} O = {Class} in {O InitialMethod} O end C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Example • The following shows how an object is created from a class using the procedure New/3, whose first argument is the class, the second is the initial method, and the result is the object. • New/3 is a generic procedure for creating objects from classes. declare C = {New Counter init(0)}{C display}{C inc(1)}{C display} Object interface is as a procedure of one argument, which expects a record C. Varela; Adapted w/permission from S. Haridi and P. Van Roy
Exercises • Write the higher-order function Map used by the Domain function in the Dictionary ADT. • Solve problem 17 in Section 3.10 (pg. 232). You do not need to implement it using gump, simply specify how you would add currying to Oz (syntax and semantics). • Do exercise 2 in Section 6.10 (pg. 482). • Read Sections 7.1-4, and 7.7 C. Varela; Adapted w/permission from S. Haridi and P. Van Roy