Может ли идея аффинных (расслабленных линейных) Типы могут быть реализованы в нетипизированной настройке, чтобы включить безопасные мутации?

#javascript #functional-programming #mutation #linear-types

#javascript #функциональное программирование #мутация #линейные типы

Вопрос:

Было бы полезно, если бы я мог время от времени использовать безопасные деструктивные обновления на месте Array для s и Map s. Линейные типы — это метод, позволяющий допускать безопасные мутации, ограничивая использование значений семантикой ровно один раз. Хотя кажется невозможным реализовать ровно один раз в Javascript, вот реализация расслабленного варианта не более одного раза, который соответствует аффинным типам:

 class LinearProxy {
  constructor() {
    this.once = false;
  }

  get(o, k) {
    if (this.once)
      throw new TypeError("non-linear type usage");

    else this.once = true;

    if (k === "run")
      return once(f => {
        const r = f(o);

        if (r === o)
          throw new TypeError("non-linear type usage");

        else return r;
      });

    return o[k];
  }

  set(o, k, v) {
    if (this.once)
      throw new TypeError("non-linear type usage");

    o[k] = v;
    return true;
  }
}

const linear = o => new Proxy(o, new LinearProxy());

const once = f => {
  let called = false;

  return x => {
    if (called)
      throw new TypeError("non-linear type usage");

    else {
      called = true;
      return f(x);
    }
  };
};

const run = f => tx =>
  tx["run"] (f);
  
const id = x => x;
const last = xs => xs[xs.length - 1];
const dup = xs => [...xs, ...xs];

const xs = linear([1,2,3]),
  ys = linear([1,2,3]),
  zs = linear([1,2,3]);

xs[3] = 4;
xs[4] = 5;

console.log(
  "run(last) (xs):",
  run(last) (xs)); // 5

try {run(last) (xs)}
catch(e) {console.log("run(last) (xs):", e.message)} // type error (A)

try {const x = xs[4]}
catch(e) {console.log("x = xs[4]:", e.message)} // type error (A)

try {xs[0] = 11}
catch(e) {console.log("xs[0] = 11:", e.message)} // type error (B)

try {run(id) (ys)}
catch(e) {console.log("run(id) (ys):", e.message)} // type error (C)

console.log(run(dup) (zs)); // [1,2,3,1,2,3] (D) 

Последующий доступ на чтение (A) и доступ на запись (B) для однажды использованных линейных типов выдает ошибку типа. Попытка получить немедленный доступ к ссылке (C) линейного типа также выдает ошибку типа. Последнее — просто наивная проверка, чтобы предотвратить случайное возвращение ссылки. Его можно легко обойти, поэтому в этом случае мы должны полагаться на соглашение.

Однако D не является аффинным, поскольку аргумент используется дважды. Означает ли тот факт, что нелинейное использование происходит в пределах области действия функции, что оно все еще (относительно) безопасно? Есть ли более умный способ реализации линейных типов в Javascript?