Skip to content

Commit 8656be3

Browse files
committed
Add section "Limitations to Splicing"
1 parent 07bbb2b commit 8656be3

File tree

1 file changed

+30
-9
lines changed

1 file changed

+30
-9
lines changed

docs/docs/reference/symmetric-meta-programming.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ Here’s an application of `map` and how it rewrites to optimized code:
253253
ys
254254
}
255255

256-
257256
### Relationship with Inline and Macros
258257

259258
Seen by itself, symmetric meta-programming looks more like a
@@ -399,6 +398,36 @@ to `T` but only `~` is subject to the PCP, whereas `run` is just a normal method
399398
def run: T // run staged code
400399
}
401400

401+
### Limitations to Splicing
402+
403+
Quotes and splices are duals as far as the PCP is concerned. But there is an additional
404+
restriction that needs to be imposed on splices to guarantee soundness:
405+
code in splices must be free of side effects. The restriction prevents code like this:
406+
407+
var x: Expr[T]
408+
’{ (y: T) => ~{ x = ’(y); 1 } }
409+
410+
This code, if it was accepted, would "extrude" a reference to a quoted variable `y` from its scope.
411+
This means we an subsequently access a variable outside the scope where it is defined, which is
412+
likely problematic. The code is clearly phase consistent, so we cannot use PCP to
413+
rule it out. Instead we postulate a future effect system that can guarantee that splices
414+
are pure. In the absence of such a system we simply demand that spliced expressions are
415+
pure by convention, and allow for undefined compiler behavior if they are not. This is analogous
416+
to the status of pattern guards in Scala, which are also required, but not verified, to be pure.
417+
418+
There is also a problem with `run` in splices. Consider the following expression:
419+
420+
’{ (x: Int) => ~{ {’(x)}.run; 1 } }
421+
422+
This is again phase correct, but will lead us into trouble. Indeed, evaluating the splice will reduce the
423+
expression `{’(x)}.run` to `x`. But then the result
424+
425+
’{ (x: Int) => ~{ x; 1 } }
426+
427+
is no longer phase correct. To prevent this soundness hole it seems easiest to classify `run` as a side-effecting
428+
operation. It would thus be prevented from appearing in splices. In a base language with side-effects we'd have to
429+
do this anyway: Since `run` runs arbitrary code it can always produce a side effect if the code it runs produces one.
430+
402431
### The `Liftable` type-class
403432

404433
Consider the following implementation of a staged interpreter that implements
@@ -615,14 +644,6 @@ splice evaluation context `e_s` are defined syntactically as follows:
615644
Eval context e ::= [ ] | e t | v e | ’e_s[~e]
616645
Splice context e_s ::= [ ] | (x: T) => e_s | e_s t | q e_s
617646

618-
A _run_ operation can be added to the calculus by adding a binding
619-
`run : expr T -> T` to the initial environment, together with the evaluation rule
620-
621-
run('t) --> t
622-
623-
That is, `run` reduces in the same way as `~` when in evaluation position. But unlike splices,
624-
`run` operations are only evaluated outside of quotes, which means they are not affected by the PCP.
625-
626647
### Typing rules
627648

628649
Typing judgments are of the form `Es |- t: T`. There are two

0 commit comments

Comments
 (0)