User-reifiable References

# Claus Reinke (13 years ago)

Context:

References are a specification-level construct that has no 
language-level representation in current ES. Reifying 
References as objects has been deemed troublesome/
inefficient for implementations.

Problem:

The lack of reified References is a blocking issue when trying
to define control operator libraries: user defined assignment 
operators require at least References as arguments, some 
operators might want to return References.

I keep running into this limitation, and examples like the '??=' in strawman:default_operator show I'm not the only one. So I have looked for a workaround, and I think I've found a useful option, on which I'd like your input.

Suggestion:

The starting point is the representation of References as an object with a single getter/setter pair, so that reifying, eg.,

base[prop]

gives (*)

let reference = { get deref() { return base[prop] }
                           , set deref(v) { return base[prop] = v } }

This gives us an object that can be passed around, and can be reflected back to an r-value or l-value, depending on context:

reference.deref = reference.deref + 1;    

Of course, (*) is a handful, and gets worse when one tries to share computation within 'base' and 'prop' between getter and setter. No one is going to write this out by hand for every reified Reference, or read code with lots of these without struggling to see the simple intention behind the code.

The suggestion is to introduce a keyword phrase, so that

ref base[prop]

desugars to

(function(){
var _base = base;    // private symbols _base, _prop;
var _prop = prop;    // share evaluation of base, prop
return { get deref() { return _base[_prop] }
            , set deref(v) { return _base[_prop] = v } }
}())

(and similarly for the other valid Reference phrases)

Then, a user-defined assignment operator, eg. matching the strawman:default_operator's '??=', would be possible as (ignoring infix syntax and multiple evaluation of 'r' here, as those are separate issues)

function defaultAssign(r,def) {
    return (r.deref = r.deref===undefined ? def : r.deref)
}

var x, y = 2;
defaultAssign(ref x,1);
defaultAssign(ref y,1);
// x === 1, y === 2

The readability improvement is so extreme that I doubt anyone is using this pattern without syntax support?-)

Since reification of References happens only on request, this has no performance implications for normal operation.

Since reification is a simple desugaring, implementing and backtranslation into ES5 are not difficult.

Two questions come to mind:

1 where, exactly, does 'ref <Reference>' fit into the syntax and post-syntax checks for valid References?

2 'ref' is not reserved, what about existing code using 'ref'?

As to 2, if 'ref' is in common use as a variable (a github search [1] turns up some examples), I wouldn't mind having to import 'ref' from some standard module before using it as a keyword, or having local 'ref' declarations disable 'ref' as a keyword. That way, users can migrate away from 'ref' as a variable, but don't have to do so unless they want to use 'ref' as a keyword.

From what I can see, syntax support for user-reifiable References

would allow progress with control abstractions in JS, without having to invoke or wait for the full complexity of macros for JS.

Without something like user-reifiable References, even simple abstractions like assignment operators remain beyond the reach of JS library coders.

Thoughts? Claus clausreinke.github.com

[1] search?q=ref&type=Code&language=JavaScript

# Brendan Eich (13 years ago)

Claus Reinke wrote:

Thoughts?

As noted, we're moving away from References in the language. Macros are out there but the better way: you want to abstract over syntax, not runtime lvalue semantics.

# Claus Reinke (13 years ago)

As noted, we're moving away from References in the language.

l-values as function parameters are still going to be useful, for abstractions involving assignment operators.

Macros are out there but the better way: you want to abstract over syntax, not runtime lvalue semantics.

Useful macros are going to have a lot of expressive power over syntax, which tends to go along with decreased ability to reason about code. Functions are not as simple in JS as they are in other functional languages, but it still seems advisable to do as much as possible without macros.

I've updated my infix operator gist, to include initial support for 'ref', and also to wrap operator arguments - so both assignment operators and short-circuiting operators are now possible. Unwrapping of arguments and reference objects is done explicitly in operator definitions.

https://gist.github.com/3157251

See example 6 (strawman:default_operator, .??, .??=) in

https://gist.github.com/3157251#file_sample.js

The expected output (for those who don't want to clone and run the gist) can be seen in the comments of

https://gist.github.com/3157251#file_infix.js

In this gist, the desugaring of 'ref' and infix operators corresponds to hard-coded macros, doing the things with syntax that cannot be done with functions alone. The rest is handled by function definitions and conventional code.

Until macros make it into the language, this is an easy way to play with suggested operators (factoring out the parser hacking, some of which is still TODO).

Example extract:

 Operator['.??='] =
    function(lhs_,rhs_){
      var lhs   = lhs_();
      var value = lhs.deref;
      return (lhs.deref = ( value!==undefined ? value : rhs_()))
    };

(function(){
  var x;
  console.log('6(assignment x):', ref x .??= 1);
  console.log('6(assignment x):', x );
}());

Output extract:

6(assignment x): 1
6(assignment x): 1

Btw, I've also added (example 7) a simple version of Allen's new strawman:define_properties_operator, '.:=' .

Claus