Coder Social home page Coder Social logo

proposal-refcollection's Introduction

RefCollection

ECMAScript proposal for the RefCollection.

Author:

  • Robin Ricard (Bloomberg)

Champions:

  • Robin Ricard (Bloomberg)
  • Richard Button (Bloomberg)

Advisors:

  • Dan Ehrenberg (Igalia)

Stage: 0

Overview

The RefCollection introduces a way to keep value references to objects through symbols.

One of its main goals is to be able to keep track of objects in Records and Tuples without introducing anything mutable in them.

The RefCollection is able to automatically track symbols in such a way that when they become unreachable (and since symbols can't be forged) the referenced object can become unreachable as well.

Examples

const refc = new RefCollection();

const rootRef = refc.ref(document.getElementById("root"));
assert(refc.deref(rootRef) === document.querySelector("#root"));

const otherRootRef = refc.ref(document.querySelector("#root"));
assert(rootRef === otherRootRef);
const refc = new RefCollection();

const vdom = #{
    type: "div",
    props: #{ id: "root" },
    children: #[
        #{
            type: "button",
            props: #{
                onClick: refc.ref(function () {
                    alert("Clicked!");
                }),
            },
            children: #[
                "Click Me!",
            ],
        },
    ],
};

refc.deref(vdom.children[0].props.onClick).call();
// Alert: Clicked!

API

new RefCollection()

Creates a RefCollection. Does not takes any initializer arguments.

RefCollection.prototype.ref(obj, sym = Symbol())

Returns a stable symbol for a corresponding object obj.

You can optionally give a symbol sym that will be used if a new symbol needs to get registered. If the obj is already in the RefCollection, sym will get discarded. If sym is already pointing to an object in the refCollection, expect a TypeError.

RefCollection.prototype.deref(sym)

Returns the object corresponding to the symbol sym. If a symbol not returned by the same RefCollection gets passed here you will receive undefined.

Behaviors

Object-Ref stability

The collection does not let you change or remove from it, only add. So for a same object, expect the same ref symbol.

Unreachability

The RefCollection will mark all objects tracked as unreachable as soon as the collection becomes unreachable.

If a symbol becomes independently unreachable, since they can't be reforged, the collection will mark the corresponding object as unreachable from the collection.

Note: This second point is impossible to polyfill.

Polyfill

You will find a polyfill in this same repository.

FAQ

Why Symbols?

Symbols are a good way to represent that reference since we can't reforge them to recreate a ref from nothing (which a number or a string would have been possible to forge).

proposal-refcollection's People

Contributors

dependabot[bot] avatar rricard avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

jridgewell

proposal-refcollection's Issues

disassociated ownership

Some "basic" usage of ref collections and records:

// We have some records and refs (which must always be tracked together)
let refA = ...;
let recA = ...;

let refB = ...;
let recB = ...;

// We combine them
let refC = ...;
let recC = #{ recA, recB, ... };
// OR ...
let refD = ...;
let recD = #{ ...recA, ...recB, ... };

There are a number of problems present here.

  1. refX and recX must always be tracked together.
  2. The value representing a ref is not inherently a ref, it's a symbol. There's no way for someone to know which symbols need dereferencing.
  3. When you combine records you end up tracking N+1 ref collections (where N is the number of records you combined) an exponential amount of ref collections (because you inherit all the collections those refs were tracked with) without any way of knowing which symbols go to which references, and now you have to drag around all these ref collections for 1 record.
  4. The semantics of combining ref collections to fix (3) are most likely undecidable because the old ref collection still has to exist and will likely be used by other parts of the code, resulting in symbol reuse between collections.

`ref` can't take a symbol

If ref takes a symbol, then I can pass it a registered symbol (Symbol.for('foo')) or a well-known symbol (Symbol.iterator).

These can never be garbage collected, which is why Symbols are not permitted in WeakMap keys, or WeakRefs.

In other words, there's an established precedent that only unregistered symbols could be weakly held, and that any API that takes both kinds of symbols must treat them the same, which means no API that takes a symbol can weakly hold it. (cc @erights @devsnek).

Give more use cases and examples

I like how the examples seem to relate to real applications, but giving examples at a bit more length to clarify both why it's needed and how it's used would be nice. For example, it'd be good to show:

  • How you could use a single RefMap across the lifecycle of a whole program, depending on its native GC properties
  • How you could use multiple RefMaps if you want, locally, even providing that second symbol argument in a template-like way
  • What it looks like when you print the result of .ref() (namely, Symbol())

Bikeshed the class name

I like the RefMap name intuitively, but at the same time, the API is not parallel to Map. So, it might be best to choose a different name. Off the top of my head:

  • RefGroup
  • RefCollection
  • References
  • RefIndirection
  • (Also, for any of these, we could use Reference instead of Ref--you repeat the class name fewer times than the method name.)

What do other people think?

(I like the choice of the names of the methods, ref and deref, in any case!)

Clarify explanations in the explainer

We probably want to tweak the wording around garbage collection to make it more pedantic. In particular, engines don't 'release' things, but rather, if the symbol is impossible to forge, then the engine may reason that the value in the refm is unreachable, even if it's logically "never deleted".

Similarly, the text about object-ref stability could also use rewording for clarity, explaining that, at a high level, this is a never-changing map that you can just add entries to, and why this is nice to have (basically, it's dependable and meets use cases).

Document: Why a Symbol?

What is gained over a simple integer?

There was mention of being unable to be forged, but if you already must have a direct reference to the ref collection itself to perform a deref(), the reference to the ref collection is also unable to be forged.

More realistic VDOM example

The example in the README shows a record representing the VDOM for a button, but it's assigned to a variable once, rather than produced by a function, so it misses showing usage for components and the suitability for diffing.

A more realistic example would be probably something like this:

const Button = (label) => {
  const refsc = new RefCollection();
  return {
    refs: refc,
    vdom: #{
        type: "div",
        props: #{ id: "root" },
        children: #[
            #{
                type: "button",
                props: #{
                    onClick: refc.ref(function () {
                        alert("Clicked!");
                    }),
                },
                children: #[
                    refc.ref(label), // The label is changeable between renders
                ],
            },
        ],
    },
  };
}

Apps will have multiple renders with the same components. It'll be state-change / event driven, but it's basically this:

// first render
vdomLib.render(Button('Hello'), document.body)
// second render
vdomLib.render(Button('Welcome'), document.body)

The key behavior we need is that the record is identical between calls to Button() so that the VDOM library does not need to deeply diff the two records:

Button('Hello').vdom === Button('Welcome').vdom; // this must be true

Define common usage pattern

As seen in #7, creating refcollections for every use is nonergonomic. We intend to add a common RefCollection per realm. While doing this we need to be careful defining interactions with membranes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.