Support cross-stage persistence (CSP) #45
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Cross-stage persistence is the ability for code fragments to refer to values created at a previous stages, as in
val foo = 'ok; code"Some(foo.name)"
– notice the bare reference tofoo
, which is not an inserted piece of code, but really the value'ok
.This capability is extremely useful when doing runtime multi-stage programming. For instance, imagine that you are making a data manager class
DataManager
that uses multi-stage programming (i.e., runtime code generation, composition, and compilation) to speed-up some of its operations. It could look like:Using
myDataManager.stagedIterator
, one can obtain a closed piece of code (of typeCode[Iterator[Int],{}]
) that can be inserted into a bigger program and optimized, and then runtime-compiled –– the generated bytecode will have a runtime reference tomyDataManager.data
.The feature does not make sense for compile-time multi-stage programming or static code generation. I had previously shied away from supporting CSP because of the unclear behavior in this case. Squid now solves it in two ways:
cross-stage persistence is only enabled if the
Base
extendsCrossStageEnabled
, which prevents users from using CSP by mistake;if CSP is enabled and one tries to generate static Scala code (as opposed to runtime-compiling it), then Java serialization is used on a best-effort basis to convert cross-stage values to a string representation to be loaded when the generated program is ran;
for example in
val ls = List(1,2,3); val cde = code"ls.map(_+1).sum"
,cde
contains a cross-stage reference to aList(1,2,3)
. Trying to generate static Scala code from it withbase.scalaTree(cde.rep)
results in: