Uses and joins together (programming lessons in Pascaline) #253
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.
-
Uses and joins together (programming lessons in Pascaline)
Previously I talked about the uses statements. Uses is pretty straightforward, and so is joins. However, using them together gets
complex.
uses and the flood model
A uses statement is (by design) pretty close to an include:
This would merge all of the definitions in a and b together with the current program. If any of the definitions in a or b conflict with the program, or with each other, it will be an error.
So everyone and their dog implements uses, what could go wrong?
Problems with uses
The first issue is that uses basically unites the program and all of its submodules (modules it uses/joins) into a large single text. If
there are any duplicate symbols, you will hear about it. And when you start using large modules together with large programs, you will hear about it.
This was "solved" in many implementations with a "rename" type facillity. That is like:
uses a symbol q renamed as n;You get the idea. q moves to the name n, just in the using module, and voila! There is no conflict. Ok, with all respect, this is an awful solution. Trying to unite complex modules is bad enough. Trying to read though multiple modules with renames is far worse. You don't know what the symbol is called in module xyz. you have to reference the uses statements to figure it out[1].
The other issue with uses is what I call "zipper effect". Zipper effect is the tendency of uses statements that are nested to just dump
all of the symbols, to any depth, into your program. That is:
Here test includes all of the definitions of a into test, and because a uses b, it gets all of the definitions of b as well. This can go to
any depth. All uses implementations have this issue, and even C programs have this issue.
Finally, there is the loop issue. If you have:
Voila! You have a loop in the uses statements, resulting in anything from a crash, lockup or simlar.
Solutions
The solution to duplicate symbols in Pascaline is the joins statement. I'll cover that in a bit.
Zipper effect is more interesting. The "deluxe" algorithm is that each uses pull is conditioned on if the original user also uses that
module. That is:
The definitions in b would only be included in test if, and only if,
test also includes b:
This requires the compiler to keep track of uses symbols, and if required, remove b from the space of test if not specifically used.
The alternative to this is what I call the "flood" model. This is where the compiler simply includes everything in the program or module that uses it.
And the algorithim Pascal-P6 uses?
The flood model. Hey, the deluxe algorithm is hard to implement.
Finally, the loop fix is the one everyone (and their dog) implements, including C programs. And that is, if the compiler sees the uses
module is already read, it simply skips it.
Enter joins
joins fixes the issues with uses, for a price. A joins statement includes the joined module, but keeps the symbols separated from the
program or module that joined it. Then the entire joined module, to any depth of module nesting, is kept in a different symbols space.
Now there is no conflict. q exists in test and a, but they are separate, and there is no error. If test wants to use the var q in
module b, it has to do:
b.q
Or a so called "qualident" or "qualified identifier", that specifies what module we are talking about.
This solves the issues with uses. Using joins you can have as many modules as you want, and they won't conflict[3].
Why you still may want uses
Uses are, in most cases, just easier to use (pun intended). Plus, a lot of programmers will divide up a program into uses modules only after the program gets large. With uses this is easy. With joins it means relabeling all of the symbols in the divided code[2].
The other factor is that sometimes Pascaline requires you to include certain modules as uses modules. The Pascaline specification requires that you apply uses if you are going to overload operators in a new module. Why?
Operator overloads are a very powerful technique, usually used to extend Pascaline operators to new, user defined types. They have the ability to change the definition of operators in programs that include those modules.
As such, Pascaline requires that modules that extend each others operators be intimately tied together, as uses modules.
Thus, Pascaline basically requires uses in some cases.
Mixing uses and joins
When uses and joins get complex is when they are used together. First of all, joins must preceed uses in Pascaline. This rule is meant so that joins definitions cannot rely on uses definitions, which would violate the rule that modules should not depend on how they are used.
When considering a tree of mixed uses and joins definitions:
Because the joins definitions are removed from the active definitions, the net set of definitions left behind in the program are of the tree with the joins definitions removed:
Thus the net set of definitions in the program are those that are targets of a uses definition, or modules that are nested in uses modules, etc.
The uses definitions that remain can well reference definitions in the joined modules, but must use qualidents to reference them. Thus there can be no definitions remaining in the program whose base definitions cannot be reached.
At present, any joined module can be reached by the program via qualident, regardless of if that module is joined in the program using the reference or not. This will likely be changed so that such references result in an error. ie., if you reference it via qualident,
then join the module containing it.
Summing up
IP Pascal did not use to have a joins statement. This resulted in several practical problems creating modules using just the uses statement. joins fixes those issues, and often uses and joins are used together.
There are other issues, such as how errors are rippled up to the top layers in a modular system, and how programs like large interpreters can restart from deep nested calls. Because modules are compiled to final form before being linked together, this means that mechanisims to deal with those issues must be constructed at runtime.
This is what exceptions and overrides are good at. The result is, with Pascaline, writing modular programs is different than Pascal, and different even than Pascal with a uses statement.
[1] Fortunately or unfortunately, there are ways to do a rename in effect in Pascaline. These include creating a type alias, wrapping a variable or procedure/function in another, etc.
[2] Actually, I think this is a good thing. It helps you to think about modularizing your programs.
[3] If you have used C++ "namespaces", this will be familiar. The difference is that in Pascaline, it aligns with modules.
Beta Was this translation helpful? Give feedback.
All reactions