Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) (03/26/89)
Jelske Kloppenberg has asked about module termination. The forthcoming draft ISO standard is likely to require a procedure somewhere (in a required library module) to handle this problem. The syntax will be something like IMPLEMENTATION MODULE Library; FROM Finish IMPORT SetTerminate; PROCEDURE TermProc; BEGIN (* code required for termination when this Library is used *) END TermProc; BEGIN (*Initialisation code*); SetTerminate(TermProc) END Library. That is, Finish.SetTerminate(T : PROC) will install T in a stack of procedures that are called automatically, at least when the program ends normally or executes HALT. It is still to be decided what will happen in other situations (eg when exceptions are raised, coroutines "stop" and so on). Incidentally, this feature is already present in many MS-DOS implementations. JPI, StonyBrook, FST, FTL and Logitech all have it (with slight variations on how it's actually done). I suspect it's in many other implementations too, as it's such an obvious thing to want to do! -- uucp: ..!{mcvax!uunet,tektronix,sun!nosun}!oresoft!dawggon!2!490!19.101!Pat.Terry Internet: Pat.Terry@p101.f19.n490.z2.fidonet.org
rjh@cs.purdue.EDU (Bob Hathaway) (03/31/89)
In article <846.242C4BE0@dawggon.fidonet.org> Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) writes: >That is, Finish.SetTerminate(T : PROC) will install T in a stack of >procedures that are called automatically, at least when the program ends >normally or executes HALT. It is still to be decided what will happen in >other situations (eg when exceptions are raised, coroutines "stop" and so >on). Why should procedures be called at program termination? When the program ends all space and resources should be freed anyway. I can see invoking termination blocks at module scope exit for abstract state machines or initialization code for runtime elaboration of Adt (opaque variable) declarations together with termination code for Adt scope exits but I don't see any need for termination code at program exit. Anyway since I'm typing this in I'll bring up an issue which has been in the back of my mind for a long time. Modula restricts imports to a hierarchy, i.e. a tree. To my knowledge, this precludes recursively defined abstract data types. One solution is to nest all abstract data types within a module and export them from a single interface (definition module) but the hierarchical restriction seems unnecessary. For instance, the following recursive structure was desired in a recently posted article: 1. >-------> table: Create Initialize Lookup Insert Print | /|\ GetDefaultValue | / | \ | / | \ | / | \ 2. | list tree hash table ...: Initialize Insert Lookup Print | / | \ 3. ^__ ... ... ... : objects Tables were to be implemented as abstract data types as were lists, trees, and hash tables. Objects were abstract data types also and explicitly referenced tables, resulting in a recursive definition. This requires a general graph and cannot be implemented hierarchically. So for discussion, is there a better solution than nesting modules of all self referencing Adts? Are there any reasons for restricting compilation dependency *trees* to hierarchies? I've forgotten to look at the Modula-3 report for Import dependencies, but I hope the hierarchical restriction has been revoked. Bob Hathaway rjh@purdue.edu
aubrey@val.UUCP (Aubrey McIntosh) (04/01/89)
In article <6354@medusa.cs.purdue.edu> rjh@cs.purdue.edu (Bob Hathaway) writes: [edited heavily throughout. Hope I preserved the correct flavor.] >Anyway since I'm typing this in I'll bring up an issue which has been in the >back of my mind for a long time. Modula restricts imports to a hierarchy, >i.e. a tree. To my knowledge, this precludes recursively defined abstract >data types. > >1. >-------> table: Create Initialize Lookup Insert Print > | /|\ GetDefaultValue > | / | \ > | / | \ > | / | \ >2. | list tree hash table ...: Initialize Insert Lookup Print > | / | \ >3. ^__ ... ... ... : objects > >Tables were to be implemented as abstract data types as were lists, trees, >and hash tables. Objects were abstract data types also and explicitly >referenced tables, resulting in a recursive definition. > >Bob Hathaway >rjh@purdue.edu *Although* I have always made my DEF modules hierarchical, I view this as a style. In fact, as a benchmark of style, the first Modula-2 code I wrote was very fuzzy about the distinction between DEF and MOD files, and I went through considerable agony before it dawned on me that I could import fairly freely into MOD files. Please note that I don't recommend doing that, because of the expense of changing the DEFs, but it did come in handy on that first project. Bob's article made me curious enough about this that I picked up a copy of PIM2, 3rd corr., and broused a bit. In the Report part, "chapter" 14, p169 (Compilation units), I find: (discussion about definition modules and implementation modules) ` ... Opaque export is restricted to pointers ... ' ` As in local modules, the body of an implementation module acts as an initialization facility for its local objects. Before its execution, the imported modules are initialized in the order in which they are listed. If circular references occur among modules, their order of initialization is not defined. ' Additionally, in 4, p 146 (Declarations and Scope rules) I read rules 1..3 as applicable... 1. If an identifier ... is used in another declaration ... then D1 must textually preceed D2. 2. A type T1 can be used in a declaration of a pointer type T which textually precedes the declaration of T1 if both are declared in the same block. This is a relaxation of rule 1. 3. If an identifier ... is exported from compilation unit M1 ... the scope extends to all those units which import M1. I admit that I haven't read PIM2 in its entirety since the first edition, but I didn't find a prohibition in those places I would first look. As I type this, I believe that a simple circular reference among DEF modules, where opaque types are used in subsequent definitions, doesn't violate anything. What's also going through my mind, is that I also expect any implementation which compiles SYM files to have an implementation restriction on this. :-( Perhaps I will go and try some contrived cases. If I do put much more time into this, I will use either the Logitech or the C18 compiler to check things out, and one of the circular imports will have two versions of the DEF file. -- Whatcha' call a boomerang that doesn't come back? --Lost-- -------------------------------------------------------------- 1-(512)-346-5781 (v) Using Modula-2. Austin, TX 78759 ...!cs.utexas.edu![dell|oakhill|kvue]!val!aubrey
Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) (04/03/89)
Bob Hathaway has queried the need for providing "termination" code. He writes: >Why should procedures be called at program termination? When the program >ends all space and resources should be freed anyway. The sort of situation I have in mind is where a library module opens files, takes over interrupt vectors and so on. If indeed the implementation is such that the O/S environment is restored to *exactly* what it was when the program was set running, then there may be no need to provide this facility. Given that many implementations don't seem to do this (especially those on microcomputers, where taking over an interrupt vector and not restoring it leads to chaos), some way of doing so seems a good idea. Since detecting the sort of activity just mentioned (or implementing it) may be done deviously using SYSTEM functions, the need for a programmer to have an easy hook into the termination chain is increased. >I can see invoking termination blocks at module scope exit for abstract >state machines or initialization code for runtime elaboration of Adt >(opaque variable) declarations together with termination code for Adt >scope exits but I don't see any need for termination code at program exit. I take this to mean you would also like to be able to write, for example PROCEDURE x; (*inner*) MODULE y; . . . BEGIN Initialise; SetTerminate(Cleanup) END y; ... END x; so that whenever x was called "Initialise" was invoked, and whenever x returned "CleanUp" was invoked. I don't think the WG13 effort has considered this, and perhaps, for completeness, it should. On the other hand, the problem is solved by placing a call to CleanUp just before the end of x. -- uucp: {mcvax!uunet,tektronix,sun!nosun}!oresoft!dawggon!2!490!19.101!Pat.Terry Internet: Pat.Terry@p101.f19.n490.z2.fidonet.org
rjh@cs.purdue.EDU (Bob Hathaway) (04/04/89)
In article <904.24372D43@dawggon.fidonet.org> Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) writes: >Bob Hathaway has queried the need for providing "termination" code. He >writes: >>Why should procedures be called at program termination? When the program >>ends all space and resources should be freed anyway. > >The sort of situation I have in mind is where a library module opens files, >takes over interrupt vectors and so on. If indeed the implementation is >such that the O/S environment is restored to *exactly* what it was when the >program was set running, then there may be no need to provide this facility. >Given that many implementations don't seem to do this (especially those on >microcomputers, where taking over an interrupt vector and not restoring it >leads to chaos), some way of doing so seems a good idea. Since detecting >the sort of activity just mentioned (or implementing it) may be done >deviously using SYSTEM functions, the need for a programmer to have an easy >hook into the termination chain is increased. > Ok, I saw termination code at module scope exit as a generalization of this idea, with program termination code being specified as termination code for the main program module. >>I can see invoking termination blocks at module scope exit for abstract >>state machines or initialization code for runtime elaboration of Adt >>(opaque variable) declarations together with termination code for Adt >>scope exits but I don't see any need for termination code at program exit. > >I take this to mean you would also like to be able to write, for example > > PROCEDURE x; > (*inner*) MODULE y; > . . . > BEGIN > Initialise; > SetTerminate(Cleanup) > END y; > > ... > END x; > >so that whenever x was called "Initialise" was invoked, and whenever x >returned "CleanUp" was invoked. I don't think the WG13 effort has >considered this, and perhaps, for completeness, it should. On the other >hand, the problem is solved by placing a call to CleanUp just before the >end of x. Yes, this is what I meant. However, for top-level modules serving as abstract state machines there is no means for calling CleanUp just before the end of x, since no x exists, and an explicit termination code section is therefore required. As an alternative, the main program termination code could call a CleanUp procedure for each top-level module but this would have an analogous drawback to calling top-level module initialization routines from the main program instead of from each module initialization block, that of tightly coupling modules. Also, leaving initialization and termination sequences to module clients introduces potential insecurities by not guaranteeing these sequences will be performed correctly, so module initialization and termination blocks provide a simple and safe solution. As I remember, Pascal-Plus has something like the following syntax: MODULE x; MODULE y; . . . BEGIN (* y *) (* y. *)Initialise; *** (* A termination block begins after *** *) (* y. *)Terminate; END y; . . . BEGIN (* x *) (* x. *)Initialise; *** (* x. *)Terminate; END x; Bob Hathaway rjh@purdue.edu