A "forward" statement for modules? (programming lessons in Pascaline) #250
samiam95124
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
A "forward" statement for modules? (programming lessons in Pascaline)
Modular program construction is a very simple construct with far reaching (and non-obvious) implications. First, where did modules come from in Pascal?
Original Pascal was famous for (among other things) for not having modular compilation. The first popular dialect[1] of Pascal was UCSD, and it featured the "uses" statement:
Uses from UCSD got copied far and wide, and exists today in Delphi/FPC, was in SVS, GPC, many other Pascals, and, well, Pascaline. The import of the uses statement is very similar to a straight "include" statement (a la C). In fact, Pascaline started with a C type include statement. Uses takes the file indicated by the uses name, and puts the definitions found in the file into the using file. The exact meaning of that depends on the implementation. It can be anything from including the text directly to interpreting the statements in the file that is used[4]. Typically it goes along with relaxed order of declarations, since otherwise this would cause definitions to be included out of order.
In Pascaline, the rule exists that uses (and joins) are only allowed at the top of the program or module. This means that the user cannot have definitions prior to the uses (or joins) statement. This is by design. The idea is that if the program or module can have definitions the submodule depends on, then the submodule is not then isolated from its user.
Using uses
The main issue with uses is that a uses module can contain other uses statements, and in fact might use the same module in different places. If this happens, a "loop" can occur in the uses statements. It is also possible for uses statements to "drag in" all of the uses subreferences from a module. I refer to this as "zipper effect", because it causes all of the submodules to be included, to any depth.
For the first issue, Pascaline has the rule that no uses module is included twice, at any depth. This effectively stops uses loops.
For the second issue, this is more compiler dependent. The compiler must obey the uses order specified. ie., if a submodule uses another module, it must load that module into its declaration space, but it does not have to keep it for higher levels[2]. This leads to the problem that a used module may have references that the using module does not have access to. For example:
In this case y "exports" type q, but the base type of it, n, is not visible to program x. So x could have types that are opaque to it. The solution to this is for x to also use module z if it needs to[3].
The meaning of modularity
These kinds of concerns are not unique to Pascaline, and (for example) even C has a convention used to prevent duplicate inclusion.
The general use of modules tends to dictate an effect I call "fractionalization". This is a consequence of the fact that lower level types and routines that are needed by multiple modules will tend to be promoted into their own modules just so they can be used or joined by all of those modules. This tends to mean that complex programs get broken down into smaller and smaller parts.
A forward for modules
What can help with fractionalization is a good "forward" statement that works at the module level. Turns out there is one, just not the way you would expect.
Here program x defers a routine that multiple modules want to use but maintains the implementation of that routine within itself. It goes the opposite direction from fractionalization because it essentially places a forward reference to the routines contained in x to a lower level uses module, but brings the implementation back up to a high level[5].
This works because the "forward reference" occurs at runtime. Module x fixes the definition of "gettype" at runtime.
History of overrides in Pascaline
Overrides in Pascaline started in about 1993 as a way to form plug in modules in the library implementation system. The terminal and graphical libraries of Pascaline were designed to all be compatible with lower level modules. That is, the terminal I/O system also implemented serial I/O systems, and the graphical I/O system implemented both serial I/O and terminal I/O as well as graphics, etc. The easiest way to implement this was to have the modules plug into each other, and this meant overrides.
The first overrides in Pascaline were done with assembly language, but I noticed that several languages implemented overrides as a formal language construct. Thus they eventually made their way into the language.
[1] UCSD was an (incompatible) dialect of Pascal, but the subset it uses was, in fact, identical to that of Pascal-P2. That made sense, because UCSD Pascal used that compiler as a base.
[2] There were a lot of design considerations about this rule. I think Pascal-P6 implements this the simplest way right now, which is accumulation (including zipper effect). The solution to this is to use the joins statement, which not only purges the joined module from the current program deck, it preserves the joined contents and allows them to be referenced at anytime with qualidents.
[3] Uses, joins, and what happens when they are used together is a very complex subject that deserves its own article.
[4] In fact, it famously allows the compiler to completely convert the used file into a binary form for quick evaluation on successive uses.
[5] If you are confused about how you can override a regular procedure or function in a module, please read the Pascaline specification. Modules are a static form of classes in Pascaline, and use the same virtual/override syntax.
Beta Was this translation helpful? Give feedback.
All reactions