Optional chaining syntax but with the "mice" operator ?
On Thu, Sep 5, 2019 at 2:39 PM Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
This is basically a solution to a common problem we have these days, where modules published in the wild might have a
default
property, to support ESM logic, or not.
// current optional chaining logic const imported = exported?.default ?? exported; // my "mice operator" proposal const imported = exported<?.default;
Semantically speaking, not only
<?
actually looks like a mice, it also points at its previous value in case the chaining didn't work.Beside the basic example, the "mice operator" might save CPU cycles when it comes to involving more complex expressions, i.e.
// current "solution" const thing = require('thing')?.default ?? require('thing'); // mice operator const thing = require('thing')<?.default;
This is also easily tranpilable, so kinda a no-brainer for modern dev tools to bring in.
TL;DR specially for cases where an accessed property should fallback to its source, this operator might save both typing and CPU time whenever it's needed.
I find it a rather curious pattern, that I'd never seen before! Is it used in anything besides this ESM-compat thing you're talking about?
(Saving CPU cycles is not a convincing argument; it's trivial to write such a line over two declarations and avoid any expensive recomputations.)
On Thu, Sep 5, 2019 at 2:39 PM Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote: > > This is basically a solution to a common problem we have these days, where modules published in the wild might have a `default` property, to support ESM logic, or not. > > ```js > // current optional chaining logic > const imported = exported?.default ?? exported; > > // my "mice operator" proposal > const imported = exported<?.default; > ``` > > Semantically speaking, not only `<?` actually looks like a mice, it also points at its previous value in case the chaining didn't work. > > Beside the basic example, the "mice operator" might save CPU cycles when it comes to involving more complex expressions, i.e. > > ```js > // current "solution" > const thing = require('thing')?.default ?? require('thing'); > > // mice operator > const thing = require('thing')<?.default; > ``` > > This is also easily tranpilable, so kinda a no-brainer for modern dev tools to bring in. > > TL;DR specially for cases where an accessed property should fallback to its source, this operator might save both typing and CPU time whenever it's needed. I find it a rather curious pattern, that I'd never seen before! Is it used in anything besides this ESM-compat thing you're talking about? (Saving CPU cycles is not a convincing argument; it's trivial to write such a line over two declarations and avoid any expensive recomputations.) ~TJ
Another use case that I believe will be common is the following one:
// current state of the art
const result = dbQuery(data)?.rows ?? 'did it just failed or what?';
// VS the "mice operator"
const result = dbQuery(data)<?.rows;
// if it was rows
if (Array.isArray(result))
console.log(result);
else if (result instanceof Error)
console.error(result.message);
else
console.warn(`unexpected result: ${result}`);
Ideally, the "mice" should grant chaining up to its latest presence, but I wouldn't know right now how to reference to it ...
// if no ?? is needed, this might work
const result = dbQuery(data)<?.rows?.data?.entry;
// if ?? is needed, no idea how to back-reference the latest successfull
"mice" result
Another use case that I believe will be common is the following one: ```js // current state of the art const result = dbQuery(data)?.rows ?? 'did it just failed or what?'; // VS the "mice operator" const result = dbQuery(data)<?.rows; // if it was rows if (Array.isArray(result)) console.log(result); else if (result instanceof Error) console.error(result.message); else console.warn(`unexpected result: ${result}`); ``` Ideally, the "mice" should grant chaining up to its latest presence, but I wouldn't know right now how to reference to it ... ```js // if no ?? is needed, this might work const result = dbQuery(data)<?.rows?.data?.entry; // if ?? is needed, no idea how to back-reference the latest successfull "mice" result ``` On Thu, Sep 5, 2019 at 11:44 PM Tab Atkins Jr. <jackalmage at gmail.com> wrote: > On Thu, Sep 5, 2019 at 2:39 PM Andrea Giammarchi > <andrea.giammarchi at gmail.com> wrote: > > > > This is basically a solution to a common problem we have these days, > where modules published in the wild might have a `default` property, to > support ESM logic, or not. > > > > ```js > > // current optional chaining logic > > const imported = exported?.default ?? exported; > > > > // my "mice operator" proposal > > const imported = exported<?.default; > > ``` > > > > Semantically speaking, not only `<?` actually looks like a mice, it also > points at its previous value in case the chaining didn't work. > > > > Beside the basic example, the "mice operator" might save CPU cycles when > it comes to involving more complex expressions, i.e. > > > > ```js > > // current "solution" > > const thing = require('thing')?.default ?? require('thing'); > > > > // mice operator > > const thing = require('thing')<?.default; > > ``` > > > > This is also easily tranpilable, so kinda a no-brainer for modern dev > tools to bring in. > > > > TL;DR specially for cases where an accessed property should fallback to > its source, this operator might save both typing and CPU time whenever it's > needed. > > I find it a rather curious pattern, that I'd never seen before! Is it > used in anything besides this ESM-compat thing you're talking about? > > (Saving CPU cycles is not a convincing argument; it's trivial to write > such a line over two declarations and avoid any expensive > recomputations.) > > ~TJ > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/2b885a93/attachment.html>
Another pattern it could be useful in is with, say, nosql dbs where something might be an object or id reference:
const fooId = foo<?.id;
Another pattern it could be useful in is with, say, nosql dbs where something might be an object or id reference: ``` const fooId = foo<?.id; ``` On Thu, 5 Sep 2019, 23:03 Andrea Giammarchi, <andrea.giammarchi at gmail.com> wrote: > Another use case that I believe will be common is the following one: > > ```js > // current state of the art > const result = dbQuery(data)?.rows ?? 'did it just failed or what?'; > > // VS the "mice operator" > const result = dbQuery(data)<?.rows; > > // if it was rows > if (Array.isArray(result)) > console.log(result); > else if (result instanceof Error) > console.error(result.message); > else > console.warn(`unexpected result: ${result}`); > ``` > > Ideally, the "mice" should grant chaining up to its latest presence, but I > wouldn't know right now how to reference to it ... > > ```js > // if no ?? is needed, this might work > const result = dbQuery(data)<?.rows?.data?.entry; > > // if ?? is needed, no idea how to back-reference the latest successfull > "mice" result > ``` > > > > > On Thu, Sep 5, 2019 at 11:44 PM Tab Atkins Jr. <jackalmage at gmail.com> > wrote: > >> On Thu, Sep 5, 2019 at 2:39 PM Andrea Giammarchi >> <andrea.giammarchi at gmail.com> wrote: >> > >> > This is basically a solution to a common problem we have these days, >> where modules published in the wild might have a `default` property, to >> support ESM logic, or not. >> > >> > ```js >> > // current optional chaining logic >> > const imported = exported?.default ?? exported; >> > >> > // my "mice operator" proposal >> > const imported = exported<?.default; >> > ``` >> > >> > Semantically speaking, not only `<?` actually looks like a mice, it >> also points at its previous value in case the chaining didn't work. >> > >> > Beside the basic example, the "mice operator" might save CPU cycles >> when it comes to involving more complex expressions, i.e. >> > >> > ```js >> > // current "solution" >> > const thing = require('thing')?.default ?? require('thing'); >> > >> > // mice operator >> > const thing = require('thing')<?.default; >> > ``` >> > >> > This is also easily tranpilable, so kinda a no-brainer for modern dev >> tools to bring in. >> > >> > TL;DR specially for cases where an accessed property should fallback to >> its source, this operator might save both typing and CPU time whenever it's >> needed. >> >> I find it a rather curious pattern, that I'd never seen before! Is it >> used in anything besides this ESM-compat thing you're talking about? >> >> (Saving CPU cycles is not a convincing argument; it's trivial to write >> such a line over two declarations and avoid any expensive >> recomputations.) >> >> ~TJ >> > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190905/bd207839/attachment-0001.html>
absolutely, I'm working with PostgreSQL these days and indeed for any promise/awaited result this pattern looks like a win, and while it's targeting a limitation of the chaining one, it can be used in various other cases where knowing the initial result is more important than just falling back to "dunnoWhatHappenedThere"
absolutely, I'm working with PostgreSQL these days and indeed for any promise/awaited result this pattern looks like a win, and while it's targeting a limitation of the chaining one, it can be used in various other cases where knowing the initial result is more important than just falling back to "_dunnoWhatHappenedThere_" On Fri, Sep 6, 2019 at 12:24 AM Michael Luder-Rosefield < rosyatrandom at gmail.com> wrote: > Another pattern it could be useful in is with, say, nosql dbs where > something might be an object or id reference: > > ``` > const fooId = foo<?.id; > ``` > > On Thu, 5 Sep 2019, 23:03 Andrea Giammarchi, <andrea.giammarchi at gmail.com> > wrote: > >> Another use case that I believe will be common is the following one: >> >> ```js >> // current state of the art >> const result = dbQuery(data)?.rows ?? 'did it just failed or what?'; >> >> // VS the "mice operator" >> const result = dbQuery(data)<?.rows; >> >> // if it was rows >> if (Array.isArray(result)) >> console.log(result); >> else if (result instanceof Error) >> console.error(result.message); >> else >> console.warn(`unexpected result: ${result}`); >> ``` >> >> Ideally, the "mice" should grant chaining up to its latest presence, but >> I wouldn't know right now how to reference to it ... >> >> ```js >> // if no ?? is needed, this might work >> const result = dbQuery(data)<?.rows?.data?.entry; >> >> // if ?? is needed, no idea how to back-reference the latest successfull >> "mice" result >> ``` >> >> >> >> >> On Thu, Sep 5, 2019 at 11:44 PM Tab Atkins Jr. <jackalmage at gmail.com> >> wrote: >> >>> On Thu, Sep 5, 2019 at 2:39 PM Andrea Giammarchi >>> <andrea.giammarchi at gmail.com> wrote: >>> > >>> > This is basically a solution to a common problem we have these days, >>> where modules published in the wild might have a `default` property, to >>> support ESM logic, or not. >>> > >>> > ```js >>> > // current optional chaining logic >>> > const imported = exported?.default ?? exported; >>> > >>> > // my "mice operator" proposal >>> > const imported = exported<?.default; >>> > ``` >>> > >>> > Semantically speaking, not only `<?` actually looks like a mice, it >>> also points at its previous value in case the chaining didn't work. >>> > >>> > Beside the basic example, the "mice operator" might save CPU cycles >>> when it comes to involving more complex expressions, i.e. >>> > >>> > ```js >>> > // current "solution" >>> > const thing = require('thing')?.default ?? require('thing'); >>> > >>> > // mice operator >>> > const thing = require('thing')<?.default; >>> > ``` >>> > >>> > This is also easily tranpilable, so kinda a no-brainer for modern dev >>> tools to bring in. >>> > >>> > TL;DR specially for cases where an accessed property should fallback >>> to its source, this operator might save both typing and CPU time whenever >>> it's needed. >>> >>> I find it a rather curious pattern, that I'd never seen before! Is it >>> used in anything besides this ESM-compat thing you're talking about? >>> >>> (Saving CPU cycles is not a convincing argument; it's trivial to write >>> such a line over two declarations and avoid any expensive >>> recomputations.) >>> >>> ~TJ >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/183316d1/attachment.html>
Since somebody asked me already elsewhere about the expected precedence of the operator, this would be my answer:
const result = await dbQuery(data)<?.rows;
would be the equivalent of
let result = await dbQuery(data);
if (result != null && result.rows != null)
result = result.rows;
or to better answer about precedence:
const result = (await dbQuery(data))<?.rows;
I hope this adds some extra context to this proposal.
Since somebody asked me already elsewhere about the expected precedence of the operator, this would be my answer: ```js const result = await dbQuery(data)<?.rows; ``` would be the equivalent of ```js let result = await dbQuery(data); if (result != null && result.rows != null) result = result.rows; ``` or to better answer about precedence: ```js const result = (await dbQuery(data))<?.rows; ``` I hope this adds some extra context to this proposal. On Fri, Sep 6, 2019 at 12:28 AM Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote: > absolutely, I'm working with PostgreSQL these days and indeed for any > promise/awaited result this pattern looks like a win, and while it's > targeting a limitation of the chaining one, it can be used in various other > cases where knowing the initial result is more important than just falling > back to "_dunnoWhatHappenedThere_" > > On Fri, Sep 6, 2019 at 12:24 AM Michael Luder-Rosefield < > rosyatrandom at gmail.com> wrote: > >> Another pattern it could be useful in is with, say, nosql dbs where >> something might be an object or id reference: >> >> ``` >> const fooId = foo<?.id; >> ``` >> >> On Thu, 5 Sep 2019, 23:03 Andrea Giammarchi, <andrea.giammarchi at gmail.com> >> wrote: >> >>> Another use case that I believe will be common is the following one: >>> >>> ```js >>> // current state of the art >>> const result = dbQuery(data)?.rows ?? 'did it just failed or what?'; >>> >>> // VS the "mice operator" >>> const result = dbQuery(data)<?.rows; >>> >>> // if it was rows >>> if (Array.isArray(result)) >>> console.log(result); >>> else if (result instanceof Error) >>> console.error(result.message); >>> else >>> console.warn(`unexpected result: ${result}`); >>> ``` >>> >>> Ideally, the "mice" should grant chaining up to its latest presence, but >>> I wouldn't know right now how to reference to it ... >>> >>> ```js >>> // if no ?? is needed, this might work >>> const result = dbQuery(data)<?.rows?.data?.entry; >>> >>> // if ?? is needed, no idea how to back-reference the latest successfull >>> "mice" result >>> ``` >>> >>> >>> >>> >>> On Thu, Sep 5, 2019 at 11:44 PM Tab Atkins Jr. <jackalmage at gmail.com> >>> wrote: >>> >>>> On Thu, Sep 5, 2019 at 2:39 PM Andrea Giammarchi >>>> <andrea.giammarchi at gmail.com> wrote: >>>> > >>>> > This is basically a solution to a common problem we have these days, >>>> where modules published in the wild might have a `default` property, to >>>> support ESM logic, or not. >>>> > >>>> > ```js >>>> > // current optional chaining logic >>>> > const imported = exported?.default ?? exported; >>>> > >>>> > // my "mice operator" proposal >>>> > const imported = exported<?.default; >>>> > ``` >>>> > >>>> > Semantically speaking, not only `<?` actually looks like a mice, it >>>> also points at its previous value in case the chaining didn't work. >>>> > >>>> > Beside the basic example, the "mice operator" might save CPU cycles >>>> when it comes to involving more complex expressions, i.e. >>>> > >>>> > ```js >>>> > // current "solution" >>>> > const thing = require('thing')?.default ?? require('thing'); >>>> > >>>> > // mice operator >>>> > const thing = require('thing')<?.default; >>>> > ``` >>>> > >>>> > This is also easily tranpilable, so kinda a no-brainer for modern dev >>>> tools to bring in. >>>> > >>>> > TL;DR specially for cases where an accessed property should fallback >>>> to its source, this operator might save both typing and CPU time whenever >>>> it's needed. >>>> >>>> I find it a rather curious pattern, that I'd never seen before! Is it >>>> used in anything besides this ESM-compat thing you're talking about? >>>> >>>> (Saving CPU cycles is not a convincing argument; it's trivial to write >>>> such a line over two declarations and avoid any expensive >>>> recomputations.) >>>> >>>> ~TJ >>>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/4db5381f/attachment-0001.html>
As someone noticed already, the operator should be called eventually "mouse" operator, as mice is plural and was a misunderstand of mine 😅
As someone noticed already, the operator should be called eventually "mouse" operator, as mice is plural and was a misunderstand of mine 😅 On Fri, Sep 6, 2019, 00:54 Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote: > Since somebody asked me already elsewhere about the expected precedence of > the operator, this would be my answer: > > ```js > const result = await dbQuery(data)<?.rows; > ``` > > would be the equivalent of > > ```js > let result = await dbQuery(data); > if (result != null && result.rows != null) > result = result.rows; > ``` > > or to better answer about precedence: > > ```js > const result = (await dbQuery(data))<?.rows; > ``` > > I hope this adds some extra context to this proposal. > > > On Fri, Sep 6, 2019 at 12:28 AM Andrea Giammarchi < > andrea.giammarchi at gmail.com> wrote: > >> absolutely, I'm working with PostgreSQL these days and indeed for any >> promise/awaited result this pattern looks like a win, and while it's >> targeting a limitation of the chaining one, it can be used in various other >> cases where knowing the initial result is more important than just falling >> back to "_dunnoWhatHappenedThere_" >> >> On Fri, Sep 6, 2019 at 12:24 AM Michael Luder-Rosefield < >> rosyatrandom at gmail.com> wrote: >> >>> Another pattern it could be useful in is with, say, nosql dbs where >>> something might be an object or id reference: >>> >>> ``` >>> const fooId = foo<?.id; >>> ``` >>> >>> On Thu, 5 Sep 2019, 23:03 Andrea Giammarchi, < >>> andrea.giammarchi at gmail.com> wrote: >>> >>>> Another use case that I believe will be common is the following one: >>>> >>>> ```js >>>> // current state of the art >>>> const result = dbQuery(data)?.rows ?? 'did it just failed or what?'; >>>> >>>> // VS the "mice operator" >>>> const result = dbQuery(data)<?.rows; >>>> >>>> // if it was rows >>>> if (Array.isArray(result)) >>>> console.log(result); >>>> else if (result instanceof Error) >>>> console.error(result.message); >>>> else >>>> console.warn(`unexpected result: ${result}`); >>>> ``` >>>> >>>> Ideally, the "mice" should grant chaining up to its latest presence, >>>> but I wouldn't know right now how to reference to it ... >>>> >>>> ```js >>>> // if no ?? is needed, this might work >>>> const result = dbQuery(data)<?.rows?.data?.entry; >>>> >>>> // if ?? is needed, no idea how to back-reference the latest >>>> successfull "mice" result >>>> ``` >>>> >>>> >>>> >>>> >>>> On Thu, Sep 5, 2019 at 11:44 PM Tab Atkins Jr. <jackalmage at gmail.com> >>>> wrote: >>>> >>>>> On Thu, Sep 5, 2019 at 2:39 PM Andrea Giammarchi >>>>> <andrea.giammarchi at gmail.com> wrote: >>>>> > >>>>> > This is basically a solution to a common problem we have these days, >>>>> where modules published in the wild might have a `default` property, to >>>>> support ESM logic, or not. >>>>> > >>>>> > ```js >>>>> > // current optional chaining logic >>>>> > const imported = exported?.default ?? exported; >>>>> > >>>>> > // my "mice operator" proposal >>>>> > const imported = exported<?.default; >>>>> > ``` >>>>> > >>>>> > Semantically speaking, not only `<?` actually looks like a mice, it >>>>> also points at its previous value in case the chaining didn't work. >>>>> > >>>>> > Beside the basic example, the "mice operator" might save CPU cycles >>>>> when it comes to involving more complex expressions, i.e. >>>>> > >>>>> > ```js >>>>> > // current "solution" >>>>> > const thing = require('thing')?.default ?? require('thing'); >>>>> > >>>>> > // mice operator >>>>> > const thing = require('thing')<?.default; >>>>> > ``` >>>>> > >>>>> > This is also easily tranpilable, so kinda a no-brainer for modern >>>>> dev tools to bring in. >>>>> > >>>>> > TL;DR specially for cases where an accessed property should fallback >>>>> to its source, this operator might save both typing and CPU time whenever >>>>> it's needed. >>>>> >>>>> I find it a rather curious pattern, that I'd never seen before! Is it >>>>> used in anything besides this ESM-compat thing you're talking about? >>>>> >>>>> (Saving CPU cycles is not a convincing argument; it's trivial to write >>>>> such a line over two declarations and avoid any expensive >>>>> recomputations.) >>>>> >>>>> ~TJ >>>>> >>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/e82a4507/attachment.html>
Le 5 sept. 2019 à 23:39, Andrea Giammarchi <andrea.giammarchi at gmail.com> a écrit :
This is basically a solution to a common problem we have these days, where modules published in the wild might have a
default
property, to support ESM logic, or not.
// current optional chaining logic const imported = exported?.default ?? exported; // my "mice operator" proposal const imported = exported<?.default;
The semantics of the ?.
in exported?.default
is not to check whether the default
property exists, but whether exported
evaluates to undefined or null. If the default
property is absent, exported.default
has always evaluated to undefined
, and there is no need to optional chaining operator. So that I guess you actually meant:
``js const imported = exported.default ?? exported;
> Le 5 sept. 2019 à 23:39, Andrea Giammarchi <andrea.giammarchi at gmail.com> a écrit : > > This is basically a solution to a common problem we have these days, where modules published in the wild might have a `default` property, to support ESM logic, or not. > > ```js > // current optional chaining logic > const imported = exported?.default ?? exported; > > // my "mice operator" proposal > const imported = exported<?.default; > ``` > The semantics of the `?.` in `exported?.default` is not to check whether the `default` property exists, but whether `exported` evaluates to undefined or null. If the `default` property is absent, `exported.default` has always evaluated to `undefined`, and there is no need to optional chaining operator. So that I guess you actually meant: ``js const imported = exported.default ?? exported; ``` —Claude -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/34a74e0a/attachment-0001.html>
I think introducing this operator encourages bad logic design like "instanceof", isArray etc. These are unreadable disambiguation factors in that they don't inform about which part the expression is going to the next stage in the process. Also it leads to "type branching", which tends towards more convoluted logical flow. These things, in my mind, would lead to more bugs. Hence I would tend to be against introducing it, especially in light of other proposals that I find more useful that haven't been taken even to discussion.
I think introducing this operator encourages bad logic design like "instanceof", isArray etc. These are unreadable disambiguation factors in that they don't inform about which part the expression is going to the next stage in the process. Also it leads to "type branching", which tends towards more convoluted logical flow. These things, in my mind, would lead to more bugs. Hence I would tend to be against introducing it, especially in light of other proposals that I find more useful that haven't been taken even to discussion. On Fri, 6 Sep 2019, 07:58 Claude Pache, <claude.pache at gmail.com> wrote: > > > Le 5 sept. 2019 à 23:39, Andrea Giammarchi <andrea.giammarchi at gmail.com> > a écrit : > > This is basically a solution to a common problem we have these days, where > modules published in the wild might have a `default` property, to support > ESM logic, or not. > > ```js > // current optional chaining logic > const imported = exported?.default ?? exported; > > // my "mice operator" proposal > const imported = exported<?.default; > ``` > > > > The semantics of the `?.` in `exported?.default` is not to check whether > the `default` property exists, but whether `exported` evaluates to > undefined or null. If the `default` property is absent, `exported.default` > has always evaluated to `undefined`, and there is no need to optional > chaining operator. So that I guess you actually meant: > > ``js > const imported = exported.default ?? exported; > ``` > > > —Claude > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/65d93a29/attachment.html>
The purpose is to address what chaining lacks, in terms of "stopping" at some point whenever it's available or not.
Take this example:
// the current chaining operator
const result = nmsp.some.payload()?.result ?? nmsp.some.payload();
// the mouse operator
const result = nmsp.some.payload()<?.result;
Keeping it semantic, the mouse operator is "a trap" for the chain that makes reading possible expensive parts of the chain easier. An utility to obtain the same code would look something like the following:
// the mouse utility
const mouse = trap => {
// a way to self clean right after, as it is for RegExp.$* values
// which is something impossible to obtain with regular syntax
Promise.resolve(mouse.trap = trap).then(() => delete mouse.trap);
return trap;
};
// the previous example
const result = mouse(nmsp.some.payload())?.result ?? mouse.trap;
Since there is no easy way to syntactically obtain the same with the
current ?
and ??
without repeating all steps in the right side of the
??
, I've thought this "mouse operator" would play a role to actually
avoid bugs easily introduced by repeating calls on the right hand side of
the ??
either via getters or expensive operations.
It is also possible to keep going on a chain or eventually provide feedbacks of what went wrong:
const name = await some.query(id)<?.rows?.[0]?.name;
if (typeof name !== 'string')
throw name;
As summary, considering how semantic it's the operator in both visual and
practical meanings, and considering it's not possible to achieve the same
result through ??
, I wish it would be considered as complementary help
for the recently introduced ?.
and ??
syntax.
So thanks in advance for possible consideration.
The purpose is to address what chaining lacks, in terms of "stopping" at some point whenever it's available or not. Take this example: ```js // the current chaining operator const result = nmsp.some.payload()?.result ?? nmsp.some.payload(); // the mouse operator const result = nmsp.some.payload()<?.result; ``` Keeping it semantic, the mouse operator is "a trap" for the chain that makes reading possible expensive parts of the chain easier. An utility to obtain the same code would look something like the following: ```js // the mouse utility const mouse = trap => { // a way to self clean right after, as it is for RegExp.$* values // which is something impossible to obtain with regular syntax Promise.resolve(mouse.trap = trap).then(() => delete mouse.trap); return trap; }; // the previous example const result = mouse(nmsp.some.payload())?.result ?? mouse.trap; ``` Since there is no easy way to syntactically obtain the same with the current `?` and `??` without repeating all steps in the right side of the `??`, I've thought this "mouse operator" would play a role to actually avoid bugs easily introduced by repeating calls on the right hand side of the `??` either via getters or expensive operations. It is also possible to keep going on a chain or eventually provide feedbacks of what went wrong: ```js const name = await some.query(id)<?.rows?.[0]?.name; if (typeof name !== 'string') throw name; ``` As summary, considering how semantic it's the operator in both visual and practical meanings, and considering it's not possible to achieve the same result through `??`, I wish it would be considered as complementary help for the recently introduced `?.` and `??` syntax. So thanks in advance for possible consideration. Regards On Fri, Sep 6, 2019 at 9:14 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: > I think introducing this operator encourages bad logic design like > "instanceof", isArray etc. These are unreadable disambiguation factors in > that they don't inform about which part the expression is going to the next > stage in the process. Also it leads to "type branching", which tends > towards more convoluted logical flow. These things, in my mind, would lead > to more bugs. Hence I would tend to be against introducing it, especially > in light of other proposals that I find more useful that haven't been taken > even to discussion. > > On Fri, 6 Sep 2019, 07:58 Claude Pache, <claude.pache at gmail.com> wrote: > >> >> >> Le 5 sept. 2019 à 23:39, Andrea Giammarchi <andrea.giammarchi at gmail.com> >> a écrit : >> >> This is basically a solution to a common problem we have these days, >> where modules published in the wild might have a `default` property, to >> support ESM logic, or not. >> >> ```js >> // current optional chaining logic >> const imported = exported?.default ?? exported; >> >> // my "mice operator" proposal >> const imported = exported<?.default; >> ``` >> >> >> >> The semantics of the `?.` in `exported?.default` is not to check whether >> the `default` property exists, but whether `exported` evaluates to >> undefined or null. If the `default` property is absent, `exported.default` >> has always evaluated to `undefined`, and there is no need to optional >> chaining operator. So that I guess you actually meant: >> >> ``js >> const imported = exported.default ?? exported; >> ``` >> >> >> —Claude >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/88488bb5/attachment-0001.html>
Doesn't that bring risks to breaking the web?
You seen, many, MANY servers running php have the "shot-tags" feature enabled, in which pages with <? and ?> will be interpreted.
In this case, any html page with embedded scripts using this operator, or event .js files when the server is configured to also run php in them, will break.
Or am I missing something here?
[ ]s
--
Felipe N. Moura Web Developer, Google Developer Expert developers.google.com/experts/people/felipe-moura, Founder of
BrazilJS braziljs.org and Nasc nasc.io.
Website: felipenmoura.com / nasc.io Twitter: @felipenmoura twitter.com/felipenmoura
Facebook: fb.com/felipenmoura LinkedIn: goo.gl/qGmq
Changing the world is the least I expect from myself!
Doesn't that bring risks to breaking the web? You seen, many, MANY servers running php have the "shot-tags" feature enabled, in which pages with <? and ?> will be interpreted. In this case, any html page with embedded scripts using this operator, or event .js files when the server is configured to also run php in them, will break. Or am I missing something here? [ ]s *--* *Felipe N. Moura* Web Developer, Google Developer Expert <https://developers.google.com/experts/people/felipe-moura>, Founder of BrazilJS <https://braziljs.org/> and Nasc <http://nasc.io/>. Website: http://felipenmoura.com / http://nasc.io/ Twitter: @felipenmoura <http://twitter.com/felipenmoura> Facebook: http://fb.com/felipenmoura LinkedIn: http://goo.gl/qGmq --------------------------------- *Changing the world* is the least I expect from myself! On Fri, Sep 6, 2019 at 4:59 AM Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote: > The purpose is to address what chaining lacks, in terms of "stopping" at > some point whenever it's available or not. > > Take this example: > > ```js > // the current chaining operator > const result = nmsp.some.payload()?.result ?? nmsp.some.payload(); > > // the mouse operator > const result = nmsp.some.payload()<?.result; > ``` > > Keeping it semantic, the mouse operator is "a trap" for the chain that > makes reading possible expensive parts of the chain easier. An utility to > obtain the same code would look something like the following: > > ```js > // the mouse utility > const mouse = trap => { > // a way to self clean right after, as it is for RegExp.$* values > // which is something impossible to obtain with regular syntax > Promise.resolve(mouse.trap = trap).then(() => delete mouse.trap); > return trap; > }; > > // the previous example > const result = mouse(nmsp.some.payload())?.result ?? mouse.trap; > ``` > > Since there is no easy way to syntactically obtain the same with the > current `?` and `??` without repeating all steps in the right side of the > `??`, I've thought this "mouse operator" would play a role to actually > avoid bugs easily introduced by repeating calls on the right hand side of > the `??` either via getters or expensive operations. > > It is also possible to keep going on a chain or eventually provide > feedbacks of what went wrong: > > ```js > const name = await some.query(id)<?.rows?.[0]?.name; > if (typeof name !== 'string') > throw name; > ``` > > As summary, considering how semantic it's the operator in both visual and > practical meanings, and considering it's not possible to achieve the same > result through `??`, I wish it would be considered as complementary help > for the recently introduced `?.` and `??` syntax. > > So thanks in advance for possible consideration. > > Regards > > > On Fri, Sep 6, 2019 at 9:14 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> I think introducing this operator encourages bad logic design like >> "instanceof", isArray etc. These are unreadable disambiguation factors in >> that they don't inform about which part the expression is going to the next >> stage in the process. Also it leads to "type branching", which tends >> towards more convoluted logical flow. These things, in my mind, would lead >> to more bugs. Hence I would tend to be against introducing it, especially >> in light of other proposals that I find more useful that haven't been taken >> even to discussion. >> >> On Fri, 6 Sep 2019, 07:58 Claude Pache, <claude.pache at gmail.com> wrote: >> >>> >>> >>> Le 5 sept. 2019 à 23:39, Andrea Giammarchi <andrea.giammarchi at gmail.com> >>> a écrit : >>> >>> This is basically a solution to a common problem we have these days, >>> where modules published in the wild might have a `default` property, to >>> support ESM logic, or not. >>> >>> ```js >>> // current optional chaining logic >>> const imported = exported?.default ?? exported; >>> >>> // my "mice operator" proposal >>> const imported = exported<?.default; >>> ``` >>> >>> >>> >>> The semantics of the `?.` in `exported?.default` is not to check whether >>> the `default` property exists, but whether `exported` evaluates to >>> undefined or null. If the `default` property is absent, `exported.default` >>> has always evaluated to `undefined`, and there is no need to optional >>> chaining operator. So that I guess you actually meant: >>> >>> ``js >>> const imported = exported.default ?? exported; >>> ``` >>> >>> >>> —Claude >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/0a55b44f/attachment.html>
I'm not in TC39, sorry if I sounded like I was, just voicing my opinion.
I think the example you gave is better served by throwing the exception from inside "query", instead of doing a "typeof" with the proposed operator afterward.
I find "type branching" normally to be a cumbersome logical flow. Ordinarily the branching can be factored into a common "method" between the types, so that logical flow doesn't need the branching at all (the method "override" does it for you), but in the case of a "mouse" operator, you could often be dealing with completely different types, for which a "common method" may not make sense in the logical flow, thereby necessitating the "type branching" featured in all your examples so far.
To me "type branching" is a non-communicative style of programming. The reader may not know the exact "type" of a particular property or method/function's return value, even more so in JavaScript. Even worse if the method may return different types depending on conditions. It is this lack of clarity that I think can introduce bugs. The remedy, in my mind, is a consistent (non-branching) logical flow. I think that language constructs should encourage that type of programming and discourage type branching (and other patterns that risk having a lack of logical clarity).
Just my opinion, as I said. Disagreements welcome.
I'm not in TC39, sorry if I sounded like I was, just voicing my opinion. I think the example you gave is better served by throwing the exception from inside "query", instead of doing a "typeof" with the proposed operator afterward. I find "type branching" normally to be a cumbersome logical flow. Ordinarily the branching can be factored into a common "method" between the types, so that logical flow doesn't need the branching at all (the method "override" does it for you), but in the case of a "mouse" operator, you could often be dealing with completely different types, for which a "common method" may not make sense in the logical flow, thereby necessitating the "type branching" featured in all your examples so far. To me "type branching" is a non-communicative style of programming. The reader may not know the exact "type" of a particular property or method/function's return value, even more so in JavaScript. Even worse if the method may return different types depending on conditions. It is this lack of clarity that I think can introduce bugs. The remedy, in my mind, is a consistent (non-branching) logical flow. I think that language constructs should encourage that type of programming and discourage type branching (and other patterns that risk having a lack of logical clarity). Just my opinion, as I said. Disagreements welcome. On Fri, 6 Sep 2019 at 08:59, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote: > The purpose is to address what chaining lacks, in terms of "stopping" at > some point whenever it's available or not. > > Take this example: > > ```js > // the current chaining operator > const result = nmsp.some.payload()?.result ?? nmsp.some.payload(); > > // the mouse operator > const result = nmsp.some.payload()<?.result; > ``` > > Keeping it semantic, the mouse operator is "a trap" for the chain that > makes reading possible expensive parts of the chain easier. An utility to > obtain the same code would look something like the following: > > ```js > // the mouse utility > const mouse = trap => { > // a way to self clean right after, as it is for RegExp.$* values > // which is something impossible to obtain with regular syntax > Promise.resolve(mouse.trap = trap).then(() => delete mouse.trap); > return trap; > }; > > // the previous example > const result = mouse(nmsp.some.payload())?.result ?? mouse.trap; > ``` > > Since there is no easy way to syntactically obtain the same with the > current `?` and `??` without repeating all steps in the right side of the > `??`, I've thought this "mouse operator" would play a role to actually > avoid bugs easily introduced by repeating calls on the right hand side of > the `??` either via getters or expensive operations. > > It is also possible to keep going on a chain or eventually provide > feedbacks of what went wrong: > > ```js > const name = await some.query(id)<?.rows?.[0]?.name; > if (typeof name !== 'string') > throw name; > ``` > > As summary, considering how semantic it's the operator in both visual and > practical meanings, and considering it's not possible to achieve the same > result through `??`, I wish it would be considered as complementary help > for the recently introduced `?.` and `??` syntax. > > So thanks in advance for possible consideration. > > Regards > > > On Fri, Sep 6, 2019 at 9:14 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> I think introducing this operator encourages bad logic design like >> "instanceof", isArray etc. These are unreadable disambiguation factors in >> that they don't inform about which part the expression is going to the next >> stage in the process. Also it leads to "type branching", which tends >> towards more convoluted logical flow. These things, in my mind, would lead >> to more bugs. Hence I would tend to be against introducing it, especially >> in light of other proposals that I find more useful that haven't been taken >> even to discussion. >> >> On Fri, 6 Sep 2019, 07:58 Claude Pache, <claude.pache at gmail.com> wrote: >> >>> >>> >>> Le 5 sept. 2019 à 23:39, Andrea Giammarchi <andrea.giammarchi at gmail.com> >>> a écrit : >>> >>> This is basically a solution to a common problem we have these days, >>> where modules published in the wild might have a `default` property, to >>> support ESM logic, or not. >>> >>> ```js >>> // current optional chaining logic >>> const imported = exported?.default ?? exported; >>> >>> // my "mice operator" proposal >>> const imported = exported<?.default; >>> ``` >>> >>> >>> >>> The semantics of the `?.` in `exported?.default` is not to check whether >>> the `default` property exists, but whether `exported` evaluates to >>> undefined or null. If the `default` property is absent, `exported.default` >>> has always evaluated to `undefined`, and there is no need to optional >>> chaining operator. So that I guess you actually meant: >>> >>> ``js >>> const imported = exported.default ?? exported; >>> ``` >>> >>> >>> —Claude >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/892d32a0/attachment-0001.html>
You keep diverging from the intent, basing your answers on my quick'n'dirty examples. I agree my examples are probably not the best looking, but there are no solutions right now to retrieve one part of th echain that failed, if not repeating the chain, and eventually thesame errors, on the right hand side.
This is what <?
would like to solve, and I've created a mice.trap [1]
module so that maybe devs can start playing with the idea, and come back
with a better experience/understanding of such idea.
The point about PHP is also somehow valid, but I wasn't aware about the fact we have other PL constrains with JS syntax.
You keep diverging from the intent, basing your answers on my quick'n'dirty examples. I agree my examples are probably not the best looking, but there are no solutions right now to retrieve one part of th echain that failed, if not repeating the chain, and eventually thesame errors, on the right hand side. This is what `<?` would like to solve, and I've created a mice.trap [1] module so that maybe devs can start playing with the idea, and come back with a better experience/understanding of such idea. The point about PHP is also somehow valid, but I wasn't aware about the fact we have other PL constrains with JS syntax. [1] https://github.com/WebReflection/mice.trap#readme On Fri, Sep 6, 2019 at 2:48 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: > > I'm not in TC39, sorry if I sounded like I was, just voicing my opinion. > > I think the example you gave is better served by throwing the exception > from inside "query", instead of doing a "typeof" with the proposed operator > afterward. > > I find "type branching" normally to be a cumbersome logical flow. > Ordinarily the branching can be factored into a common "method" between the > types, so that logical flow doesn't need the branching at all (the method > "override" does it for you), but in the case of a "mouse" operator, you > could often be dealing with completely different types, for which a "common > method" may not make sense in the logical flow, thereby necessitating the > "type branching" featured in all your examples so far. > > To me "type branching" is a non-communicative style of programming. The > reader may not know the exact "type" of a particular property or > method/function's return value, even more so in JavaScript. Even worse if > the method may return different types depending on conditions. It is this > lack of clarity that I think can introduce bugs. The remedy, in my mind, is > a consistent (non-branching) logical flow. I think that language constructs > should encourage that type of programming and discourage type branching > (and other patterns that risk having a lack of logical clarity). > > Just my opinion, as I said. Disagreements welcome. > > On Fri, 6 Sep 2019 at 08:59, Andrea Giammarchi < > andrea.giammarchi at gmail.com> wrote: > >> The purpose is to address what chaining lacks, in terms of "stopping" at >> some point whenever it's available or not. >> >> Take this example: >> >> ```js >> // the current chaining operator >> const result = nmsp.some.payload()?.result ?? nmsp.some.payload(); >> >> // the mouse operator >> const result = nmsp.some.payload()<?.result; >> ``` >> >> Keeping it semantic, the mouse operator is "a trap" for the chain that >> makes reading possible expensive parts of the chain easier. An utility to >> obtain the same code would look something like the following: >> >> ```js >> // the mouse utility >> const mouse = trap => { >> // a way to self clean right after, as it is for RegExp.$* values >> // which is something impossible to obtain with regular syntax >> Promise.resolve(mouse.trap = trap).then(() => delete mouse.trap); >> return trap; >> }; >> >> // the previous example >> const result = mouse(nmsp.some.payload())?.result ?? mouse.trap; >> ``` >> >> Since there is no easy way to syntactically obtain the same with the >> current `?` and `??` without repeating all steps in the right side of the >> `??`, I've thought this "mouse operator" would play a role to actually >> avoid bugs easily introduced by repeating calls on the right hand side of >> the `??` either via getters or expensive operations. >> >> It is also possible to keep going on a chain or eventually provide >> feedbacks of what went wrong: >> >> ```js >> const name = await some.query(id)<?.rows?.[0]?.name; >> if (typeof name !== 'string') >> throw name; >> ``` >> >> As summary, considering how semantic it's the operator in both visual and >> practical meanings, and considering it's not possible to achieve the same >> result through `??`, I wish it would be considered as complementary help >> for the recently introduced `?.` and `??` syntax. >> >> So thanks in advance for possible consideration. >> >> Regards >> >> >> On Fri, Sep 6, 2019 at 9:14 AM Naveen Chawla <naveen.chwl at gmail.com> >> wrote: >> >>> I think introducing this operator encourages bad logic design like >>> "instanceof", isArray etc. These are unreadable disambiguation factors in >>> that they don't inform about which part the expression is going to the next >>> stage in the process. Also it leads to "type branching", which tends >>> towards more convoluted logical flow. These things, in my mind, would lead >>> to more bugs. Hence I would tend to be against introducing it, especially >>> in light of other proposals that I find more useful that haven't been taken >>> even to discussion. >>> >>> On Fri, 6 Sep 2019, 07:58 Claude Pache, <claude.pache at gmail.com> wrote: >>> >>>> >>>> >>>> Le 5 sept. 2019 à 23:39, Andrea Giammarchi <andrea.giammarchi at gmail.com> >>>> a écrit : >>>> >>>> This is basically a solution to a common problem we have these days, >>>> where modules published in the wild might have a `default` property, to >>>> support ESM logic, or not. >>>> >>>> ```js >>>> // current optional chaining logic >>>> const imported = exported?.default ?? exported; >>>> >>>> // my "mice operator" proposal >>>> const imported = exported<?.default; >>>> ``` >>>> >>>> >>>> >>>> The semantics of the `?.` in `exported?.default` is not to check >>>> whether the `default` property exists, but whether `exported` evaluates to >>>> undefined or null. If the `default` property is absent, `exported.default` >>>> has always evaluated to `undefined`, and there is no need to optional >>>> chaining operator. So that I guess you actually meant: >>>> >>>> ``js >>>> const imported = exported.default ?? exported; >>>> ``` >>>> >>>> >>>> —Claude >>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/3cd92246/attachment.html>
Le 6 sept. 2019 à 14:35, Felipe Nascimento de Moura <felipenmoura at gmail.com> a écrit :
Doesn't that bring risks to breaking the web?
You seen, many, MANY servers running php have the "shot-tags" feature enabled, in which pages with <? and ?> will be interpreted. In this case, any html page with embedded scripts using this operator, or event .js files when the server is configured to also run php in them, will break.
Or am I missing something here?
[ ]s
Any future PHP file that incorporate that syntax will almost surely refuse to compile on servers that has short-tags enabled, making the problem evident before it produces something useful on the web. This may be an issue, but this is not what “breaking the web” is intended to mean. Existing, untouched content will not break. Carelessly updated content might break, but that’s not fundamentally different from any other careless update.
(If anything else, it may convince people that having different configuration settings w.r.t. short-tags in development environment and in production environment, is a very bad idea...)
> Le 6 sept. 2019 à 14:35, Felipe Nascimento de Moura <felipenmoura at gmail.com> a écrit : > > Doesn't that bring risks to breaking the web? > > You seen, many, MANY servers running php have the "shot-tags" feature enabled, in which pages with <? and ?> will be interpreted. > In this case, any html page with embedded scripts using this operator, or event .js files when the server is configured to also run php in them, will break. > > Or am I missing something here? > > [ ]s > Any future PHP file that incorporate that syntax will almost surely refuse to compile on servers that has short-tags enabled, making the problem evident before it produces something useful on the web. This may be an issue, but this is not what “breaking the web” is intended to mean. Existing, untouched content will not break. Carelessly updated content might break, but that’s not fundamentally different from any other careless update. (If anything else, it may convince people that having different configuration settings w.r.t. short-tags in development environment and in production environment, is a very bad idea...) —Claude -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/7d6b99eb/attachment-0001.html>
Typically, "dot" expressions navigate through values of different types, making "type branching" the inevitable next step in those cases (unless you introduce a common method for further processing for each of those types). So I'm not sure how ultimately that would be avoided.
Typically, "dot" expressions navigate through values of different types, making "type branching" the inevitable next step in those cases (unless you introduce a common method for further processing for each of those types). So I'm not sure how ultimately that would be avoided. On Fri, 6 Sep 2019 at 14:15, Claude Pache <claude.pache at gmail.com> wrote: > > > Le 6 sept. 2019 à 14:35, Felipe Nascimento de Moura < > felipenmoura at gmail.com> a écrit : > > Doesn't that bring risks to breaking the web? > > You seen, many, MANY servers running php have the "shot-tags" feature > enabled, in which pages with <? and ?> will be interpreted. > In this case, any html page with embedded scripts using this operator, or > event .js files when the server is configured to also run php in them, will > break. > > Or am I missing something here? > > [ ]s > > > Any future PHP file that incorporate that syntax will almost surely refuse > to compile on servers that has short-tags enabled, making the problem > evident before it produces something useful on the web. This may be an > issue, but this is not what “breaking the web” is intended to mean. > Existing, untouched content will not break. Carelessly updated content > might break, but that’s not fundamentally different from any other careless > update. > > (If anything else, it may convince people that having different > configuration settings w.r.t. short-tags in development environment and in > production environment, is a very bad idea...) > > —Claude > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/536cd558/attachment.html>
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
)
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`) On Fri, Sep 6, 2019 at 4:17 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: > Typically, "dot" expressions navigate through values of different types, > making "type branching" the inevitable next step in those cases (unless you > introduce a common method for further processing for each of those types). > So I'm not sure how ultimately that would be avoided. > > On Fri, 6 Sep 2019 at 14:15, Claude Pache <claude.pache at gmail.com> wrote: > >> >> >> Le 6 sept. 2019 à 14:35, Felipe Nascimento de Moura < >> felipenmoura at gmail.com> a écrit : >> >> Doesn't that bring risks to breaking the web? >> >> You seen, many, MANY servers running php have the "shot-tags" feature >> enabled, in which pages with <? and ?> will be interpreted. >> In this case, any html page with embedded scripts using this operator, or >> event .js files when the server is configured to also run php in them, will >> break. >> >> Or am I missing something here? >> >> [ ]s >> >> >> Any future PHP file that incorporate that syntax will almost surely >> refuse to compile on servers that has short-tags enabled, making the >> problem evident before it produces something useful on the web. This may be >> an issue, but this is not what “breaking the web” is intended to mean. >> Existing, untouched content will not break. Carelessly updated content >> might break, but that’s not fundamentally different from any other careless >> update. >> >> (If anything else, it may convince people that having different >> configuration settings w.r.t. short-tags in development environment and in >> production environment, is a very bad idea...) >> >> —Claude >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/fc29ef1f/attachment.html>
Syntactically marking, in a chain, what you'd like the final value of the chain to be, seems interesting - forcing optionality into it seems unnecessary, though, if such a syntactic marker could be attached to all forms of property access.
Something like: a.b>.c.d
or a?.b>?.c?.d
or a>[b][c][d]
.
(Obviously, the >
won't work with bracket, and any syntax for normal
properties that only applies to dot and not also bracket would somewhat be a nonstarter; but the specific syntax can be bikeshedded separately)
Syntactically marking, in a chain, what you'd like the final value of the chain to be, seems interesting - forcing optionality into it seems unnecessary, though, if such a syntactic marker could be attached to all forms of property access. Something like: `a.b>.c.d` or `a?.b>?.c?.d` or `a>[b][c][d]`. (Obviously, the `>` won't work with bracket, and any syntax for normal properties that only applies to dot and not also bracket would somewhat be a nonstarter; but the specific syntax can be bikeshedded separately) On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi < andrea.giammarchi at gmail.com> 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`) > > On Fri, Sep 6, 2019 at 4:17 PM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> Typically, "dot" expressions navigate through values of different types, >> making "type branching" the inevitable next step in those cases (unless you >> introduce a common method for further processing for each of those types). >> So I'm not sure how ultimately that would be avoided. >> >> On Fri, 6 Sep 2019 at 14:15, Claude Pache <claude.pache at gmail.com> wrote: >> >>> >>> >>> Le 6 sept. 2019 à 14:35, Felipe Nascimento de Moura < >>> felipenmoura at gmail.com> a écrit : >>> >>> Doesn't that bring risks to breaking the web? >>> >>> You seen, many, MANY servers running php have the "shot-tags" feature >>> enabled, in which pages with <? and ?> will be interpreted. >>> In this case, any html page with embedded scripts using this operator, >>> or event .js files when the server is configured to also run php in them, >>> will break. >>> >>> Or am I missing something here? >>> >>> [ ]s >>> >>> >>> Any future PHP file that incorporate that syntax will almost surely >>> refuse to compile on servers that has short-tags enabled, making the >>> problem evident before it produces something useful on the web. This may be >>> an issue, but this is not what “breaking the web” is intended to mean. >>> Existing, untouched content will not break. Carelessly updated content >>> might break, but that’s not fundamentally different from any other careless >>> update. >>> >>> (If anything else, it may convince people that having different >>> configuration settings w.r.t. short-tags in development environment and in >>> production environment, is a very bad idea...) >>> >>> —Claude >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/d5d855fc/attachment-0001.html>
It's <?. though, not >
It's <?. though, not > On Fri, Sep 6, 2019, 20:14 Jordan Harband <ljharb at gmail.com> wrote: > Syntactically marking, in a chain, what you'd like the final value of the > chain to be, seems interesting - forcing optionality into it seems > unnecessary, though, if such a syntactic marker could be attached to all > forms of property access. > > Something like: `a.b>.c.d` or `a?.b>?.c?.d` or `a>[b][c][d]`. > > (Obviously, the `>` won't work with bracket, and any syntax for normal > properties that only applies to dot and not also bracket would somewhat be > a nonstarter; but the specific syntax can be bikeshedded separately) > > On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi < > andrea.giammarchi at gmail.com> 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`) >> >> On Fri, Sep 6, 2019 at 4:17 PM Naveen Chawla <naveen.chwl at gmail.com> >> wrote: >> >>> Typically, "dot" expressions navigate through values of different types, >>> making "type branching" the inevitable next step in those cases (unless you >>> introduce a common method for further processing for each of those types). >>> So I'm not sure how ultimately that would be avoided. >>> >>> On Fri, 6 Sep 2019 at 14:15, Claude Pache <claude.pache at gmail.com> >>> wrote: >>> >>>> >>>> >>>> Le 6 sept. 2019 à 14:35, Felipe Nascimento de Moura < >>>> felipenmoura at gmail.com> a écrit : >>>> >>>> Doesn't that bring risks to breaking the web? >>>> >>>> You seen, many, MANY servers running php have the "shot-tags" feature >>>> enabled, in which pages with <? and ?> will be interpreted. >>>> In this case, any html page with embedded scripts using this operator, >>>> or event .js files when the server is configured to also run php in them, >>>> will break. >>>> >>>> Or am I missing something here? >>>> >>>> [ ]s >>>> >>>> >>>> Any future PHP file that incorporate that syntax will almost surely >>>> refuse to compile on servers that has short-tags enabled, making the >>>> problem evident before it produces something useful on the web. This may be >>>> an issue, but this is not what “breaking the web” is intended to mean. >>>> Existing, untouched content will not break. Carelessly updated content >>>> might break, but that’s not fundamentally different from any other careless >>>> update. >>>> >>>> (If anything else, it may convince people that having different >>>> configuration settings w.r.t. short-tags in development environment and in >>>> production environment, is a very bad idea...) >>>> >>>> —Claude >>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190906/a4887557/attachment.html>
On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi <andrea.giammarchi at gmail.com> 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 inconst 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 thatresult !== 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!
On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi <andrea.giammarchi at gmail.com> 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
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:
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.
// 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).
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. <jackalmage at gmail.com> wrote: > On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi > <andrea.giammarchi at gmail.com> 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 > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190907/1f8e62a3/attachment-0001.html>
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;
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, <andrea.giammarchi at gmail.com> 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. <jackalmage at gmail.com> > wrote: > >> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >> <andrea.giammarchi at gmail.com> 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 > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190907/c8330268/attachment.html>
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
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 <rosyatrandom at gmail.com> 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, <andrea.giammarchi at gmail.com> > 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. <jackalmage at gmail.com> >> wrote: >> >>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>> <andrea.giammarchi at gmail.com> 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 >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190907/3d6b55de/attachment-0001.html>
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.
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 <andrea.giammarchi at gmail.com> 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 <rosyatrandom at gmail.com> > 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, <andrea.giammarchi at gmail.com> >> 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. <jackalmage at gmail.com> >>> wrote: >>> >>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>> <andrea.giammarchi at gmail.com> 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 >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190907/e42182e6/attachment.html>
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.
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.
`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 <naveen.chwl at gmail.com> 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 < > andrea.giammarchi at gmail.com> 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 < >> rosyatrandom at gmail.com> 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, < >>> andrea.giammarchi at gmail.com> 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. <jackalmage at gmail.com> >>>> wrote: >>>> >>>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>>> <andrea.giammarchi at gmail.com> 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 >>>> es-discuss at mozilla.org >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190909/a4af1b13/attachment-0001.html>
so maybe we'll come back...
so *maybe* we'll come back... On Mon, Sep 9, 2019 at 10:04 AM Andrea Giammarchi < andrea.giammarchi at gmail.com> 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 <naveen.chwl at gmail.com> > 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 < >> andrea.giammarchi at gmail.com> 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 < >>> rosyatrandom at gmail.com> 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, < >>>> andrea.giammarchi at gmail.com> 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. <jackalmage at gmail.com> >>>>> wrote: >>>>> >>>>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>>>> <andrea.giammarchi at gmail.com> 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 >>>>> es-discuss at mozilla.org >>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>> >>>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190909/67636d20/attachment.html>
"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
"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, <andrea.giammarchi at gmail.com> wrote: > so *maybe* we'll come back... > > On Mon, Sep 9, 2019 at 10:04 AM Andrea Giammarchi < > andrea.giammarchi at gmail.com> 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 <naveen.chwl at gmail.com> >> 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 < >>> andrea.giammarchi at gmail.com> 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 < >>>> rosyatrandom at gmail.com> 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, < >>>>> andrea.giammarchi at gmail.com> 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. <jackalmage at gmail.com> >>>>>> wrote: >>>>>> >>>>>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>>>>> <andrea.giammarchi at gmail.com> 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 >>>>>> es-discuss at mozilla.org >>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>> >>>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190909/da38f732/attachment.html>
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.
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 <naveen.chwl at gmail.com> 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, <andrea.giammarchi at gmail.com> > wrote: > >> so *maybe* we'll come back... >> >> On Mon, Sep 9, 2019 at 10:04 AM Andrea Giammarchi < >> andrea.giammarchi at gmail.com> 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 <naveen.chwl at gmail.com> >>> 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 < >>>> andrea.giammarchi at gmail.com> 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 < >>>>> rosyatrandom at gmail.com> 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, < >>>>>> andrea.giammarchi at gmail.com> 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. <jackalmage at gmail.com> >>>>>>> wrote: >>>>>>> >>>>>>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>>>>>> <andrea.giammarchi at gmail.com> 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 >>>>>>> es-discuss at mozilla.org >>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>> >>>>>> _______________________________________________ >>>>> es-discuss mailing list >>>>> es-discuss at mozilla.org >>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190909/92ca25f6/attachment-0001.html>
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.
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 <andrea.giammarchi at gmail.com> 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 <naveen.chwl at gmail.com> > 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, <andrea.giammarchi at gmail.com> >> wrote: >> >>> so *maybe* we'll come back... >>> >>> On Mon, Sep 9, 2019 at 10:04 AM Andrea Giammarchi < >>> andrea.giammarchi at gmail.com> 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 <naveen.chwl at gmail.com> >>>> 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 < >>>>> andrea.giammarchi at gmail.com> 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 < >>>>>> rosyatrandom at gmail.com> 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, < >>>>>>> andrea.giammarchi at gmail.com> 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. < >>>>>>>> jackalmage at gmail.com> wrote: >>>>>>>> >>>>>>>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>>>>>>> <andrea.giammarchi at gmail.com> 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 >>>>>>>> es-discuss at mozilla.org >>>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>>> >>>>>>> _______________________________________________ >>>>>> es-discuss mailing list >>>>>> es-discuss at mozilla.org >>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190909/c3a65925/attachment-0001.html>
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.
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 <naveen.chwl at gmail.com> 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 < > andrea.giammarchi at gmail.com> 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 <naveen.chwl at gmail.com> >> 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, < >>> andrea.giammarchi at gmail.com> wrote: >>> >>>> so *maybe* we'll come back... >>>> >>>> On Mon, Sep 9, 2019 at 10:04 AM Andrea Giammarchi < >>>> andrea.giammarchi at gmail.com> 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 <naveen.chwl at gmail.com> >>>>> 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 < >>>>>> andrea.giammarchi at gmail.com> 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 < >>>>>>> rosyatrandom at gmail.com> 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, < >>>>>>>> andrea.giammarchi at gmail.com> 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. < >>>>>>>>> jackalmage at gmail.com> wrote: >>>>>>>>> >>>>>>>>>> On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi >>>>>>>>>> <andrea.giammarchi at gmail.com> 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 >>>>>>>>> es-discuss at mozilla.org >>>>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>>>> >>>>>>>> _______________________________________________ >>>>>>> es-discuss mailing list >>>>>>> es-discuss at mozilla.org >>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>> >>>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190909/39917954/attachment-0001.html>
This is basically a solution to a common problem we have these days, where modules published in the wild might have a
default
property, to support ESM logic, or not.// current optional chaining logic const imported = exported?.default ?? exported; // my "mice operator" proposal const imported = exported<?.default;
Semantically speaking, not only
<?
actually looks like a mice, it also points at its previous value in case the chaining didn't work.Beside the basic example, the "mice operator" might save CPU cycles when it comes to involving more complex expressions, i.e.
// current "solution" const thing = require('thing')?.default ?? require('thing'); // mice operator const thing = require('thing')<?.default;
This is also easily tranpilable, so kinda a no-brainer for modern dev tools to bring in.
TL;DR specially for cases where an accessed property should fallback to its source, this operator might save both typing and CPU time whenever it's needed.
What do you think about it?