CS 334
|
When pass function (or procedure) parameters in stack-based languages, must also pass the equivalent of a closure. In particular must pass the environment in which the function was defined. This is accomplished by passing the appropriate static pointer with the function so that can find non-local variables. Usually pass the pair (ep,ip) of environment pointer and instruction pointer as the "closure" of a procedure, when it is passed as a parameter.
Returning functions from functions is harder since defining environment may go away:
program ret; function a(): function (integer): integer; var m: integer; function addm (n: integer): integer; begin return (n + m) end; begin (* a *) m := 5; return addm end; (* a *) procedure b (g: function(integer): integer); begin (* b *) writeln(g(2)) end (* b *) begin (* main *) b(a()) (* note that a() returns a function, which is then passed to b *) end.When b(a()) is called, a() returns a function which depends on the non-local variable m, but m has gone away by the time the function is actually applied. Hence languages (like ML) which allow functions to return functions cannot use the simple stack discipline - must keep around activation records even after their associated function or procedure has returned.
Definitional have constant, variable, procedural, and functional.
Constant parameters are treated as values, not variables - different from
call-by-value.
Default for Ada in parameters.
Can think of call-by-name as definitional with expression parameter.
Note that difference in parameter passing depends on what is bound (value or address) and when it is bound.
Another way of classifying parameters is to note that each parameter mechanism corresponds to declaration in language:
E.g., constant, variable (def. & declaration), procedure & function, type(?)
What about call-by-name?
Very disturbing in functions since can make it hard to figure out values of expressions. Example:
A[f(j)] := j * f(j) + jMakes it harder to optimize - e.g. evaluate f(j) only once.
Example:
Procedure swap( var x, y: integer); begin x := x + y; y := x - y; x := x - y end;
This is a tricky way of completing a swap of x and y w/out extra space.
Unfortunately, it doesn't always work - swap (a,a) (but does work with value-result! )
Can get similar probs with A, A[i] as parameters and pointers
Another problem: Overlap btn global vble and by-reference parameter.
Aliasing causes problems with correctness since any two vbles may refer to the same object. It also makes it difficult to optimize if can't predict when a vble might be changed.
If no aliasing, can't detect difference btn call-by-reference and call-by-value-result. But not semantically equivalent if aliasing is possible.
Leads to problems in Ada where language definition does not specify whether in-out parameters are to be implemented by reference or value-result. (Illegal program if it makes a difference - but not detectable!)
Unfortunately Ada doesn't enforce no aliasing. Therefore possible problems with in out parameters.
Euclid (variant of Pascal) designed to write verifiable programs. Attempted to eliminate aliasing. Unfortunately some can only be caught at run-time, e.g. p(A[i], A[j]). Legality assertions generated to check run-time problems. Global vbles had to be explicitly imported to avoid problems. I.e. treated as implicit parameters
Package data structure and its operations in same module - Encapsulation
Data type consists of set of objects plus set of operations on the objects of the type (constructors, inspectors, destructors).
Want mechanism to build new data types (extensible types). Should be treated same way as built-in types. Representation should be hidden from users (abstract). Users only have access via operations provided by the ADT.
Distinguish between specification and implementation.
Method for defining data type and the operations on that type (all in same place). The definitions should not depend on any implementation details. The definitions of the operations should include a specification of their semantics.
Provides user-interface with ADT.
Typically includes
Ex:
pop(push(S,x)) = S,Data + Operations (+ possibly equations) = Algebraif not empty(S) then push(pop(S), top(S)) = S
Method for collecting the implementation details of the type and its operations (in one place), and of restricting access to these details by programs that use the data type. Provides details on all data structures (including some hidden to users) and bodies of all operations.
Note that ADT methodology is orthogonal to top-down design
Three predominant concerns in language design:
Derived from Algol 60. Simulation language.
Provided notion of class.
class vehicle(weight,maxload); real weight, maxload; begin integer licenseno; (* attributes of class instance *) real load; Boolean procedure tooheavy; tooheavy := weight + load > maxload; load := 0; (* initialization code *) endRefer to objects through references:
ref(vehicle) rv, pickup; rv1:- new vehicle(2000,2500); pickup:- rv1; (* special assignment via sharing *) pickup.licenseno := 3747; pickup.load := pickup.load +150; if pickup.tooheavy then ...Notice that attributes are available to all users.
Representation not hidden.
Come back to discuss subclasses later when discussing object-oriented languages.
Abstract data type is one that is defined by group of operations (including constants) and (possibly) a set of equations. Set of values only defined indirectly as those values which can be generated by ops, starting from constructors or constants.
E.g., Stack defined by EmptyStack, push, pop, top, and empty operations and equations.
pop(push(fst,rest)) = rest, top(push(fst,rest)) = fst, empty(EmptyStack) = true, empty(push(fst,rest)) = false, etc.Key is representation is hidden.