# Overloadable data accesor operator

**URL:** <https://es.discourse.group/t/overloadable-data-accesor-operator/662>\
**Category:** 💡 Ideas\
**Created:** [February 24, 2021, 3:51am UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662 "2021-02-24T03:51:20Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 24, 2021, 3:51am UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/1 "2021-02-24T03:51:20Z")

</div>

As you know, `obj['key']` is used to retrieve a property that's literally on the object (unless you're using proxy magic, getters, etc).  
The proposed `obj@['key']` operator doesn't do anything unless it is overloaded, but can be overloaded using two new, well-known symbols: Symbol.getDataValue and Symbol.setDataValue. The usefulness of an overloadable data-accessor operator will become clear with some example use cases.

If we set `Map.prototype[Symbol.getDataValue]` to `Map.prototype.get`, and set `Map.prototype[Symbol.setDataValue]` to `Map.prototype.set`, then the following would become possible:

```javascript
m = new Map([['x', 2]])
m@['x'] ??= 5
m@['y'] ??= 5
console.log(m) // Map(2) { 'x' => 2, 'y' => 5 }

```

The "Array.prototype.at" proposal provides a convinient way to index into arrays with negative inidices. With the data accessor operator, we could provide similar functionality:

```javascript
items = [5, 6, 7]
items@[-1]++
console.log(items@[2]) // 8
items@[-2] = 0
console.log(items) // [5, 0, 8]

```

I presume URLSearchParams is not part of ECMAScript, but whatever comittee that has juristiction over it could also take advantage of the data accessor operator:

```javascript
const params = new URLSearchParams('x=2&y=3')
params@['x'] = '5'
console.log(params.toString()) // x=5&y=3

```

We could potentially provide a default data-access behavior for objects (by defining the new well-known symbols on Object.prototype), The default behavior can be defined similar to normal propecty access, except it only accesses own properties.

```javascript
const untrustedKey = 'toString'
const mapping = { x: 1, y: 2 }

console.log(mapping[untrustedKey]) // [Function: toString]
console.log(mapping@[untrustedKey]) // undefined

```

# Implementation Details

Here's an example of how one would go about creating their own overload for this operator, along with some examples of when these operators get called:

```javascript
const customObj = {
  [Symbol.getDataValue](key) {
    console.log('GET', key)
    return 5
  },
  [Symbol.setDataValue](key, value) {
    console.log('SET', key, value)
  },
}

customObj@['x'] // GET x
customObj@['x'] = 'NEW_VALUE' // SET x NEW_VALUE
customObj@['x']++ // "GET x" then "SET x 6"

```

# Suggestions?

I'd love to hear any feedback on this idea, especially on these points:

- I'm certainly not set on the `obj@[key]` syntax - I just picked something random for this. If anyone has any other ideas for syntax that they would prefer, please share!
- Let me know if you can think of other use cases for such an operator, I'm sure there's plenty more out there that I haven't thought of.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [February 24, 2021, 10:11am UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/2 "2021-02-24T10:11:30Z")

</div>

Looking at [GitHub - tc39/proposal-decorators: Decorators for ES6 classes](https://github.com/tc39/proposal-decorators) it looks like this wouldn't clash with the `@` uses there because square brackets are not allowed around decorators. So that's good.

In terms of number of characters to type. If `Map` had a single character alias for `get` it would only be one more character than adding new syntax.

```javascript
Map.prototype.g = Map.prototype.get;

map@['x']
map.g('x')

```

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 24, 2021, 3:35pm UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/3 "2021-02-24T15:35:23Z")

</div>

I assume you meant to use parentheses, not square brackets, when calling your shorthand get?

I agree that for the simple cases of getting or setting a value, this operator wouldn't add too much value for Map (except that the operator is easier to remember then the function names). Where it really shines is with self assignment:

```javascript
map = new Map()

map@['myKey'] ??= 5
// vs
map.set('myKey', map.get('myKey') ?? 5)

map@[computeKey()]++
// vs
const computedKey = computeKey()
map.set(computedKey, map.get(computedKey) + 1)

```

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [February 24, 2021, 8:38pm UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/4 "2021-02-24T20:38:44Z")

</div>

> [@theScottyJam](#):
>
> I assume you meant to use parentheses, not square brackets, when calling your shorthand get?

yep 🤦🏻‍♂️ (I've edited it now)

> [@theScottyJam](#):
>
> Where it really shines is with self assignment:

Those examples are quite compelling. Worth noting the existence of [GitHub - tc39/proposal-upsert: ECMAScript Proposal, specs, and reference implementation for Map.prototype.upsert](https://github.com/tc39/proposal-upsert)

```javascript
map@['myKey'] ??= 5;
map.emplace({ insert: () => 5 });

map@[computeKey()]++
map.emplace({ update: v => v + 1 });

```

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/senocular/32/642_2.png) [@senocular](https://es.discourse.group/u/senocular)\
**Post date:** [February 24, 2021, 9:19pm UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/5 "2021-02-24T21:19:23Z")

</div>

Just mixing @aclaymore's first example with some of that proxy magic which gets you pretty close:

```javascript
m = new Map([['x', 2]])
m.g = new Proxy(m, {
    get (target, prop) {
        return target.get(prop)
    },
    set (target, prop, value) {
        target.set(prop, value)
    }
})
m.g['x'] ??= 5
m.g['y'] ??= 5
console.log(m) // Map(2) { 'x' => 2, 'y' => 5 }
m.g['x']++
console.log(m) // Map(2) { 'x' => 3, 'y' => 5 }

```

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [February 24, 2021, 9:47pm UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/6 "2021-02-24T21:47:42Z")

</div>

Very cool! Only downside is that this only works for string keys, and one of the main advantages of `Map` over objects is not having this restriction. Still cool though!

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/senocular/32/642_2.png) [@senocular](https://es.discourse.group/u/senocular)\
**Post date:** [February 25, 2021, 1:07am UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/7 "2021-02-25T01:07:54Z")

</div>

> [@aclaymore](#):
>
> Only downside is that this only works for string keys...

Even given the usage of Map I wasn't even considering that aspect of the proposal. Support for property key values other than strings and symbols would be compelling.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 27, 2021, 2:29am UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/8 "2021-02-27T02:29:25Z")

</div>

Some more use cases:

We could potentially allow this syntax to be used within object destructuring:

```javascript
const map = new Map([['key1', 1], ['key2', 2]])
const { @['key1']: value1, @['key2']: value2 } = map

const items = ['a', 'b', 'c', 'd']
const { 0: firstItem, @[-1]: lastItem } = items // This example mixes the data-accessor operator with normal attribute accessing.

```

We could also implement the data accessor operator on strings, to support negative indexing, like with arrays. We could potentially make this UTF-16-aware, so it doesn't split surrogate pairs, but I'm leaning away from that idea as I understand this to be a O(n) operation.

```javascript
console.log('abcde'@[-1]) // e

```

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [March 2, 2021, 12:43pm UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/9 "2021-03-02T12:43:07Z")

</div>

The last one you could just do `'abcde'.slice(-1)` if it's not Unicode-aware, and Unicode-aware or not, it'd be roughly the same complexity as `string[string.length - 1]`.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [July 21, 2021, 3:49am UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/10 "2021-07-21T03:49:34Z")

</div>

I've figured out a solution that I like better than this overloadable data accessor operator (mostly because it doesn't require new syntax). The idea is to add a `.getRef(key)` function to Map (or any other data structure that wants to add it). The returned value is an object, with a value property that's a getter/setter, which will automatically fetch/set map values.

Example:

```javascript
// Default assign
map.getRef(key).value ??= value

// Increment
counts.getRef(key).value++

```

I proposed this idea on the Map.prototype.emplace proposal [here](https://github.com/tc39/proposal-upsert/issues/39), along with some additional details.

---

<div class="post-metadata">

**Author:** ![graphemecluster](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/graphemecluster/32/957_2.png) [@graphemecluster](https://es.discourse.group/u/graphemecluster)\
**Post date:** [October 29, 2021, 6:26pm UTC](https://es.discourse.group/t/overloadable-data-accesor-operator/662/11 "2021-10-29T18:26:10Z")

</div>

I would suggest using “`@.`” – this allows shorthand like “`map@.x`”. (“`map@x`” will be better though, if there’s no ambiguity with decorators.)  
And I think a single symbol would be enough as some logic may be the same in both get and set. Also someone may do different things like optimizations depending on the key:

```javascript
[Symbol.dataValue](key) {
	if (typeof key === "symbol") throw new TypeError(/* … */);
	return isOptimizable(key) ? {
		get: () => { /* … */ }
		set: value => { /* … */ }
	} : {
		get: () => { /* … */ }
		set: value => { /* … */ }
	};
}

```

It will definitely be great if the same can be done to a reference like `map.getRef("key") = value`.

Btw I remember that the `array[^1]` syntax has been being proposed but I can’t find the link. (Not the Stage 4 `.at` method but a brand new syntax)
