Deep-immutable set - "lenses"?

Is there an existing proposal for "lenses"? Basically something that will solve this problem (it being very inconvenient to change a nested object property without mutating any data):

const employee = {
  name: 'john',
  company: {
    name: 'awesome inc',
    address: {
      city: 'london',
      street: {
        num: 23,
        name: 'high street'
      }
    }
  }
}

const capitalize = (s) => s.substring(0, 1).toUpperCase() + s.substring(1)

const employeeCapitalized = {
  ...employee,
  company: {
    ...employee.company,
    address: {
      ...employee.company.address,
      street: {
        ...employee.company.address.street,
        name: capitalize(employee.company.address.street.name)
      }
    }
  }
}

There are libraries that help with this like monocle-ts, but adding a dependency for such a basic thing is often not better than putting up with the inconvenience/ugliness. This can lead to mutable code, or hacks like JSON.parse(JSON.stringify(obj)).

Clojure's assoc-in is probably a useful starting point since it's part of the standard library.

This feels like the sort of thing that would already have a proposal, but I wasn't able to find a topic here. Does anyone know of something that exists?

This may be of interest to you: GitHub - tc39/proposal-deep-path-properties-for-record: ECMAScript proposal for deep spread syntax for Records

For your example it would look like this:

const employeeCapitalized = {
  ...employee,
  company.address.street.name: capitalize(employee.company.address.street.name)
}
2 Likes

I've also added a new issue to that proposal: syntax/support for dynamic paths ยท Issue #17 ยท tc39/proposal-deep-path-properties-for-record ยท GitHub

Wondering if there could be syntax for 'lenses':

let path = ['prop1', 'prop2', 0];
let val = record[...path];
// equivalent to:
let val = record['prop1']['prop2'][0];