Modularization separate program into different modules. motivation: separate major portions of code. improves maintainability. increases potential for reusability. e.g., scanner, parser, symbol table. abstraction mechanism: can hide details of how an abstraction is provided. separate compilation and strong type checking. (original) Pascal doesn t support separate compilation. separate compilation in Fortran and C. linker: simple: linker just resolves addresses. linker doesn t have source text around or information about type or number of parameters. modules can be checked at compile time. clean way to do separate compilation. ECS140A Programming Languages 12-1
so, a program will consist of several modules. two kinds of modules: definition, aka specification or public: defines constants, variables, types, and procedures it provides to other modules. implementation, aka body or private: provides types and procedures defined in definition module. interface between modules via import/export mechanism. providers export their services. using modules import services they need from other modules. Roles of modules: subroutine library just procedures; e.g., math library. data and operations on that data data hidden inside module e.g., can provide table abstraction. procedure enter( name: Identifier ); procedure lookup( name: Identifier ) : boolean; abstract type: type and operation define a stack type and provide operations that manipulate that type. type stack = ; procedure push( var x:stack; item: integer ) Note: that some abstractions can be modeled either way. e.g., a stack. ECS140A Programming Languages 12-2
Modula-2 History descendant of Pascal and Modula, which make sense since Wirth designed both languages. also benefits from Mesa, Xerox PARC. first published 1978 first implemented 1979 underwent revision 1984 has influenced many languages since then, and has directly led to Modula-3 and Oberon. flavor: Pascal plus modules. strongly-typed like Pascal corrected Pascal array type/size problem (open array) separate compilation uses export to make services available; uses import to bring in outside services. 3kinds of modules: program when have only one module, which does not export anything. consider two ways of defining stack asmodule astype ECS140A Programming Languages 12-3
Stack as module (shows basic mechanism, but Bad exposes stack s internal representation to user) user main program module foo1; from io import output, writef; from BadStackmod1 import push, pop, top; var i: integer; push(10); push(100); i:=pop(); writef(output, %d\n,i); end foo1. definition module BadStackmod1; export qualified push, pop, top; procedure push(item: integer); procedure pop() : integer; const MAX = 100; top: cardinal := 0; a: array [1..MAX] of integer; end BadStackmod1. implementation module BadStackmod1; procedure push(item: integer); procedure pop() : integer; Notes: end BadStackmod1. use of import/export. details of stack s internal representation are visible to user e.g., user (in module foo1 code) can access (read or write) top directly. can fix that easily: simply don t export top or, asseen on next page, move details into implementation module. ECS140A Programming Languages 12-4
Stack as module user main program module foo1; from io import output, writef; from Stackmod1 import push, pop; var i: integer; push(10); push(100); i:=pop(); writef(output, %d\n,i); end foo1. definition module Stackmod1; export qualified push, pop; procedure push(item: integer); procedure pop() : integer; end Stackmod1. implementation module Stackmod1; const MAX = 100; top: cardinal := 0; a: array [1..MAX] of integer; Notes: end Stackmod1. procedure push(item: integer); procedure pop() : integer; details of stack s internal representation no longer visible to user. e.g., user (in module foo1 code) can no longer access top directly. ECS140A Programming Languages 12-5
stack as type ( bad e.g., stack s representation is visible to user): user main program module foo2; from io import output, writef; from BadStackMod2 import stack, init, push, pop; var i: integer; s1, s2: stack; (* two instances of a stack *) init(s1); init(s2); push(s1,10); push(s2,23); push(s1,100); push(s1,40); i:=pop(s1); writef(output, %d\n,i); end foo2. definition module BadStackMod2; export qualified stack, init, push, pop; const MAX = 100; Type stack = record top: cardinal; a: array [1..MAX] of integer; end; procedure init(var s:stack); procedure push(var s:stack; item: integer); procedure pop(var s:stack) : integer; end BadStackMod2. implementation module BadStackMod2; procedure init(var s:stack); s.top := 0; end init; procedure push(var s:stack; item: integer); procedure pop(var s:stack) : integer; end BadStackMod2. Note: user can access, e.g., s1.top ECS140A Programming Languages 12-6
stack as type: user main program module foo2; from io import output, writef; from StackMod2 import stack, init, push, pop; var i: integer; s1, s2: stack; (* two instances of a stack *) init(s1); init(s2); push(s1,10); push(s2,23); push(s1,100); push(s1,40); i:=pop(s1); writef(output, %d\n,i); end foo2. definition module StackMod2; export qualified stack, init, push, pop; Type stack; (* opaque *) procedure init(var s:stack); procedure push(var s:stack; item: integer); procedure pop(var s:stack) : integer; end StackMod2. implementation module StackMod2; const MAX = 100; Type stack = pointer to stackrecord; (* opaque *) Type stackrecord = record top: cardinal; a: array [1..MAX] of integer; end; procedure init(var s:stack); new(s); sˆ.top := 0; end init; procedure push(var s:stack; item: integer); procedure pop(var s:stack) : integer; end StackMod2. ECS140A Programming Languages 12-7
Notes: opaque type what it actually is isn t visible outside module. typical implementation restriction: opaque type must be a pointer; not a big problem. must be a pointer so that compiler knows how much space to allocate for it when compiling, e.g, main, when haven t yet compiled implementation module of StackMod2. in fact more efficient, so don t need to pass around lots of data. instantiation: get pointer for each instance of stack; init allocates record. would be nice if did not need initialization. could initialize as part of declaration of type. e.g., top := 0. note declared stack as var, even though only init changes it (because it is a pointer). pop and push change only record, not the pointer such does fit in better with nature of abstraction, though. Modula-2 also supports several forms of import/export local modules, i.e., modules can be nested (additional flexibility, but complicates language) ECS140A Programming Languages 12-8