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
{{ message }}
Repository navigation
Commit 814157b
Browse filesBrowse the repository at this point in the historyBrowse files
This commit adds src/Language/PureScript/CoreFn/Laziness.hs, a
transformation on CoreFn that introduces call-by-need semantics to
bindings in recursive binding groups. This is the only way to restore
soundness to those groups without forbidding many programs we've
previously allowed, as historically any reference to another identifier
within the same binding group has been permitted as long as it occurs
within a lambda, regardless of whether that lambda is reduced in the
course of initialization.
The rest of PureScript remains call-by-value. Also, the imperfect
prohibitions on cyclic references enforced during desugaring and type
checking remain in place; consequently, this change does not alter the
set of programs accepted by the compiler.
For details, see the extensive commentary in Laziness.hs.
This is unlikely to break a working program, but the upshot for users is
that it's now possible to get a run-time error when dereferencing an
identifier in a recursive binding group before it has been initialized,
instead of silently getting an `undefined` value and having that maybe
or maybe not lead to an error somewhere else.
This is unlikely to break a working program, but the upshot for users is
4
+
that it's now possible to get a run-time error when dereferencing an
5
+
identifier in a recursive binding group before it has been initialized,
6
+
instead of silently getting an `undefined` value and having that maybe
7
+
or maybe not lead to an error somewhere else.
8
+
9
+
This change can cause code that relies on tail-call optimization to no
10
+
longer compile with that optimization. If you find that code that
11
+
previously compiled to a TCO loop no longer does but does include `$lazy`
12
+
initializers, please report the issue.
13
+
14
+
**Alternate backend maintainers:** for you, this change represents a
15
+
clarification of a responsibility shared by all backends. The identifiers
16
+
bound in a recursive binding group need to behave as if those identifiers
17
+
have call-by-need semantics during the initialization of the entire binding
18
+
group. (Initializing the binding group entails ensuring every initializer
19
+
has been executed, so after the binding group is initialized, these
20
+
identifiers can be considered call-by-value again.)
21
+
22
+
If an identifier is needed during its own call-by-need initialization, the
23
+
backend must ensure that an explicit run-time error is raised appropriate for
24
+
your target platform. This error may be raised at compile time instead if the
25
+
backend can determine that such a cycle is inevitable. Returning your
26
+
target language's equivalent of JavaScript's `undefined`, as `purs` did in
27
+
earlier releases in some cases, is not permitted.
28
+
29
+
If your target language natively has call-by-need semantics, you probably
30
+
don't have to do anything. If your target language is call-by-value and you
31
+
are using PureScript as a library, you can use the function
32
+
`Language.PureScript.CoreFn.Laziness.applyLazinessTransform` to your CoreFn
33
+
input to satisfy this responsibility; if you do, you will need to do the
34
+
following:
35
+
36
+
* Translate `InternalIdent RuntimeLazyFactory` and `InternalIdent (Lazy _)`
37
+
identifiers to appropriate strings for your backend
38
+
* Ensure that any output file that needs it has a reference to a function
39
+
named `InternalIdent RuntimeLazyFactory`, with type `forall a. Fn3 String
40
+
String (Unit -> a) (Int -> a)`, and with the same semantics as the
41
+
following JavaScript (though you should customize the error raised to be
42
+
appropriate for your target language):
43
+
44
+
```js
45
+
function (name, moduleName, init) {
46
+
var state =0;
47
+
var val;
48
+
returnfunction (lineNumber) {
49
+
if (state ===2) return val;
50
+
if (state ===1) thrownewReferenceError(name +" was needed before it finished initializing (module "+ moduleName +", line "+ lineNumber +")", moduleName, lineNumber);
51
+
state =1;
52
+
val =init();
53
+
state =2;
54
+
return val;
55
+
};
56
+
};
57
+
```
58
+
59
+
If neither of the previous cases apply to you, you can meet this
60
+
responsibility most easily simply by ensuring that all recursive bindings are
61
+
lazy. You may instead choose to implement some light analysis to skip
62
+
generating lazy bindings in some cases, such as if every initializer in the
63
+
binding group is an `Abs`. You also may choose to reimplement
64
+
`applyLazinessTransform`, or even develop a more sophisticated laziness
65
+
transform for your backend. It is of course your responsibility to ensure
66
+
that the result of whatever analysis you do is equivalent to the expected
0 commit comments