Skip to content

Transpile let a = 1 to const [a] = __mutable__ (1) #190

@pearmini

Description

@pearmini

Related Issue #50
Ref. Observable Mutable
Ref. Observable Framework Mutable
Ref. Observable Framework Converting Mutable
Ref. Observable Notebook Kit Source
Ref. Add Observable Plot Attribution to Mosaic

Given this code snippet:

let a = 0;

a += 1;

echo(a);

If we transpile to the following snippet, it won't work. Because outside the codeblock defining a mutable, it's just a normal value, without value property:

const a = __mutable__(0);

a.value += 1; // a.value is undefined 

echo(a); // 1

What if we expose a setter from the defining place? It still doesn't work as expected. The block setA(a+1) results in incrementing forever. Because it reference a and mutate a, and every block referencing a reruns when a is mutated.

const [a, setA] = (() => {
  const a = __mutable__(0);
  return [a, (value) => a.value = value];
})();

setA(a + 1);

echo(a);

How about we expose a setter as well? It works. Because setA(getA() + 1); doesn't reference a anymore!

const [a, setA, getA] = (() => {
  const a = __mutable__(0);
  return [a, (value) => a.value = value, () => a.value];
})();

setA(getA() + 1);

echo(a);

In order to make mutable more transpiling-friendly, changes API a little bit. Then converts a to mutator$$a.value.

const [a, mutator$$a] = __mutator__(0);

mutator$$a.value += 1;

echo(a);

But this brings another question: When should we transpile a to mutator$$a.value, and when should we leave it as is?

We can't transpile all of them to mutator$$[NAME].value, because they are not reactive anymore.

let a = 0;

setTimeout(() => {
  a += 1;
}, 1000);

echo(a); // !!!! 0 instead of 1

// ------------------>

const [a, mutator$$a] =  __mutator__(0);

setTimeout(() => {
  mutator$$a.value += 1;
}, 1000);

echo(mutator$$a); // mutator$$a is not a mutable 

My intuition is that if a codeblock mutates the mutatble, then transpile it, otherwise don't. But what if a block mutates and references a mutable:

let a = 0;

setTimeout(() => {
  a *= 10;
}, 1000);

{
  a += 1;
  echo(a); // !!! 1 instead of 11
}

This will not work as expected either. So I guess we should discourage this usage? Because you can always split them up:

let a = 0;

setTimeout(() => {
  a *= 10;
}, 1000);

a += 1;

{
  echo(a); // 10
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions