Merging Bind Syntax with Relationships

# Kevin Smith (5 years ago)

I'm not sure to what extent this has been discussed previously, but I think we should consider merging relationships and bind syntax.

The relationships strawman introduces two new built-in symbols: @geti and @seti. I will instead use the more descriptive names: Symbol.referenceGet and Symbol.referenceSet.

The basic idea is that arbitrary objects can be used as the "referenced name" in a Reference object. When GetValue or PutValue is called on those kinds of reference objects, behavior is delegated to the functions defined on the "reference name" object at the appropriate symbol. If the required method is not defined on the name object, then an exception is thrown.

The operator "::" creates such a reference object.

The following code should illustrate the idea:

Function.prototype[Symbol.referenceGet] = function(target) { return this };

Map.prototype[Symbol.referenceGet] = function(target) {

    while (target !== null) {

        if (this.has(target))
            return this.get(target);

        target = Object.getPrototypeOf(target);
    }

    return void 0;
};

Map.prototype[Symbol.referenceSet] = Map.prototype.set;

// Using a Map as a set of internal fields:
var obj = {};
var field = new Map;

obj::field = "value";
obj::field; // "value"

// Using a function as an extension method:
function f() { return this.x; }

obj.x = "abc";
obj::f(); // "abc";
# Benjamin Grurnbaum (5 years ago)

What about protocols?

# Kevin Smith (5 years ago)

Can you elaborate? I know that Russell Leggett did some work along those lines a while ago (which really inspired the merge idea), but can you describe exactly what you mean?

# Kevin Smith (5 years ago)

Note though, that since behavior of referenceGet is completely arbitrary, you could (for instance) create a "reference name" object which traverses up the prototype chain looking for a certain method name, and returns some fallback method if nothing is found.

# Benjamin (Inglor) Gruenbaum (5 years ago)

When you asked about discussion and this ran into my mind :)

I remember it was discussed here esdiscuss.org/topic/protocol-library-as-alternative-to-refinements-russell-leggett, ( the syntax in gist.github.com/genericallyloud/7086380 )

Do the semantics proposed above work with what's proposed there?

The "using the function as an extension method" seems pretty similar. However , Russell's work proposes a form of polymorphic dispatch based on the extended type. For example:

 let dmot = new DoingMyOwnThing("Bob");
    dmot::map( n => n * 3;) // calls method `map` of the DoingMyOwnThing

class instead of using the protocol method

This is really useful behavior that solves a few very real problems for me (it's real extension methods) - I think it would be very beneficial for discussions about bind syntax to allow or at least consider this.

# Kevin Smith (5 years ago)

I remember it was discussed here esdiscuss.org/topic/protocol-library-as-alternative-to-refinements-russell-leggett, ( the syntax in gist.github.com/genericallyloud/7086380 )

Do the semantics proposed above work with what's proposed there?

That's it - thanks for the link!

Yes - it could definitely work. Russell didn't actually an implementation for the his Protocol function, but using this proposal, the "methods" property of the resulting "protocol" object could be a dictionary of custom "reference name" objects.

Leaving aside all of the gory dispatch details, it might look something like this:

class Protocol {

    constructor(...names) {

        this.methods = {};

        for (let name of names)
            this.methods[name] = this._createMethod(name);
    }

    _createMethod(name) {

        return {

            [Symbol.referenceGet]: target => {

                // 1. If target has an own property of "name", return it.
                // 2. If target's type matches an extension of the protocol,
                //    return the protocol method.
                // 3. If the target has a method of the same name somewhere up
                //    the prototype chain, return it.
                // 4. If a default has been defined, return it.
                // 5. Otherwise, return undefined.
            }

        };
    }
}