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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions