You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: courses/RascalAmendmentProposals/RAP6/RAP6.md
+15-9Lines changed: 15 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,16 +12,16 @@ sidebar_position: 6
12
12
13
13
### Issue
14
14
15
-
We have received reports, and encountered ourselves, some baffling emergent behavior of the current semantics of \`import\` and \`extend\` module semantics in Rascal. We see unexpected and complex behaviors, often after long debugging sessions, where the implementation is not wrong per se, but just very hard to get. Ergo, we have to do something about the design of these two language features. It’s not comprehensible now.
15
+
We have received reports, and encountered ourselves, some baffling emergent behavior of the current semantics of `import` and `extend` module semantics in Rascal. We see unexpected and complex behaviors, often after long debugging sessions, where the implementation is not wrong per se, but just very hard to get. Ergo, we have to do something about the design of these two language features. It’s not comprehensible now.
16
16
17
17
### Analysis
18
18
19
19
First the current relevant features of import and extend are listed:
20
20
21
-
*“import X;” makes the declared items (variables, functions, data-types and syntax-definitions) of the imported module “X” visible in the importing module.
21
+
*`import X;` makes the declared items (variables, functions, data-types and syntax-definitions) of the imported module “X” visible in the importing module.
22
22
* An “imported” module is a singleton instance, with its own module state with respect to its global variables
23
23
* If more than one importer modules A and B import the same imported module X, they share the same view on the state of the imported module X.
24
-
*“import X;” does not make the declared items of modules it imports itself visible to the importing module. Import is not transitive.
24
+
*`import X;` does not make the declared items of modules it imports itself visible to the importing module. Import is not transitive.
25
25
* This is like Java import also works and what our (beginner) audience expects
26
26
* It helps to keep namespaces clean
27
27
* It is therefore necessary for information hiding and reuse
@@ -32,7 +32,7 @@ First the current relevant features of import and extend are listed:
32
32
* This behavior stems from a time when “extend” did not exist yet in Rascal. It’s a backward compatibility issue. When “extend” did not exist yet, this was the only reason why data-types, syntax definitions could be (marginally) modularly extensible. The “extend” feature was added later because this is not enough, in combination with extensible overloaded functions.
33
33
* When we added extensible overloaded recursive functions over the extensible data-types it became also urgent to add “extend”.
34
34
35
-
*“extend X”; like “import” also makes the declared items (variables, functions, data-types and syntax-definitions) of the imported module “X” visible in the importing module.
35
+
*`extend X`; like `import` also makes the declared items (variables, functions, data-types and syntax-definitions) of the imported module “X” visible in the importing module.
36
36
* An “extended” module is not an instance and it does not have state.
37
37
* Instead of making reference to an external module instance, all its declarations are cloned into the local scope of the extending module
38
38
* This also goes for global variables. Each extended global has its own instance in the extending module.
@@ -46,15 +46,17 @@ First the current relevant features of import and extend are listed:
46
46
* By effectively merging the declarations of data-types and syntax-definitions and functions into the same extending module, both recursive functions and recursive data and syntax types are now openly extensible
47
47
* Recursive calls in extended modules now resolve to the bigger overloaded function rather than to the overloaded function as it was in the original scope.
48
48
49
-
**Observation 1:** by not shadowing names declared by imported modules, “import” merges definitions almost like “extend” does, but not completely transitively and recursively. The **semi-merge** surely generates hard-to-predict run-time behavior (why did this function not match?)
49
+
50
+
> **Observation 1:** by not shadowing names declared by imported modules, “import” merges definitions almost like “extend” does, but not completely transitively and recursively. The **semi-merge** surely generates hard-to-predict run-time behavior (why did this function not match?)
51
+
50
52
51
53
* Never static errors are produced that this semi-merging is going on
52
54
53
-
**Observation 2:** open extensibility is for data-types (languages) and recursive functions that operate on these data-types is a distinguishing Rascal feature with a strong language-oriented flavor. It is an important (yet advanced) language feature. “Extend” does not have any information hiding feature, which is necessary for the “openness” it requires.
55
+
> **Observation 2:** open extensibility is for data-types (languages) and recursive functions that operate on these data-types is a distinguishing Rascal feature with a strong language-oriented flavor. It is an important (yet advanced) language feature. “Extend” does not have any information hiding feature, which is necessary for the “openness” it requires.
54
56
55
-
**Observation 3:** “import” is useful for libraries of non-extensible functions and specifically for information hiding. We can not do without “import” either: larger Rascal programs would become nearly impossible to write and maintain (remember ASF+SDF which only had “extend” semantics for its “import” declarations).
57
+
> **Observation 3:** “import” is useful for libraries of non-extensible functions and specifically for information hiding. We can not do without “import” either: larger Rascal programs would become nearly impossible to write and maintain (remember ASF+SDF which only had “extend” semantics for its “import” declarations).
56
58
57
-
**Observation 4:** the feature interactions between import and extend are gruesome.
59
+
> **Observation 4:** the feature interactions between import and extend are currently "gruesome"; unpredictable, with complex consequences, and no method of debugging.
58
60
59
61
* With globals involved, it becomes unclear what instance we are talking about
60
62
* With function merging involved it becomes unclear which overloaded alternatives are active at which level in the import/extend hierarchy
@@ -86,16 +88,19 @@ Positive consequences:
86
88
87
89
By affecting this change, the type checker will start producing more warnings and errors automatically. For example:
88
90
91
+
```rascal
89
92
module A;
90
93
import basic/Identifiers;
91
94
92
95
module B;
93
96
import A;
94
97
95
98
syntax Exp \= Id; // error undeclared non-terminal Id (before it would get the Id from basic/Identifiers via the transitive import of A).
99
+
```
96
100
97
101
and:
98
102
103
+
```rascal
99
104
module A
100
105
101
106
data X \= x();
@@ -108,7 +113,8 @@ import A;
108
113
// X shadows the X from module A:
109
114
data X \= y(X x); // possible warning: X is not productive, there is no base case
110
115
111
-
int f(y(x())) \= 1 // undeclared constructor x on local type \`X\`
116
+
int f(y(x())) \= 1 // undeclared constructor x on local type `X`
0 commit comments