#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?