Kevin Reid
2012-02-21 13:20:00 UTC
I had an idea last night. (It would be better to have some code for existing ideas, but I'm short on time and enthusiasm at the moment. Bear with me a few more months...)
Let's call this tool
willBeDeepFrozen(p :vow)
with the following properties:
* is DeepFrozen
* is a transparent-except-for-== forwarder around p
* if called before p is resolved, or if p is resolved and is not
DeepFrozen, it will become permanently "ruined" -- all calls will
throw.
(This last property is the same thing a Proxy does if it gets an invalid resolutionSlot.)
This allows us to break cycles of DeepFrozen objects:
def bar
def barW := willBeDeepFrozen(bar)
def foo as DeepFrozen {
... barW ...
}
bind bar as DeepFrozen {
... foo ...
}
If the real bar is needed, then it can be obtained, without introducing any new facilities, as
barW.__whenMoreResolved(fn x {x})
The problem this addresses is currently handled in E-on-CL by using "EventuallyDeepFrozen", a kludge which would be applied to "foo" in the above example and which consists of foo giving access to its internals to the DeepFrozen guard, so that it can check if foo is *currently* DeepFrozen. This is much less efficient on DF checks of foo, but avoids the cost of barW's forwarding inside foo's code.
Furthermore, this serves as a general solution to the problem of cyclic dependencies among emakers while keeping <import> DeepFrozen (i.e. it does not return a promise on cyclic import, as it does now): we simply provide a way to request one of these wrappers from <import>. (It would have to crash if you do a regular import.)
I think that (if this works in practice) it is a superior solution to EventuallyDeepFrozen. What do you think?
A further refinement would be to make the forwarder transparent to == (and audit checks), thus avoiding the need to unwrap it ever. However, this would mean that it becomes ruined if examined using these ordinarily-side-effect-free before its referent resolves. This seems excessively magical. Comments?
Let's call this tool
willBeDeepFrozen(p :vow)
with the following properties:
* is DeepFrozen
* is a transparent-except-for-== forwarder around p
* if called before p is resolved, or if p is resolved and is not
DeepFrozen, it will become permanently "ruined" -- all calls will
throw.
(This last property is the same thing a Proxy does if it gets an invalid resolutionSlot.)
This allows us to break cycles of DeepFrozen objects:
def bar
def barW := willBeDeepFrozen(bar)
def foo as DeepFrozen {
... barW ...
}
bind bar as DeepFrozen {
... foo ...
}
If the real bar is needed, then it can be obtained, without introducing any new facilities, as
barW.__whenMoreResolved(fn x {x})
The problem this addresses is currently handled in E-on-CL by using "EventuallyDeepFrozen", a kludge which would be applied to "foo" in the above example and which consists of foo giving access to its internals to the DeepFrozen guard, so that it can check if foo is *currently* DeepFrozen. This is much less efficient on DF checks of foo, but avoids the cost of barW's forwarding inside foo's code.
Furthermore, this serves as a general solution to the problem of cyclic dependencies among emakers while keeping <import> DeepFrozen (i.e. it does not return a promise on cyclic import, as it does now): we simply provide a way to request one of these wrappers from <import>. (It would have to crash if you do a regular import.)
I think that (if this works in practice) it is a superior solution to EventuallyDeepFrozen. What do you think?
A further refinement would be to make the forwarder transparent to == (and audit checks), thus avoiding the need to unwrap it ever. However, this would mean that it becomes ruined if examined using these ordinarily-side-effect-free before its referent resolves. This seems excessively magical. Comments?
--
Kevin Reid <http://switchb.org/kpreid/>
Kevin Reid <http://switchb.org/kpreid/>