# Symbol.lazy

**URL:** <https://es.discourse.group/t/symbol-lazy/1534>\
**Category:** 💡 Ideas\
**Created:** [November 25, 2022, 2:54pm UTC](https://es.discourse.group/t/symbol-lazy/1534 "2022-11-25T14:54:42Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [November 25, 2022, 2:54pm UTC](https://es.discourse.group/t/symbol-lazy/1534/1 "2022-11-25T14:54:42Z")

</div>

The idea is to allow certain APIs to take arguments whose type is:

```nohighlight
value | { [Symbol.lazy](): value }

```

This would allow APIs that take default values to ensure that the values are not constructed if they are not needed.

For example you could have:

```nohighlight
Map.getOr(key, {[Symbol.lazy]() { return new ExpensiveClass } })

```

---

<div class="post-metadata">

**Author:** ![claudepache](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudepache/32/236_2.png) [@claudepache](https://es.discourse.group/u/claudepache)\
**Post date:** [November 25, 2022, 4:44pm UTC](https://es.discourse.group/t/symbol-lazy/1534/2 "2022-11-25T16:44:32Z")

</div>

The problem is interesting, but the proposed solution is not very intuitive. The following is already more readable:

```javascript
map.getOr(key, LazyValue(() => new ExpensiveClass))

```

But the API can also be designed to take a callback that returns a value, instead of a value, when it is not sure that it will use that value:

```javascript
map.getOr(key, new ExpensiveClass) // TypeError, 2nd arg must be a callback
map.getOr(key, () => new ExpensiveClass) // ok

```

---

<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:** [November 25, 2022, 4:59pm UTC](https://es.discourse.group/t/symbol-lazy/1534/3 "2022-11-25T16:59:30Z")

</div>

It also gets tricky if you want, e.g., the fallback parameter for map.getOr() to be a lazy value (however that gets defined). E.g.

SomeOtherFnThatAcceptsLazyValues(map.getOr(key, ))

Where is an object with a symbol or the result of calling LazyValue()

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [November 25, 2022, 5:01pm UTC](https://es.discourse.group/t/symbol-lazy/1534/4 "2022-11-25T17:01:53Z")

</div>

I'm not wedded to any particular solution, I was just spitballing. I chose `Symbol.lazy` for its similarity to `Symbol.iterator`, since I see lazy as sort of iterator-but-with-only-one-value.

~~It needs to be nice to use though since there will be no way to encapsulate laziness inside a function.~~

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [November 25, 2022, 5:07pm UTC](https://es.discourse.group/t/symbol-lazy/1534/5 "2022-11-25T17:07:38Z")

</div>

@claudepache you mention:

```nohighlight
map.getOr(key, LazyValue(() => new ExpensiveClass))

```

I'd think that in order for this to work you'd have to have

```nohighlight
const LazyValue = fn => ({ [Symbol.lazy]: fn })

```

`LazyValue` can't call `fn` or else it wouldn't be lazy!

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [November 26, 2022, 3:17am UTC](https://es.discourse.group/t/symbol-lazy/1534/6 "2022-11-26T03:17:12Z")

</div>

@theScottyJam It's a little odd, but I think you'd just write `lazy(() => lazy(() => value))`. There's nothing recursive going on so nesting works fine.

The complete example would then be:

```nohighlight
acceptsLazy(lazy(() => map.getOr(key, lazy(computeExpensiveDefault))));

```

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [November 26, 2022, 3:47am UTC](https://es.discourse.group/t/symbol-lazy/1534/7 "2022-11-26T03:47:35Z")

</div>

I ran [some v8 microbenchmarks](https://gist.github.com/conartist6/11e2ba3539699147970bc41fa500e2d7) on common empty constructors (x1,000,000):

```nohighlight
new Set: 18ms
new Map: 21ms
[]: 1ms
() => {}: 17ms
(() => new Map)(): 24ms

```

The results indicate that `map.getOr(key, new Set())` is just as fast as `map.getOr(key, () => new Set())`, which to me means that the improved readability of being able to omit the arrow function can often be realized. Significant perf improvements are even achieved when the map values are primitives or arrays!

---

<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:** [November 26, 2022, 3:34pm UTC](https://es.discourse.group/t/symbol-lazy/1534/8 "2022-11-26T15:34:38Z")

</div>

Yeah, that would work.

I guess my main worry, is that it's nice to have simple APIs that can operate with whatever value you give it, and it just works. You don't have special-case behaviors for special values.

It's one thing I dislike about React's useState() hook, which looks like this:

```javascript
const [state, setState] = useState(...)

```

`useState()` returns a tuple containing the current state, and a function to update the current state (the `setState()` function). You can call `setState()` with whatever value to make that the new current state.

With one exception. If you call it with a function, instead of setting the current state as a function, it'll call your function, passing in the old state and expecting you to return a transformed state. This is useful and important behavior, but it's also really annoying that they're overloading the same `useState()` function to provide it. What if, I want to actually store a function as state, or, I want to store a user-supplied value (I don't care what it is, could be a function, or anything else) as state? This has happened to me before. Well, you can't, because of how this function is set up. You instead have to put the function into a single-property object and store that as the state.

The root of this problem is that they tried to smartly overload a single function to have two different behaviors depending on what was passed in. I worry that creating a "lazy function" will be encouraging many APIs to make this same mistake, where they overload their functions to have two different behaviors depending on what was passed in, and now you can't just use those APIs with whatever random user-supplied value that you received, you have to be cautious because it's possible that user-supplied value is a lazy function, which might trigger special behaviors in the function you're trying to use, and you might not want those special behaviors to be triggered, but there's no good way to opt-out of that.

e.g. I can't just call `map.getOr()` with some random user-supplied value I received as input, unless I'm ok with `map.getOr()`'s default treatment of how it handles lazy functions, which it's possible that that's not how I want it to behave.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [November 27, 2022, 4:09am UTC](https://es.discourse.group/t/symbol-lazy/1534/9 "2022-11-27T04:09:39Z")

</div>

Yeah that's quite fair really. It feels esoteric issue right now because no lazys yet exist and I'm spitballing purely in syntactic terms, but I think you're right to say that it may become a real issue.

An alternate design which does not suffer from that issue would be adding four methods instead of two:

```nohighlight
Map.prototype.getDefault(key, defaultValue);
Map.prototype.getLazyDefault(key, defaultValueFactory);
Map.prototype.setDefault(key, defaultValue);
Map.prototype.setLazyDefault(key, defaultValueFactory);

```
