Yes, we can all use N variables in the middle of a chain, but `any.thing().that.queries().rows` is a more common/natural pattern.
Like in everything in JS, developers need to know what they are doing. Every operator can create bugs (i.e. NaN results or bad string concatenations instead of sums) so I'm not sold on developers being confused: they either understand the code they are writing, or they'll have code reviews from their peers that hopeful understand the code. If nobody understands the code, then the code should be changed (as in: nobody needs to use an operator they don't get). As summary, I see more advantages than disadvantages, but I've created a module to solve the same issue to let developers play with it and see if this might be an interesting extra. I am not planning to keep stating I don't find it confusing though, and if everyone else feels like it's confusing, I can live with it and move on without the mouse trap. Regards On Mon, Sep 9, 2019 at 12:02 PM Naveen Chawla <[email protected]> wrote: > I wasn't comparing it to your `??` variant of the same cases, which has > the same issues - but only because that is a curious way of using `??` in > the first place. Ordinarily `??` would be used to resolve to a value *of > the same type* to allow clear unambiguous data flow through the variables. > > Rather, I'm comparing it to > > const > resultsContainer = db.get(SQL), > firstResultIfExists = rows?.[0] > ; > > This allows re-use of the "resultsContainer" (if desired) without creating > ambiguity about each variable's meaning. Otherwise if shoehorned into a > single variable and a developer accidentally makes a wrong presumption > about the "type" without performing an explicit "type check" (of some kind) > on the resulting variable, the function would just crash. I know JavaScript > already allows this via ?? as it's a dynamic language, but the <? operator > pretty much encourages it. > > The "destructuring" example produces a buggy "rowsCount". It can only ever > be 0. However, another developer may be tempted to say `else if(rowsCount > > 1)` not realizing that it's undefined if there are actually any rows. This > is what I mean by confusing & bug prone results of the pattern. > > > On Mon, 9 Sep 2019 at 10:18, Andrea Giammarchi < > [email protected]> wrote: > >> I guess we have a different opinion about what's confusing and what's >> not. To me having a `??` with potential side-effects is more bug prone than >> the proposed mouse trap, as it's subtle, yet "promoted" by the `?.` + `??` >> pattern. >> >> On Mon, Sep 9, 2019 at 11:16 AM Naveen Chawla <[email protected]> >> wrote: >> >>> "resultsContainerOrSingleResult" appears to be the end variable. I just >>> find this "shoehorning" to be a sacrifice in code clarity and >>> manageability. "rowCount" would be undefined if greater than 0 in the 2nd >>> example, it seems. Surely that is a confusing behaviour, if not bug prone >>> >>> On Mon, 9 Sep 2019, 09:17 Andrea Giammarchi, < >>> [email protected]> wrote: >>> >>>> so *maybe* we'll come back... >>>> >>>> On Mon, Sep 9, 2019 at 10:04 AM Andrea Giammarchi < >>>> [email protected]> wrote: >>>> >>>>> `require("module")<?.default` is the easiest use case for this, as >>>>> initially explained. >>>>> >>>>> `db.get(SQL)<?.rows?.[0]` the most common use case, for queries you >>>>> know that won't fail but might not return the desired result, so that you >>>>> end up holding the top most object with all the informations, instead of >>>>> simply ending up with undefined. This works well with destructuring too. >>>>> >>>>> ```js >>>>> const {rowsCount, id, name, email} = db.get(SQL)<?.rows?.[0]; >>>>> if (rowCounts === 0) >>>>> askUserToRegister(); >>>>> else >>>>> showUserDetails(); >>>>> ``` >>>>> >>>>> As mentioned, there is a module that let you explicitly use this >>>>> operator through a callback that tries to be as safe as it can (void after >>>>> first `.trap` access + self clean on next microtask), so manye we'll come >>>>> back to this discussion once we all understand the use case and why it's >>>>> actually very useful in some circumstance. >>>>> >>>>> Regards >>>>> >>>>> >>>>> >>>>> On Sat, Sep 7, 2019 at 1:23 PM Naveen Chawla <[email protected]> >>>>> wrote: >>>>> >>>>>> There has to be a better pattern than returning the "foo()" if the >>>>>> baz property doesn't exist. >>>>>> >>>>>> I'm curious what you would want to do with the resulting "foo()" >>>>>> anyway. I can imagine a flow where I want "bar", and it doesn't exist it >>>>>> doesn't. I cannot imagine wanting the "foo()" in place of it. There is >>>>>> type >>>>>> unpredictability in the result, so subsequent operations would normally >>>>>> expected to be impossible without type-branching. Hence my question to >>>>>> you >>>>>> about what you would typically want to do with the "foo()" if that was >>>>>> the >>>>>> returned result. >>>>>> >>>>>> On Sat, 7 Sep 2019 at 12:08, Andrea Giammarchi < >>>>>> [email protected]> wrote: >>>>>> >>>>>>> Interesting I forgot about that, but it wouldn't cover the "trap >>>>>>> here" use case. >>>>>>> >>>>>>> foo().bar ?! what => what : what; >>>>>>> >>>>>>> I'd like to forward foo() here >>>>>>> >>>>>>> On Sat, Sep 7, 2019, 11:45 Michael Luder-Rosefield < >>>>>>> [email protected]> wrote: >>>>>>> >>>>>>>> This is getting very reminiscent of my 'forwarding ternary' >>>>>>>> operator (or whatever I called it) I suggested a couple of years ago. I >>>>>>>> believe you were involved in the discussion, Andrea...! >>>>>>>> >>>>>>>> ``` >>>>>>>> const val = foo() ?! >>>>>>>> (x) => x.bar.baz : >>>>>>>> someFallbackValue; >>>>>>>> ``` >>>>>>>> >>>>>>>> On Sat, 7 Sep 2019, 10:17 Andrea Giammarchi, < >>>>>>>> [email protected]> wrote: >>>>>>>> >>>>>>>>> To better answer, let's start dropping any direct access and put a >>>>>>>>> payload in the mix. >>>>>>>>> >>>>>>>>> As example, in the `foo()?.bar.baz` case, you might end up having >>>>>>>>> `null` or `undefined`, as result, because `foo().bar` existed, but >>>>>>>>> `bar.baz` didn't. >>>>>>>>> >>>>>>>>> In the `foo()?.bar?.baz` case, you might end up having >>>>>>>>> `foo().bar`, because `bar.baz` didn't exist. >>>>>>>>> >>>>>>>>> But what if you are not interested in the whole chain, but only in >>>>>>>>> a main specific point in such chain? In that case you would have >>>>>>>>> `foo()?.bar.baz ?? foo()`, but you wouldn't know how to obtain that >>>>>>>>> via >>>>>>>>> `foo()?.bar?.baz ?? foo()`, because the latest one might result into >>>>>>>>> `foo().bar`. >>>>>>>>> >>>>>>>>> Moreover, in both cases you'll end up multiplying the payload at >>>>>>>>> least * 2, while the mouse trap will work like this: >>>>>>>>> >>>>>>>>> ```js >>>>>>>>> foo()<?.bar?.baz >>>>>>>>> ``` >>>>>>>>> >>>>>>>>> if either `foo().bar` or `bar.baz` don't exist, the returned >>>>>>>>> result is `foo()`, and it's computed once. You don't care about >>>>>>>>> `foo().bar` >>>>>>>>> if `bar.baz` is not there, 'cause you want to retrieve `foo()` >>>>>>>>> whenever you >>>>>>>>> have a failure down the chain. >>>>>>>>> >>>>>>>>> Specially with DB operations, this is a very common case >>>>>>>>> (abstraction layers all have somehow different nested objects with >>>>>>>>> various >>>>>>>>> info) and the specific info you want to know is usually attached at >>>>>>>>> the top >>>>>>>>> level bject, while crawling its sub properties either leads to the >>>>>>>>> expected >>>>>>>>> result or you're left clueless about the result, 'cause all info got >>>>>>>>> lost >>>>>>>>> in the chain. >>>>>>>>> >>>>>>>>> The `foo()<?.bar.baz` case is a bit different though, 'cause if >>>>>>>>> `foo().bar` existed, there's no way to expect `foo()` as result, and >>>>>>>>> if >>>>>>>>> it's `bar` that you're after you can write instead `foo()?.bar<?.baz` >>>>>>>>> so >>>>>>>>> that if `baz` is not there, `bar` it is. >>>>>>>>> >>>>>>>>> This short-circuit the need for `??` in most cases, 'cause you >>>>>>>>> already point at the desired result in the chain in case the result >>>>>>>>> would >>>>>>>>> be `null` or `undefined`. >>>>>>>>> >>>>>>>>> However, `??` itself doesn't provide any ability to reach any >>>>>>>>> point in the previous chain that failed, so that once again, you find >>>>>>>>> yourself crawling such chain as fallback, resulting potentially in >>>>>>>>> multiple >>>>>>>>> chains and repeated payloads. >>>>>>>>> >>>>>>>>> ```js >>>>>>>>> // nested chains >>>>>>>>> foo()?.bar.baz?.biz ?? foo()?.bar.baz ?? foo()?.bar; >>>>>>>>> >>>>>>>>> // mouse trap >>>>>>>>> foo()?.bar<?.baz?.biz; >>>>>>>>> ``` >>>>>>>>> >>>>>>>>> Above example would prefer `foo().bar` if it exists, and if either >>>>>>>>> `bar.baz` or `bar.baz.biz` returned `null` or `undefined`. >>>>>>>>> >>>>>>>>> I hope this clarifies further the intent, or the simplification, >>>>>>>>> that such operator offers: it's a complementary hint for any optional >>>>>>>>> chain, it doesn't have to be used, but when it does, it's visually >>>>>>>>> semantic >>>>>>>>> in its intent (at least to my eyes). >>>>>>>>> >>>>>>>>> Regards >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> On Fri, Sep 6, 2019 at 11:20 PM Tab Atkins Jr. < >>>>>>>>> [email protected]> wrote: >>>>>>>>> >>>>>>>>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>>>>>>>> <[email protected]> wrote: >>>>>>>>>> > Indeed I'm not super convinced myself about the "branching >>>>>>>>>> issue" 'cause `const result = this?.is?.branching?.already` and all >>>>>>>>>> I am >>>>>>>>>> proposing is to hint the syntax where to stop in case something else >>>>>>>>>> fails >>>>>>>>>> down the line, as in `const result = this.?.is<?.branching?.too` to >>>>>>>>>> know >>>>>>>>>> that if any other part is not reached, there is a certain point to >>>>>>>>>> keep >>>>>>>>>> going (which is, example, checking that `result !== this`) >>>>>>>>>> >>>>>>>>>> Important distinction there is that ?. only "branches" between the >>>>>>>>>> intended type and undefined, not between two arbitrary types. The >>>>>>>>>> cognitive load between those two is significantly different. >>>>>>>>>> >>>>>>>>>> In particular, you can't *do* anything with undefined, so >>>>>>>>>> `foo?.bar.baz` has pretty unambiguous semantics - you don't think >>>>>>>>>> you >>>>>>>>>> might be accessing the .baz property of undefined, because that >>>>>>>>>> clearly doesn't exist. >>>>>>>>>> >>>>>>>>>> That's not the case with mouse, where it's not clear, at least to >>>>>>>>>> me, >>>>>>>>>> whether `foo<?.bar.baz` is doing `(foo.bar ? foo.bar : foo).baz` >>>>>>>>>> or >>>>>>>>>> `foo.bar.baz ? foo.bar.baz : foo` or even `foo.bar ? foo.bar.baz : >>>>>>>>>> foo`. All three seem at least somewhat reasonable, and definitely >>>>>>>>>> *believable* as an interpretation! >>>>>>>>>> >>>>>>>>>> ~TJ >>>>>>>>>> >>>>>>>>> _______________________________________________ >>>>>>>>> es-discuss mailing list >>>>>>>>> [email protected] >>>>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>>>> >>>>>>>> _______________________________________________ >>>>>>> es-discuss mailing list >>>>>>> [email protected] >>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>> >>>>>>
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

