Skip to content

Instantly share code, notes, and snippets.

@pipcet
Created April 22, 2016 13:19
Show Gist options
  • Save pipcet/878f46a85019f4b7917fc07f8f8798aa to your computer and use it in GitHub Desktop.
Save pipcet/878f46a85019f4b7917fc07f8f8798aa to your computer and use it in GitHub Desktop.
Not-actual-Values
https://bugzilla.mozilla.org/show_bug.cgi?id=1259280
Currently, SpiderMonkey (without asm.js) has a unique NaN value. It might be necessary for compliance to distinguish all IEEE NaN values in case they are stored into Float64Arrays or Float32Arrays. The idea would be to use a JS::Value that has the same tag as the canonical NaN but a payload pointing to a garbage-collected cell containing the actual payload that would be written to a typed array.
But why stop there? We could allow any value, including objects, to be "NaNified": define functions NaV ("not an actual value") and VaN such that:
NaV(x) !== NaV(y) (in particular, NaV(x) !== NaV(x))
VaN(NaV(x)) is identical to x
In essence, we allow anything as the "payload" of a NaN, even JavaScript objects. In particular, we allow Promises:
NaV(Promise(x)) represents what we would have done with x had it been an actual value: it's a what-if expression.
Now we define, for a Promise p, NaV(p) === NaV(p) to be NaV(p.then(whatif)) where whatif(v) is what the current function would have returned had NaV(p) been the actual value v: it's the current function's stack frame restarted at the point where the comparison was made.
For example, the function
function delayedInc(x)
{
var v = x === x;
if (!v)
return v;
return x + 1;
}
could be applied to a Promise p to effectively result in
NaV(p.then(function (xv) { return xv + 1; }));
(It would actually be NaV(p.then(function (xv) { var v = xv === xv; if (!v) return v; return xv + 1; })))
Capturing the current function's stack frame sounds hard, but it's what generators already do, so it should be possible.
I've looked at the code again to see how difficult it would be to store nan-canonical NaNs as garbage-collected cells, and I think that would be doable. The idea is that the JS::Value would have an NaN tag, but a pointer payload, which is traced but only really used when the value is stored into a typed array—all other places will be happy with just any NaN value, as far as I can see.
But then it occurred to me that it might be an interesting extension idea to allow any value to be "NaNified": define functions NaV ("not an actual value") and VaN such that NaV(x) !== NaV(y), but VaN(NaV(x)) is identical to x for all values x. I think this would be useful for Promises: make NaV(p) stand for the non-actual value of p, then redefine NaV(p) === NaV(p) to be NaV(p.then(whatif)), where whatif is the current function restarted from the point where the comparison was made.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment