mlhaufe / brevity

Brevity is a library that enables Feature-Oriented Programming (FOP) and solves the expression problem in a manner that makes data and operation declarations trivial to define and compose.
GNU Affero General Public License v3.0
1 stars 0 forks source link

Implement derived properties of Data variants #48

Closed mlhaufe closed 1 year ago

mlhaufe commented 1 year ago
const Person = Data({
  firstName: {},
  lastName: {},
  fullName: { value: (self) => `${self.firstName} ${self.lastName}` }
})
mlhaufe commented 1 year ago

Since variants are readonly, this derived property should be treated as a lazily evaluated field.

Semantically like:

const obj = {
  get foo() {
    delete this.foo
    return this.foo = 'whatever'
  }
}

A challenge here is that the variant needs to be frozen while also enabling this lazy redefinition..

An approach might be to emulate frozen by returning a Proxy:

function createLazyObject(getter) {
  let isInitialized = false;
  let value;

  const proxy = new Proxy({}, {
    get(target, prop) {
      if (!isInitialized && prop === "computed") {
        value = getter();
        isInitialized = true;
        Object.defineProperty(proxy, "computed", {
          value,
          writable: false,
          configurable: false
        });
      }
      return Reflect.get(target, prop);
    },
    set(target, prop, value) {
      if (isInitialized)
        throw new TypeError("Cannot set property on a frozen object");
      return Reflect.set(target, prop, value);
    },
    deleteProperty(target, prop) {
      if (isInitialized)
        throw new TypeError("Cannot delete property on a frozen object");
      return Reflect.deleteProperty(target, prop);
    },
    defineProperty(target, prop, descriptor) {
      if (isInitialized)
        throw new TypeError("Cannot define property on a frozen object");
      return Reflect.defineProperty(target, prop, descriptor);
    }
  });

  return proxy;
}

This latter approach is pretty heavy to support the functionality