Proposal: switch expressions
This would be covered by do expressions tc39/proposal-do-expressions. You could just do:
const category = do {
switch (...) {
...
};
};
Hi, This would be covered by do expressions <https://github.com/tc39/proposal-do-expressions>. You could just do: ```js const category = do { switch (...) { ... }; }; ``` On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: > After looking at a bunch of code in our system noted that there are many > cases where our code base has a pattern similar to this: > > let category = data.category; > > if (category === undefined) { > // Even if Tax is not enabled, we have defaults for incomeCode > switch (session.merchant.settings.tax.incomeCode) { > case TaxIncomeCode.RENTS_14: > category = PaymentCategory.RENT; > break; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > category = PaymentCategory.SERVICES; > break; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > category = PaymentCategory.SERVICES; > break; > } > } > > I also bumped into a block of go code that also implemented similar > patterns, which really demonstrated to me that there while you could go > crazy with triary nesting there should be a better way. Looked at the > pattern matching proposal and while could possibly help looked like it > was overkill for the typical use case that I'm seeing. The most relevant > example I noted was switch expressions from Java. When applied to this > problem really create a simple result: > > const category = data.category || switch (setting.incomeCode) { > case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > PaymentCategory.ROYALTIES; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > PaymentCategory.SERVICES; > default => PaymentCategory.OTHER; > } > > Note; the instead of using the '->' as Java, continue to use => and with > the understanding that the right hand side is fundamentally function. > So similar things to this are natural, note this proposal should remove > "fall through" breaks and allow for multiple cases as such. > > const quarter = switch (foo) { > case "Jan", "Feb", "Mar" => "Q1"; > case "Apr", "May", "Jun" => "Q2"; > case "Jul", "Aug", "Sep" => "Q3"; > case "Oct", "Nov", "Dec" => { return "Q4" }; > default => { throw new Error("Invalid Month") }; > } > > Also compared this to the do expression proposal, it also provides a > substantial simplification, but in a way that is more consistent with > the existing language. In one of their examples they provide an example > of the Redux reducer > https://redux.js.org/basics/reducers#splitting-reducers -- this would be > a switch expression implementation. > > function todoApp(state = initialState, action) => switch > (action.type) { > case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > action.filter }; > case ADD_TODO => { > ...state, todos: [ > ...state.todos, > { > text: action.text, > completed: false > } > ] > }; > case TOGGLE_TODO => { > ...state, > todos: state.todos.map((todo, index) => (index === > action.index) ? { ...todo, completed: !todo.completed } : todo) > }; > default => state; > } > > > > _______________________________________________ > 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/20190225/87931e24/attachment.html>
I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language.
Hence why I wanted to put out there the idea of switch expressions.
I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. Hence why I wanted to put out there the idea of switch expressions. --David > On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > > Hi, > > This would be covered by do expressions. You could just do: > > ```js > const category = do { > switch (...) { > ... > }; > }; > ``` > >> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> After looking at a bunch of code in our system noted that there are many >> cases where our code base has a pattern similar to this: >> >> let category = data.category; >> >> if (category === undefined) { >> // Even if Tax is not enabled, we have defaults for incomeCode >> switch (session.merchant.settings.tax.incomeCode) { >> case TaxIncomeCode.RENTS_14: >> category = PaymentCategory.RENT; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> } >> } >> >> I also bumped into a block of go code that also implemented similar >> patterns, which really demonstrated to me that there while you could go >> crazy with triary nesting there should be a better way. Looked at the >> pattern matching proposal and while could possibly help looked like it >> was overkill for the typical use case that I'm seeing. The most relevant >> example I noted was switch expressions from Java. When applied to this >> problem really create a simple result: >> >> const category = data.category || switch (setting.incomeCode) { >> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> PaymentCategory.ROYALTIES; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> PaymentCategory.SERVICES; >> default => PaymentCategory.OTHER; >> } >> >> Note; the instead of using the '->' as Java, continue to use => and with >> the understanding that the right hand side is fundamentally function. >> So similar things to this are natural, note this proposal should remove >> "fall through" breaks and allow for multiple cases as such. >> >> const quarter = switch (foo) { >> case "Jan", "Feb", "Mar" => "Q1"; >> case "Apr", "May", "Jun" => "Q2"; >> case "Jul", "Aug", "Sep" => "Q3"; >> case "Oct", "Nov", "Dec" => { return "Q4" }; >> default => { throw new Error("Invalid Month") }; >> } >> >> Also compared this to the do expression proposal, it also provides a >> substantial simplification, but in a way that is more consistent with >> the existing language. In one of their examples they provide an example >> of the Redux reducer >> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> a switch expression implementation. >> >> function todoApp(state = initialState, action) => switch >> (action.type) { >> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> action.filter }; >> case ADD_TODO => { >> ...state, todos: [ >> ...state.todos, >> { >> text: action.text, >> completed: false >> } >> ] >> }; >> case TOGGLE_TODO => { >> ...state, >> todos: state.todos.map((todo, index) => (index === >> action.index) ? { ...todo, completed: !todo.completed } : todo) >> }; >> default => state; >> } >> >> >> >> _______________________________________________ >> 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/20190225/1f4e29c2/attachment-0001.html>
Additionally, tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history.
Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > I quite aware that it’s covered in do expressions. Personally I find do > expressions non-JavaScript in style and it’s also not necessarily going to > make it into the language. > > Hence why I wanted to put out there the idea of switch expressions. > > --David > > > On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > > Hi, > > This would be covered by do expressions > <https://github.com/tc39/proposal-do-expressions>. You could just do: > > ```js > const category = do { > switch (...) { > ... > }; > }; > ``` > > On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: > >> After looking at a bunch of code in our system noted that there are many >> cases where our code base has a pattern similar to this: >> >> let category = data.category; >> >> if (category === undefined) { >> // Even if Tax is not enabled, we have defaults for incomeCode >> switch (session.merchant.settings.tax.incomeCode) { >> case TaxIncomeCode.RENTS_14: >> category = PaymentCategory.RENT; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> } >> } >> >> I also bumped into a block of go code that also implemented similar >> patterns, which really demonstrated to me that there while you could go >> crazy with triary nesting there should be a better way. Looked at the >> pattern matching proposal and while could possibly help looked like it >> was overkill for the typical use case that I'm seeing. The most relevant >> example I noted was switch expressions from Java. When applied to this >> problem really create a simple result: >> >> const category = data.category || switch (setting.incomeCode) { >> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> PaymentCategory.ROYALTIES; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> PaymentCategory.SERVICES; >> default => PaymentCategory.OTHER; >> } >> >> Note; the instead of using the '->' as Java, continue to use => and with >> the understanding that the right hand side is fundamentally function. >> So similar things to this are natural, note this proposal should remove >> "fall through" breaks and allow for multiple cases as such. >> >> const quarter = switch (foo) { >> case "Jan", "Feb", "Mar" => "Q1"; >> case "Apr", "May", "Jun" => "Q2"; >> case "Jul", "Aug", "Sep" => "Q3"; >> case "Oct", "Nov", "Dec" => { return "Q4" }; >> default => { throw new Error("Invalid Month") }; >> } >> >> Also compared this to the do expression proposal, it also provides a >> substantial simplification, but in a way that is more consistent with >> the existing language. In one of their examples they provide an example >> of the Redux reducer >> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> a switch expression implementation. >> >> function todoApp(state = initialState, action) => switch >> (action.type) { >> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> action.filter }; >> case ADD_TODO => { >> ...state, todos: [ >> ...state.todos, >> { >> text: action.text, >> completed: false >> } >> ] >> }; >> case TOGGLE_TODO => { >> ...state, >> todos: state.todos.map((todo, index) => (index === >> action.index) ? { ...todo, completed: !todo.completed } : todo) >> }; >> default => state; >> } >> >> >> >> _______________________________________________ >> 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/20190225/fee81b3c/attachment.html>
Jordan,
One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct.
Is there somewhere I can find a bit of discussion about the history of the syntax decisions?
Jordan, One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. Is there somewhere I can find a bit of discussion about the history of the syntax decisions? --David > On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > > Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. > >> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> Hence why I wanted to put out there the idea of switch expressions. >> >> --David >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>> >>> Hi, >>> >>> This would be covered by do expressions. You could just do: >>> >>> ```js >>> const category = do { >>> switch (...) { >>> ... >>> }; >>> }; >>> ``` >>> >>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>> After looking at a bunch of code in our system noted that there are many >>>> cases where our code base has a pattern similar to this: >>>> >>>> let category = data.category; >>>> >>>> if (category === undefined) { >>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>> switch (session.merchant.settings.tax.incomeCode) { >>>> case TaxIncomeCode.RENTS_14: >>>> category = PaymentCategory.RENT; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> } >>>> } >>>> >>>> I also bumped into a block of go code that also implemented similar >>>> patterns, which really demonstrated to me that there while you could go >>>> crazy with triary nesting there should be a better way. Looked at the >>>> pattern matching proposal and while could possibly help looked like it >>>> was overkill for the typical use case that I'm seeing. The most relevant >>>> example I noted was switch expressions from Java. When applied to this >>>> problem really create a simple result: >>>> >>>> const category = data.category || switch (setting.incomeCode) { >>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>> PaymentCategory.ROYALTIES; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>> PaymentCategory.SERVICES; >>>> default => PaymentCategory.OTHER; >>>> } >>>> >>>> Note; the instead of using the '->' as Java, continue to use => and with >>>> the understanding that the right hand side is fundamentally function. >>>> So similar things to this are natural, note this proposal should remove >>>> "fall through" breaks and allow for multiple cases as such. >>>> >>>> const quarter = switch (foo) { >>>> case "Jan", "Feb", "Mar" => "Q1"; >>>> case "Apr", "May", "Jun" => "Q2"; >>>> case "Jul", "Aug", "Sep" => "Q3"; >>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>> default => { throw new Error("Invalid Month") }; >>>> } >>>> >>>> Also compared this to the do expression proposal, it also provides a >>>> substantial simplification, but in a way that is more consistent with >>>> the existing language. In one of their examples they provide an example >>>> of the Redux reducer >>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>> a switch expression implementation. >>>> >>>> function todoApp(state = initialState, action) => switch >>>> (action.type) { >>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>> action.filter }; >>>> case ADD_TODO => { >>>> ...state, todos: [ >>>> ...state.todos, >>>> { >>>> text: action.text, >>>> completed: false >>>> } >>>> ] >>>> }; >>>> case TOGGLE_TODO => { >>>> ...state, >>>> todos: state.todos.map((todo, index) => (index === >>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>> }; >>>> default => state; >>>> } >>>> >>>> >>>> >>>> _______________________________________________ >>>> 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/20190225/e6f31d3a/attachment.html>
Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point.
I can speak for myself (as "not a champion" of that proposal, just a fan)
that any similarity to the reviled and terrible switch
is something I'll
be pushing back against - I want a replacement that lacks the footguns and
pitfalls of switch
, and that is easily teachable and googleable as a
different, distinct thing.
Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: > Jordan, > > One question that I have lingering from pattern matching is why is the > syntax so different? IMHO it is still a switch statement with a variation > of the match on the case rather than a whole new construct. > > Is there somewhere I can find a bit of discussion about the history of the > syntax decisions? > > --David > > > On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > > Additionally, https://github.com/tc39/proposal-pattern-matching - switch > statements are something I hope we'll soon be able to relegate to the > dustbin of history. > > On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > >> I quite aware that it’s covered in do expressions. Personally I find do >> expressions non-JavaScript in style and it’s also not necessarily going to >> make it into the language. >> >> Hence why I wanted to put out there the idea of switch expressions. >> >> --David >> >> >> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> Hi, >> >> This would be covered by do expressions >> <https://github.com/tc39/proposal-do-expressions>. You could just do: >> >> ```js >> const category = do { >> switch (...) { >> ... >> }; >> }; >> ``` >> >> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >>> After looking at a bunch of code in our system noted that there are many >>> cases where our code base has a pattern similar to this: >>> >>> let category = data.category; >>> >>> if (category === undefined) { >>> // Even if Tax is not enabled, we have defaults for incomeCode >>> switch (session.merchant.settings.tax.incomeCode) { >>> case TaxIncomeCode.RENTS_14: >>> category = PaymentCategory.RENT; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> } >>> } >>> >>> I also bumped into a block of go code that also implemented similar >>> patterns, which really demonstrated to me that there while you could go >>> crazy with triary nesting there should be a better way. Looked at the >>> pattern matching proposal and while could possibly help looked like it >>> was overkill for the typical use case that I'm seeing. The most relevant >>> example I noted was switch expressions from Java. When applied to this >>> problem really create a simple result: >>> >>> const category = data.category || switch (setting.incomeCode) { >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>> PaymentCategory.ROYALTIES; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>> PaymentCategory.SERVICES; >>> default => PaymentCategory.OTHER; >>> } >>> >>> Note; the instead of using the '->' as Java, continue to use => and with >>> the understanding that the right hand side is fundamentally function. >>> So similar things to this are natural, note this proposal should remove >>> "fall through" breaks and allow for multiple cases as such. >>> >>> const quarter = switch (foo) { >>> case "Jan", "Feb", "Mar" => "Q1"; >>> case "Apr", "May", "Jun" => "Q2"; >>> case "Jul", "Aug", "Sep" => "Q3"; >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>> default => { throw new Error("Invalid Month") }; >>> } >>> >>> Also compared this to the do expression proposal, it also provides a >>> substantial simplification, but in a way that is more consistent with >>> the existing language. In one of their examples they provide an example >>> of the Redux reducer >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would >>> be >>> a switch expression implementation. >>> >>> function todoApp(state = initialState, action) => switch >>> (action.type) { >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>> action.filter }; >>> case ADD_TODO => { >>> ...state, todos: [ >>> ...state.todos, >>> { >>> text: action.text, >>> completed: false >>> } >>> ] >>> }; >>> case TOGGLE_TODO => { >>> ...state, >>> todos: state.todos.map((todo, index) => (index === >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>> }; >>> default => state; >>> } >>> >>> >>> >>> _______________________________________________ >>> 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/20190225/b9bae645/attachment-0001.html>
Jordan,
Thanks for taking time to read and provide thoughts.
I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression.
This is unmaintainable --
const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99;
This is maintainable, but is less than ideal:
let x;
switch (v) { case "foo": x = 1; break; case "bar": x = 3; break; case "baz": x = 6; break; default: x = 99; break; }
Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable.
let x;
case (v) { when "foo" -> x = 1; when "bar" -> x = 3; when "baz" -> x = 6; when v -> x = 99; }
Let's try do expressions, I'll leave people's thoughts to themselves.
const x = do { if (v === "foo") { 1; } else if (v === "bar") { 3; } else if (v === "baz") { 6; } else { 99; } }
Or as another do expression variant:
const x = do { switch (v) { case "foo": 1; break; case "bar": 3; break; case "baz": 6; break; default: 99; break; } }
And as I'm thinking about switch expressions:
const x = switch (v) { case "foo" => 1; case "bar" => 3; case "baz" => 6; default => 99; }
What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept
where you have a state machine and need it switch based on the current state and evaluate to the new state.
const nextState = switch (currentState) { case ... => }
Jordan, Thanks for taking time to read and provide thoughts. I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. This is unmaintainable -- const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; This is maintainable, but is less than ideal: let x; switch (v) { case "foo": x = 1; break; case "bar": x = 3; break; case "baz": x = 6; break; default: x = 99; break; } Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. let x; case (v) { when "foo" -> x = 1; when "bar" -> x = 3; when "baz" -> x = 6; when v -> x = 99; } Let's try do expressions, I'll leave people's thoughts to themselves. const x = do { if (v === "foo") { 1; } else if (v === "bar") { 3; } else if (v === "baz") { 6; } else { 99; } } Or as another do expression variant: const x = do { switch (v) { case "foo": 1; break; case "bar": 3; break; case "baz": 6; break; default: 99; break; } } And as I'm thinking about switch expressions: const x = switch (v) { case "foo" => 1; case "bar" => 3; case "baz" => 6; default => 99; } What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. const nextState = switch (currentState) { case ... => } On 2/25/19 4:00 PM, Jordan Harband wrote: > Pattern Matching is still at stage 1; so there's not really any > permanent decisions that have been made - the repo theoretically > should contain rationales for decisions up to this point. > > I can speak for myself (as "not a champion" of that proposal, just a > fan) that any similarity to the reviled and terrible `switch` is > something I'll be pushing back against - I want a replacement that > lacks the footguns and pitfalls of `switch`, and that is easily > teachable and googleable as a different, distinct thing. > > On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com > <mailto:david at koblas.com>> wrote: > > Jordan, > > One question that I have lingering from pattern matching is why is > the syntax so different? IMHO it is still a switch statement with > a variation of the match on the case rather than a whole new > construct. > > Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > > --David > > > On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com > <mailto:ljharb at gmail.com>> wrote: > >> Additionally, https://github.com/tc39/proposal-pattern-matching - >> switch statements are something I hope we'll soon be able to >> relegate to the dustbin of history. >> >> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com >> <mailto:david at koblas.com>> wrote: >> >> I quite aware that it’s covered in do expressions. Personally >> I find do expressions non-JavaScript in style and it’s also >> not necessarily going to make it into the language. >> >> Hence why I wanted to put out there the idea of switch >> expressions. >> >> --David >> >> >> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com >> <mailto:blueshuk2 at gmail.com>> wrote: >> >>> Hi, >>> >>> This would be covered by do expressions >>> <https://github.com/tc39/proposal-do-expressions>. You could >>> just do: >>> >>> ```js >>> const category = do { >>> switch (...) { >>> ... >>> }; >>> }; >>> ``` >>> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas >>> <david at koblas.com <mailto:david at koblas.com>> wrote: >>> >>> After looking at a bunch of code in our system noted >>> that there are many >>> cases where our code base has a pattern similar to this: >>> >>> let category = data.category; >>> >>> if (category === undefined) { >>> // Even if Tax is not enabled, we have defaults >>> for incomeCode >>> switch (session.merchant.settings.tax.incomeCode) { >>> case TaxIncomeCode.RENTS_14: >>> category = PaymentCategory.RENT; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> } >>> } >>> >>> I also bumped into a block of go code that also >>> implemented similar >>> patterns, which really demonstrated to me that there >>> while you could go >>> crazy with triary nesting there should be a better way. >>> Looked at the >>> pattern matching proposal and while could possibly help >>> looked like it >>> was overkill for the typical use case that I'm seeing. >>> The most relevant >>> example I noted was switch expressions from Java. When >>> applied to this >>> problem really create a simple result: >>> >>> const category = data.category || switch >>> (setting.incomeCode) { >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>> PaymentCategory.ROYALTIES; >>> case >>> TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>> PaymentCategory.SERVICES; >>> default => PaymentCategory.OTHER; >>> } >>> >>> Note; the instead of using the '->' as Java, continue to >>> use => and with >>> the understanding that the right hand side is >>> fundamentally function. >>> So similar things to this are natural, note this >>> proposal should remove >>> "fall through" breaks and allow for multiple cases as such. >>> >>> const quarter = switch (foo) { >>> case "Jan", "Feb", "Mar" => "Q1"; >>> case "Apr", "May", "Jun" => "Q2"; >>> case "Jul", "Aug", "Sep" => "Q3"; >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>> default => { throw new Error("Invalid Month") }; >>> } >>> >>> Also compared this to the do expression proposal, it >>> also provides a >>> substantial simplification, but in a way that is more >>> consistent with >>> the existing language. In one of their examples they >>> provide an example >>> of the Redux reducer >>> https://redux.js.org/basics/reducers#splitting-reducers >>> -- this would be >>> a switch expression implementation. >>> >>> function todoApp(state = initialState, action) => >>> switch >>> (action.type) { >>> case SET_VISIBILITY_FILTER => { ...state, >>> visibilityFilter: >>> action.filter }; >>> case ADD_TODO => { >>> ...state, todos: [ >>> ...state.todos, >>> { >>> text: action.text, >>> completed: false >>> } >>> ] >>> }; >>> case TOGGLE_TODO => { >>> ...state, >>> todos: state.todos.map((todo, index) => >>> (index === >>> action.index) ? { ...todo, completed: !todo.completed } >>> : todo) >>> }; >>> default => state; >>> } >>> >>> >>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >> https://mail.mozilla.org/listinfo/es-discuss >> On 2/25/19 3:42 PM, David Koblas wrote: > Jordan, > > One question that I have lingering from pattern matching is why is the > syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > > Is there somewhere I can find a bit of discussion about the history of > the syntax decisions? > > --David > > > On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com > <mailto:ljharb at gmail.com>> wrote: > >> Additionally, https://github.com/tc39/proposal-pattern-matching - >> switch statements are something I hope we'll soon be able to relegate >> to the dustbin of history. >> >> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com >> <mailto:david at koblas.com>> wrote: >> >> I quite aware that it’s covered in do expressions. Personally I >> find do expressions non-JavaScript in style and it’s also not >> necessarily going to make it into the language. >> >> Hence why I wanted to put out there the idea of switch expressions. >> >> --David >> >> >> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com >> <mailto:blueshuk2 at gmail.com>> wrote: >> >>> Hi, >>> >>> This would be covered by do expressions >>> <https://github.com/tc39/proposal-do-expressions>. You could >>> just do: >>> >>> ```js >>> const category = do { >>> switch (...) { >>> ... >>> }; >>> }; >>> ``` >>> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com >>> <mailto:david at koblas.com>> wrote: >>> >>> After looking at a bunch of code in our system noted that >>> there are many >>> cases where our code base has a pattern similar to this: >>> >>> let category = data.category; >>> >>> if (category === undefined) { >>> // Even if Tax is not enabled, we have defaults for >>> incomeCode >>> switch (session.merchant.settings.tax.incomeCode) { >>> case TaxIncomeCode.RENTS_14: >>> category = PaymentCategory.RENT; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> } >>> } >>> >>> I also bumped into a block of go code that also implemented >>> similar >>> patterns, which really demonstrated to me that there while >>> you could go >>> crazy with triary nesting there should be a better way. >>> Looked at the >>> pattern matching proposal and while could possibly help >>> looked like it >>> was overkill for the typical use case that I'm seeing. The >>> most relevant >>> example I noted was switch expressions from Java. When >>> applied to this >>> problem really create a simple result: >>> >>> const category = data.category || switch >>> (setting.incomeCode) { >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>> PaymentCategory.ROYALTIES; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>> PaymentCategory.SERVICES; >>> default => PaymentCategory.OTHER; >>> } >>> >>> Note; the instead of using the '->' as Java, continue to use >>> => and with >>> the understanding that the right hand side is fundamentally >>> function. >>> So similar things to this are natural, note this proposal >>> should remove >>> "fall through" breaks and allow for multiple cases as such. >>> >>> const quarter = switch (foo) { >>> case "Jan", "Feb", "Mar" => "Q1"; >>> case "Apr", "May", "Jun" => "Q2"; >>> case "Jul", "Aug", "Sep" => "Q3"; >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>> default => { throw new Error("Invalid Month") }; >>> } >>> >>> Also compared this to the do expression proposal, it also >>> provides a >>> substantial simplification, but in a way that is more >>> consistent with >>> the existing language. In one of their examples they >>> provide an example >>> of the Redux reducer >>> https://redux.js.org/basics/reducers#splitting-reducers -- >>> this would be >>> a switch expression implementation. >>> >>> function todoApp(state = initialState, action) => switch >>> (action.type) { >>> case SET_VISIBILITY_FILTER => { ...state, >>> visibilityFilter: >>> action.filter }; >>> case ADD_TODO => { >>> ...state, todos: [ >>> ...state.todos, >>> { >>> text: action.text, >>> completed: false >>> } >>> ] >>> }; >>> case TOGGLE_TODO => { >>> ...state, >>> todos: state.todos.map((todo, index) => (index === >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>> }; >>> default => state; >>> } >>> >>> >>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org <mailto: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/20190226/89412436/attachment-0001.html>
You're not alone in wanting pattern matching to be expression-based:
tc39/proposal-pattern-matching#116
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
You're not alone in wanting pattern matching to be expression-based: https://github.com/tc39/proposal-pattern-matching/issues/116 ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: > > Jordan, > > Thanks for taking time to read and provide thoughts. > > I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. > > This is unmaintainable -- > > const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > > This is maintainable, but is less than ideal: > > let x; > > switch (v) { > case "foo": > x = 1; > break; > case "bar": > x = 3; > break; > case "baz": > x = 6; > break; > default: > x = 99; > break; > } > > Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. > > let x; > > case (v) { > when "foo" -> x = 1; > when "bar" -> x = 3; > when "baz" -> x = 6; > when v -> x = 99; > } > > Let's try do expressions, I'll leave people's thoughts to themselves. > > const x = do { > if (v === "foo") { 1; } > else if (v === "bar") { 3; } > else if (v === "baz") { 6; } > else { 99; } > } > > Or as another do expression variant: > > const x = do { > switch (v) { > case "foo": 1; break; > case "bar": 3; break; > case "baz": 6; break; > default: 99; break; > } > } > > And as I'm thinking about switch expressions: > > const x = switch (v) { > case "foo" => 1; > case "bar" => 3; > case "baz" => 6; > default => 99; > } > > What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. > > const nextState = switch (currentState) { > case ... => > } > > > > On 2/25/19 4:00 PM, Jordan Harband wrote: > > Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. > > I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. > > On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> Jordan, >> >> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> --David >> >> >> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>> >>> Hence why I wanted to put out there the idea of switch expressions. >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>> >>> Hi, >>> >>> This would be covered by do expressions. You could just do: >>> >>> ```js >>> const category = do { >>> switch (...) { >>> ... >>> }; >>> }; >>> ``` >>> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>> >>>> After looking at a bunch of code in our system noted that there are many >>>> cases where our code base has a pattern similar to this: >>>> >>>> let category = data.category; >>>> >>>> if (category === undefined) { >>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>> switch (session.merchant.settings.tax.incomeCode) { >>>> case TaxIncomeCode.RENTS_14: >>>> category = PaymentCategory.RENT; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> } >>>> } >>>> >>>> I also bumped into a block of go code that also implemented similar >>>> patterns, which really demonstrated to me that there while you could go >>>> crazy with triary nesting there should be a better way. Looked at the >>>> pattern matching proposal and while could possibly help looked like it >>>> was overkill for the typical use case that I'm seeing. The most relevant >>>> example I noted was switch expressions from Java. When applied to this >>>> problem really create a simple result: >>>> >>>> const category = data.category || switch (setting.incomeCode) { >>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>> PaymentCategory.ROYALTIES; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>> PaymentCategory.SERVICES; >>>> default => PaymentCategory.OTHER; >>>> } >>>> >>>> Note; the instead of using the '->' as Java, continue to use => and with >>>> the understanding that the right hand side is fundamentally function. >>>> So similar things to this are natural, note this proposal should remove >>>> "fall through" breaks and allow for multiple cases as such. >>>> >>>> const quarter = switch (foo) { >>>> case "Jan", "Feb", "Mar" => "Q1"; >>>> case "Apr", "May", "Jun" => "Q2"; >>>> case "Jul", "Aug", "Sep" => "Q3"; >>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>> default => { throw new Error("Invalid Month") }; >>>> } >>>> >>>> Also compared this to the do expression proposal, it also provides a >>>> substantial simplification, but in a way that is more consistent with >>>> the existing language. In one of their examples they provide an example >>>> of the Redux reducer >>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>> a switch expression implementation. >>>> >>>> function todoApp(state = initialState, action) => switch >>>> (action.type) { >>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>> action.filter }; >>>> case ADD_TODO => { >>>> ...state, todos: [ >>>> ...state.todos, >>>> { >>>> text: action.text, >>>> completed: false >>>> } >>>> ] >>>> }; >>>> case TOGGLE_TODO => { >>>> ...state, >>>> todos: state.todos.map((todo, index) => (index === >>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>> }; >>>> default => state; >>>> } >>>> >>>> >>>> >>>> _______________________________________________ >>>> 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 > > On 2/25/19 3:42 PM, David Koblas wrote: > > Jordan, > > One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. > > Is there somewhere I can find a bit of discussion about the history of the syntax decisions? > > --David > > > On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > > Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. > > On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> Hence why I wanted to put out there the idea of switch expressions. >> >> --David >> >> >> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> Hi, >> >> This would be covered by do expressions. You could just do: >> >> ```js >> const category = do { >> switch (...) { >> ... >> }; >> }; >> ``` >> >> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>> >>> After looking at a bunch of code in our system noted that there are many >>> cases where our code base has a pattern similar to this: >>> >>> let category = data.category; >>> >>> if (category === undefined) { >>> // Even if Tax is not enabled, we have defaults for incomeCode >>> switch (session.merchant.settings.tax.incomeCode) { >>> case TaxIncomeCode.RENTS_14: >>> category = PaymentCategory.RENT; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> } >>> } >>> >>> I also bumped into a block of go code that also implemented similar >>> patterns, which really demonstrated to me that there while you could go >>> crazy with triary nesting there should be a better way. Looked at the >>> pattern matching proposal and while could possibly help looked like it >>> was overkill for the typical use case that I'm seeing. The most relevant >>> example I noted was switch expressions from Java. When applied to this >>> problem really create a simple result: >>> >>> const category = data.category || switch (setting.incomeCode) { >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>> PaymentCategory.ROYALTIES; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>> PaymentCategory.SERVICES; >>> default => PaymentCategory.OTHER; >>> } >>> >>> Note; the instead of using the '->' as Java, continue to use => and with >>> the understanding that the right hand side is fundamentally function. >>> So similar things to this are natural, note this proposal should remove >>> "fall through" breaks and allow for multiple cases as such. >>> >>> const quarter = switch (foo) { >>> case "Jan", "Feb", "Mar" => "Q1"; >>> case "Apr", "May", "Jun" => "Q2"; >>> case "Jul", "Aug", "Sep" => "Q3"; >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>> default => { throw new Error("Invalid Month") }; >>> } >>> >>> Also compared this to the do expression proposal, it also provides a >>> substantial simplification, but in a way that is more consistent with >>> the existing language. In one of their examples they provide an example >>> of the Redux reducer >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>> a switch expression implementation. >>> >>> function todoApp(state = initialState, action) => switch >>> (action.type) { >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>> action.filter }; >>> case ADD_TODO => { >>> ...state, todos: [ >>> ...state.todos, >>> { >>> text: action.text, >>> completed: false >>> } >>> ] >>> }; >>> case TOGGLE_TODO => { >>> ...state, >>> todos: state.todos.map((todo, index) => (index === >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>> }; >>> default => state; >>> } >>> >>> >>> >>> _______________________________________________ >>> 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 > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss
Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible.
Pull request available here -- babel/babel#9604
I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real.
Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. Pull request available here -- https://github.com/babel/babel/pull/9604 I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. Thanks On 2/26/19 2:40 PM, Isiah Meadows wrote: > You're not alone in wanting pattern matching to be expression-based: > > https://github.com/tc39/proposal-pattern-matching/issues/116 > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> Jordan, >> >> Thanks for taking time to read and provide thoughts. >> >> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >> This is unmaintainable -- >> >> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> This is maintainable, but is less than ideal: >> >> let x; >> >> switch (v) { >> case "foo": >> x = 1; >> break; >> case "bar": >> x = 3; >> break; >> case "baz": >> x = 6; >> break; >> default: >> x = 99; >> break; >> } >> >> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >> let x; >> >> case (v) { >> when "foo" -> x = 1; >> when "bar" -> x = 3; >> when "baz" -> x = 6; >> when v -> x = 99; >> } >> >> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> const x = do { >> if (v === "foo") { 1; } >> else if (v === "bar") { 3; } >> else if (v === "baz") { 6; } >> else { 99; } >> } >> >> Or as another do expression variant: >> >> const x = do { >> switch (v) { >> case "foo": 1; break; >> case "bar": 3; break; >> case "baz": 6; break; >> default: 99; break; >> } >> } >> >> And as I'm thinking about switch expressions: >> >> const x = switch (v) { >> case "foo" => 1; >> case "bar" => 3; >> case "baz" => 6; >> default => 99; >> } >> >> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >> const nextState = switch (currentState) { >> case ... => >> } >> >> >> >> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >>> Jordan, >>> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >>> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >>> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>>> >>>> Hence why I wanted to put out there the idea of switch expressions. >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>>> >>>> Hi, >>>> >>>> This would be covered by do expressions. You could just do: >>>> >>>> ```js >>>> const category = do { >>>> switch (...) { >>>> ... >>>> }; >>>> }; >>>> ``` >>>> >>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>>> After looking at a bunch of code in our system noted that there are many >>>>> cases where our code base has a pattern similar to this: >>>>> >>>>> let category = data.category; >>>>> >>>>> if (category === undefined) { >>>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>>> switch (session.merchant.settings.tax.incomeCode) { >>>>> case TaxIncomeCode.RENTS_14: >>>>> category = PaymentCategory.RENT; >>>>> break; >>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>> category = PaymentCategory.SERVICES; >>>>> break; >>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>> category = PaymentCategory.SERVICES; >>>>> break; >>>>> } >>>>> } >>>>> >>>>> I also bumped into a block of go code that also implemented similar >>>>> patterns, which really demonstrated to me that there while you could go >>>>> crazy with triary nesting there should be a better way. Looked at the >>>>> pattern matching proposal and while could possibly help looked like it >>>>> was overkill for the typical use case that I'm seeing. The most relevant >>>>> example I noted was switch expressions from Java. When applied to this >>>>> problem really create a simple result: >>>>> >>>>> const category = data.category || switch (setting.incomeCode) { >>>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>>> PaymentCategory.ROYALTIES; >>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>>> PaymentCategory.SERVICES; >>>>> default => PaymentCategory.OTHER; >>>>> } >>>>> >>>>> Note; the instead of using the '->' as Java, continue to use => and with >>>>> the understanding that the right hand side is fundamentally function. >>>>> So similar things to this are natural, note this proposal should remove >>>>> "fall through" breaks and allow for multiple cases as such. >>>>> >>>>> const quarter = switch (foo) { >>>>> case "Jan", "Feb", "Mar" => "Q1"; >>>>> case "Apr", "May", "Jun" => "Q2"; >>>>> case "Jul", "Aug", "Sep" => "Q3"; >>>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>>> default => { throw new Error("Invalid Month") }; >>>>> } >>>>> >>>>> Also compared this to the do expression proposal, it also provides a >>>>> substantial simplification, but in a way that is more consistent with >>>>> the existing language. In one of their examples they provide an example >>>>> of the Redux reducer >>>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>>> a switch expression implementation. >>>>> >>>>> function todoApp(state = initialState, action) => switch >>>>> (action.type) { >>>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>>> action.filter }; >>>>> case ADD_TODO => { >>>>> ...state, todos: [ >>>>> ...state.todos, >>>>> { >>>>> text: action.text, >>>>> completed: false >>>>> } >>>>> ] >>>>> }; >>>>> case TOGGLE_TODO => { >>>>> ...state, >>>>> todos: state.todos.map((todo, index) => (index === >>>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>>> }; >>>>> default => state; >>>>> } >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> 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 >> On 2/25/19 3:42 PM, David Koblas wrote: >> >> Jordan, >> >> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> --David >> >> >> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>> >>> Hence why I wanted to put out there the idea of switch expressions. >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>> >>> Hi, >>> >>> This would be covered by do expressions. You could just do: >>> >>> ```js >>> const category = do { >>> switch (...) { >>> ... >>> }; >>> }; >>> ``` >>> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>> After looking at a bunch of code in our system noted that there are many >>>> cases where our code base has a pattern similar to this: >>>> >>>> let category = data.category; >>>> >>>> if (category === undefined) { >>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>> switch (session.merchant.settings.tax.incomeCode) { >>>> case TaxIncomeCode.RENTS_14: >>>> category = PaymentCategory.RENT; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> } >>>> } >>>> >>>> I also bumped into a block of go code that also implemented similar >>>> patterns, which really demonstrated to me that there while you could go >>>> crazy with triary nesting there should be a better way. Looked at the >>>> pattern matching proposal and while could possibly help looked like it >>>> was overkill for the typical use case that I'm seeing. The most relevant >>>> example I noted was switch expressions from Java. When applied to this >>>> problem really create a simple result: >>>> >>>> const category = data.category || switch (setting.incomeCode) { >>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>> PaymentCategory.ROYALTIES; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>> PaymentCategory.SERVICES; >>>> default => PaymentCategory.OTHER; >>>> } >>>> >>>> Note; the instead of using the '->' as Java, continue to use => and with >>>> the understanding that the right hand side is fundamentally function. >>>> So similar things to this are natural, note this proposal should remove >>>> "fall through" breaks and allow for multiple cases as such. >>>> >>>> const quarter = switch (foo) { >>>> case "Jan", "Feb", "Mar" => "Q1"; >>>> case "Apr", "May", "Jun" => "Q2"; >>>> case "Jul", "Aug", "Sep" => "Q3"; >>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>> default => { throw new Error("Invalid Month") }; >>>> } >>>> >>>> Also compared this to the do expression proposal, it also provides a >>>> substantial simplification, but in a way that is more consistent with >>>> the existing language. In one of their examples they provide an example >>>> of the Redux reducer >>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>> a switch expression implementation. >>>> >>>> function todoApp(state = initialState, action) => switch >>>> (action.type) { >>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>> action.filter }; >>>> case ADD_TODO => { >>>> ...state, todos: [ >>>> ...state.todos, >>>> { >>>> text: action.text, >>>> completed: false >>>> } >>>> ] >>>> }; >>>> case TOGGLE_TODO => { >>>> ...state, >>>> todos: state.todos.map((todo, index) => (index === >>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>> }; >>>> default => state; >>>> } >>>> >>>> >>>> >>>> _______________________________________________ >>>> 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 >> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss
> This is unmaintainable -- > > const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: ```javascript /*jslint*/ "use strict"; const v = "foo"; const x = ( v === "foo" ? 1 : v === "bar" ? 3 : v === "baz" ? 6 : 99 ); ``` here's another example from real-world production-code, where switch-expressions probably wouldn't help: ```javascript $ node -e ' /*jslint devel*/ "use strict"; function renderRecent(date) { /* * this function will render <date> to "xxx ago" */ date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; return ( !Number.isFinite(date) ? "" : date < 60 ? date + " sec ago" : date < 3600 ? Math.round(date / 60) + " min ago" : date < 86400 ? Math.round(date / 3600) + " hr ago" : date < 129600 ? "1 day ago" : Math.round(date / 86400) + " days ago" ); } console.log(renderRecent(new Date().toISOString())); // "0 sec ago" console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" ' 0 sec ago 10 sec ago 5 min ago 18 min ago 2 hr ago 16 days ago 365 days ago $ ``` > On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: > > Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. > > Pull request available here -- https://github.com/babel/babel/pull/9604 > > I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. > > Thanks > > On 2/26/19 2:40 PM, Isiah Meadows wrote: >> You're not alone in wanting pattern matching to be expression-based: >> >> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >>> Jordan, >>> >>> Thanks for taking time to read and provide thoughts. >>> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >>> >>> This is unmaintainable -- >>> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >>> >>> This is maintainable, but is less than ideal: >>> >>> let x; >>> >>> switch (v) { >>> case "foo": >>> x = 1; >>> break; >>> case "bar": >>> x = 3; >>> break; >>> case "baz": >>> x = 6; >>> break; >>> default: >>> x = 99; >>> break; >>> } >>> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >>> >>> let x; >>> >>> case (v) { >>> when "foo" -> x = 1; >>> when "bar" -> x = 3; >>> when "baz" -> x = 6; >>> when v -> x = 99; >>> } >>> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >>> >>> const x = do { >>> if (v === "foo") { 1; } >>> else if (v === "bar") { 3; } >>> else if (v === "baz") { 6; } >>> else { 99; } >>> } >>> >>> Or as another do expression variant: >>> >>> const x = do { >>> switch (v) { >>> case "foo": 1; break; >>> case "bar": 3; break; >>> case "baz": 6; break; >>> default: 99; break; >>> } >>> } >>> >>> And as I'm thinking about switch expressions: >>> >>> const x = switch (v) { >>> case "foo" => 1; >>> case "bar" => 3; >>> case "baz" => 6; >>> default => 99; >>> } >>> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >>> >>> const nextState = switch (currentState) { >>> case ... => >>> } >>> >>> >>> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >>> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >>> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >>> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >>>> Jordan, >>>> >>>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >>>> >>>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>>> >>>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >>>> >>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>>>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>>>> >>>>> Hence why I wanted to put out there the idea of switch expressions. >>>>> >>>>> --David >>>>> >>>>> >>>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>>>> >>>>> Hi, >>>>> >>>>> This would be covered by do expressions. You could just do: >>>>> >>>>> ```js >>>>> const category = do { >>>>> switch (...) { >>>>> ... >>>>> }; >>>>> }; >>>>> ``` >>>>> >>>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>>>> After looking at a bunch of code in our system noted that there are many >>>>>> cases where our code base has a pattern similar to this: >>>>>> >>>>>> let category = data.category; >>>>>> >>>>>> if (category === undefined) { >>>>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>>>> switch (session.merchant.settings.tax.incomeCode) { >>>>>> case TaxIncomeCode.RENTS_14: >>>>>> category = PaymentCategory.RENT; >>>>>> break; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>> category = PaymentCategory.SERVICES; >>>>>> break; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>> category = PaymentCategory.SERVICES; >>>>>> break; >>>>>> } >>>>>> } >>>>>> >>>>>> I also bumped into a block of go code that also implemented similar >>>>>> patterns, which really demonstrated to me that there while you could go >>>>>> crazy with triary nesting there should be a better way. Looked at the >>>>>> pattern matching proposal and while could possibly help looked like it >>>>>> was overkill for the typical use case that I'm seeing. The most relevant >>>>>> example I noted was switch expressions from Java. When applied to this >>>>>> problem really create a simple result: >>>>>> >>>>>> const category = data.category || switch (setting.incomeCode) { >>>>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>>>> PaymentCategory.ROYALTIES; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>>>> PaymentCategory.SERVICES; >>>>>> default => PaymentCategory.OTHER; >>>>>> } >>>>>> >>>>>> Note; the instead of using the '->' as Java, continue to use => and with >>>>>> the understanding that the right hand side is fundamentally function. >>>>>> So similar things to this are natural, note this proposal should remove >>>>>> "fall through" breaks and allow for multiple cases as such. >>>>>> >>>>>> const quarter = switch (foo) { >>>>>> case "Jan", "Feb", "Mar" => "Q1"; >>>>>> case "Apr", "May", "Jun" => "Q2"; >>>>>> case "Jul", "Aug", "Sep" => "Q3"; >>>>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>>>> default => { throw new Error("Invalid Month") }; >>>>>> } >>>>>> >>>>>> Also compared this to the do expression proposal, it also provides a >>>>>> substantial simplification, but in a way that is more consistent with >>>>>> the existing language. In one of their examples they provide an example >>>>>> of the Redux reducer >>>>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>>>> a switch expression implementation. >>>>>> >>>>>> function todoApp(state = initialState, action) => switch >>>>>> (action.type) { >>>>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>>>> action.filter }; >>>>>> case ADD_TODO => { >>>>>> ...state, todos: [ >>>>>> ...state.todos, >>>>>> { >>>>>> text: action.text, >>>>>> completed: false >>>>>> } >>>>>> ] >>>>>> }; >>>>>> case TOGGLE_TODO => { >>>>>> ...state, >>>>>> todos: state.todos.map((todo, index) => (index === >>>>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>>>> }; >>>>>> default => state; >>>>>> } >>>>>> >>>>>> >>>>>> >>>>>> _______________________________________________ >>>>>> 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 >>> On 2/25/19 3:42 PM, David Koblas wrote: >>> >>> Jordan, >>> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >>> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >>> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>>> >>>> Hence why I wanted to put out there the idea of switch expressions. >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>>> >>>> Hi, >>>> >>>> This would be covered by do expressions. You could just do: >>>> >>>> ```js >>>> const category = do { >>>> switch (...) { >>>> ... >>>> }; >>>> }; >>>> ``` >>>> >>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>>> After looking at a bunch of code in our system noted that there are many >>>>> cases where our code base has a pattern similar to this: >>>>> >>>>> let category = data.category; >>>>> >>>>> if (category === undefined) { >>>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>>> switch (session.merchant.settings.tax.incomeCode) { >>>>> case TaxIncomeCode.RENTS_14: >>>>> category = PaymentCategory.RENT; >>>>> break; >>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>> category = PaymentCategory.SERVICES; >>>>> break; >>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>> category = PaymentCategory.SERVICES; >>>>> break; >>>>> } >>>>> } >>>>> >>>>> I also bumped into a block of go code that also implemented similar >>>>> patterns, which really demonstrated to me that there while you could go >>>>> crazy with triary nesting there should be a better way. Looked at the >>>>> pattern matching proposal and while could possibly help looked like it >>>>> was overkill for the typical use case that I'm seeing. The most relevant >>>>> example I noted was switch expressions from Java. When applied to this >>>>> problem really create a simple result: >>>>> >>>>> const category = data.category || switch (setting.incomeCode) { >>>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>>> PaymentCategory.ROYALTIES; >>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>>> PaymentCategory.SERVICES; >>>>> default => PaymentCategory.OTHER; >>>>> } >>>>> >>>>> Note; the instead of using the '->' as Java, continue to use => and with >>>>> the understanding that the right hand side is fundamentally function. >>>>> So similar things to this are natural, note this proposal should remove >>>>> "fall through" breaks and allow for multiple cases as such. >>>>> >>>>> const quarter = switch (foo) { >>>>> case "Jan", "Feb", "Mar" => "Q1"; >>>>> case "Apr", "May", "Jun" => "Q2"; >>>>> case "Jul", "Aug", "Sep" => "Q3"; >>>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>>> default => { throw new Error("Invalid Month") }; >>>>> } >>>>> >>>>> Also compared this to the do expression proposal, it also provides a >>>>> substantial simplification, but in a way that is more consistent with >>>>> the existing language. In one of their examples they provide an example >>>>> of the Redux reducer >>>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>>> a switch expression implementation. >>>>> >>>>> function todoApp(state = initialState, action) => switch >>>>> (action.type) { >>>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>>> action.filter }; >>>>> case ADD_TODO => { >>>>> ...state, todos: [ >>>>> ...state.todos, >>>>> { >>>>> text: action.text, >>>>> completed: false >>>>> } >>>>> ] >>>>> }; >>>>> case TOGGLE_TODO => { >>>>> ...state, >>>>> todos: state.todos.map((todo, index) => (index === >>>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>>> }; >>>>> default => state; >>>>> } >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> 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 >>> >>> _______________________________________________ >>> 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/20190228/c566848a/attachment-0001.html>
Isn't the best existing pattern an object literal?
const cases = { foo: ()=>1, bar: ()=>3, baz: ()=>6 } , x = cases[v] ? casesv : 99 ;
What does any proposal have that is better than this? With optional chaining feature:
const x = { foo: ()=>1, bar: ()=>3, baz: ()=>6 }[v]?.() || 99 ;
Do let me know your thoughts guys
Isn't the best existing pattern an object literal? const cases = { foo: ()=>1, bar: ()=>3, baz: ()=>6 } , x = cases[v] ? cases[v]() : 99 ; What does any proposal have that is better than this? With optional chaining feature: const x = { foo: ()=>1, bar: ()=>3, baz: ()=>6 }[v]?.() || 99 ; Do let me know your thoughts guys On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: > This is unmaintainable -- > > const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > > i feel proposed switch-expressions are no more readable/maintainable than > ternary-operators, if you follow jslint's style-guide. i'll like to see > more convincing evidence/use-case that they are better: > ```javascript > /*jslint*/ > "use strict"; > const v = "foo"; > const x = ( > v === "foo" > ? 1 > : v === "bar" > ? 3 > : v === "baz" > ? 6 > : 99 > ); > ``` > > here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > > ```javascript > $ node -e ' > /*jslint devel*/ > "use strict"; > function renderRecent(date) { > /* > * this function will render <date> to "xxx ago" > */ > date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * > 10; > return ( > !Number.isFinite(date) > ? "" > : date < 60 > ? date + " sec ago" > : date < 3600 > ? Math.round(date / 60) + " min ago" > : date < 86400 > ? Math.round(date / 3600) + " hr ago" > : date < 129600 > ? "1 day ago" > : Math.round(date / 86400) + " days ago" > ); > } > > console.log(renderRecent(new Date().toISOString())); // "0 sec ago" > console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" > console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" > console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" > console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" > ' > > 0 sec ago > 10 sec ago > 5 min ago > 18 min ago > 2 hr ago > 16 days ago > 365 days ago > > $ > ``` > > On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: > > Just for folks who might be interested, added a babel-plugin to see what > was involved in making this possible. > > Pull request available here -- https://github.com/babel/babel/pull/9604 > > I'm sure I'm missing a bunch of details, but would be interested in some > help in making this a bit more real. > > Thanks > > On 2/26/19 2:40 PM, Isiah Meadows wrote: > > You're not alone in wanting pattern matching to be expression-based: > > https://github.com/tc39/proposal-pattern-matching/issues/116 > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: > > Jordan, > > Thanks for taking time to read and provide thoughts. > > I just back and re-read the pattern matching proposal and it still fails > on the basic requirement of being an Expression not a Statement. The > problem that I see and want to address is the need to have something that > removes the need to chain trinary expressions together to have an > Expression. > > This is unmaintainable -- > > const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > > This is maintainable, but is less than ideal: > > let x; > > switch (v) { > case "foo": > x = 1; > break; > case "bar": > x = 3; > break; > case "baz": > x = 6; > break; > default: > x = 99; > break; > } > > Pattern matching does shorten the code, but you have a weird default case > and also still end up with a loose variable and since pattern matching is a > statement you still have a initially undefined variable. > > let x; > > case (v) { > when "foo" -> x = 1; > when "bar" -> x = 3; > when "baz" -> x = 6; > when v -> x = 99; > } > > Let's try do expressions, I'll leave people's thoughts to themselves. > > const x = do { > if (v === "foo") { 1; } > else if (v === "bar") { 3; } > else if (v === "baz") { 6; } > else { 99; } > } > > Or as another do expression variant: > > const x = do { > switch (v) { > case "foo": 1; break; > case "bar": 3; break; > case "baz": 6; break; > default: 99; break; > } > } > > And as I'm thinking about switch expressions: > > const x = switch (v) { > case "foo" => 1; > case "bar" => 3; > case "baz" => 6; > default => 99; > } > > What I really like is that it preserves all of the normal JavaScript > syntax with the small change that a switch is allowed in an expression > provided that all of the cases evaluate to expressions hence the use of the > '=>' as an indicator. Fundamentally this is a very basic concept where you > have a state machine and need it switch based on the current state and > evaluate to the new state. > > const nextState = switch (currentState) { > case ... => > } > > > > On 2/25/19 4:00 PM, Jordan Harband wrote: > > Pattern Matching is still at stage 1; so there's not really any permanent > decisions that have been made - the repo theoretically should contain > rationales for decisions up to this point. > > I can speak for myself (as "not a champion" of that proposal, just a fan) > that any similarity to the reviled and terrible `switch` is something I'll > be pushing back against - I want a replacement that lacks the footguns and > pitfalls of `switch`, and that is easily teachable and googleable as a > different, distinct thing. > > On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: > > Jordan, > > One question that I have lingering from pattern matching is why is the > syntax so different? IMHO it is still a switch statement with a variation > of the match on the case rather than a whole new construct. > > Is there somewhere I can find a bit of discussion about the history of the > syntax decisions? > > --David > > > On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > > Additionally, https://github.com/tc39/proposal-pattern-matching - switch > statements are something I hope we'll soon be able to relegate to the > dustbin of history. > > On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > > I quite aware that it’s covered in do expressions. Personally I find do > expressions non-JavaScript in style and it’s also not necessarily going to > make it into the language. > > Hence why I wanted to put out there the idea of switch expressions. > > --David > > > On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > > Hi, > > This would be covered by do expressions. You could just do: > > ```js > const category = do { > switch (...) { > ... > }; > }; > ``` > > On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: > > After looking at a bunch of code in our system noted that there are many > cases where our code base has a pattern similar to this: > > let category = data.category; > > if (category === undefined) { > // Even if Tax is not enabled, we have defaults for incomeCode > switch (session.merchant.settings.tax.incomeCode) { > case TaxIncomeCode.RENTS_14: > category = PaymentCategory.RENT; > break; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > category = PaymentCategory.SERVICES; > break; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > category = PaymentCategory.SERVICES; > break; > } > } > > I also bumped into a block of go code that also implemented similar > patterns, which really demonstrated to me that there while you could go > crazy with triary nesting there should be a better way. Looked at the > pattern matching proposal and while could possibly help looked like it > was overkill for the typical use case that I'm seeing. The most relevant > example I noted was switch expressions from Java. When applied to this > problem really create a simple result: > > const category = data.category || switch (setting.incomeCode) { > case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > PaymentCategory.ROYALTIES; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > PaymentCategory.SERVICES; > default => PaymentCategory.OTHER; > } > > Note; the instead of using the '->' as Java, continue to use => and with > the understanding that the right hand side is fundamentally function. > So similar things to this are natural, note this proposal should remove > "fall through" breaks and allow for multiple cases as such. > > const quarter = switch (foo) { > case "Jan", "Feb", "Mar" => "Q1"; > case "Apr", "May", "Jun" => "Q2"; > case "Jul", "Aug", "Sep" => "Q3"; > case "Oct", "Nov", "Dec" => { return "Q4" }; > default => { throw new Error("Invalid Month") }; > } > > Also compared this to the do expression proposal, it also provides a > substantial simplification, but in a way that is more consistent with > the existing language. In one of their examples they provide an example > of the Redux reducer > https://redux.js.org/basics/reducers#splitting-reducers -- this would be > a switch expression implementation. > > function todoApp(state = initialState, action) => switch > (action.type) { > case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > action.filter }; > case ADD_TODO => { > ...state, todos: [ > ...state.todos, > { > text: action.text, > completed: false > } > ] > }; > case TOGGLE_TODO => { > ...state, > todos: state.todos.map((todo, index) => (index === > action.index) ? { ...todo, completed: !todo.completed } : todo) > }; > default => state; > } > > > > _______________________________________________ > 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 > > On 2/25/19 3:42 PM, David Koblas wrote: > > Jordan, > > One question that I have lingering from pattern matching is why is the > syntax so different? IMHO it is still a switch statement with a variation > of the match on the case rather than a whole new construct. > > Is there somewhere I can find a bit of discussion about the history of the > syntax decisions? > > --David > > > On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > > Additionally, https://github.com/tc39/proposal-pattern-matching - switch > statements are something I hope we'll soon be able to relegate to the > dustbin of history. > > On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > > I quite aware that it’s covered in do expressions. Personally I find do > expressions non-JavaScript in style and it’s also not necessarily going to > make it into the language. > > Hence why I wanted to put out there the idea of switch expressions. > > --David > > > On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > > Hi, > > This would be covered by do expressions. You could just do: > > ```js > const category = do { > switch (...) { > ... > }; > }; > ``` > > On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: > > After looking at a bunch of code in our system noted that there are many > cases where our code base has a pattern similar to this: > > let category = data.category; > > if (category === undefined) { > // Even if Tax is not enabled, we have defaults for incomeCode > switch (session.merchant.settings.tax.incomeCode) { > case TaxIncomeCode.RENTS_14: > category = PaymentCategory.RENT; > break; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > category = PaymentCategory.SERVICES; > break; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > category = PaymentCategory.SERVICES; > break; > } > } > > I also bumped into a block of go code that also implemented similar > patterns, which really demonstrated to me that there while you could go > crazy with triary nesting there should be a better way. Looked at the > pattern matching proposal and while could possibly help looked like it > was overkill for the typical use case that I'm seeing. The most relevant > example I noted was switch expressions from Java. When applied to this > problem really create a simple result: > > const category = data.category || switch (setting.incomeCode) { > case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > PaymentCategory.ROYALTIES; > case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > PaymentCategory.SERVICES; > default => PaymentCategory.OTHER; > } > > Note; the instead of using the '->' as Java, continue to use => and with > the understanding that the right hand side is fundamentally function. > So similar things to this are natural, note this proposal should remove > "fall through" breaks and allow for multiple cases as such. > > const quarter = switch (foo) { > case "Jan", "Feb", "Mar" => "Q1"; > case "Apr", "May", "Jun" => "Q2"; > case "Jul", "Aug", "Sep" => "Q3"; > case "Oct", "Nov", "Dec" => { return "Q4" }; > default => { throw new Error("Invalid Month") }; > } > > Also compared this to the do expression proposal, it also provides a > substantial simplification, but in a way that is more consistent with > the existing language. In one of their examples they provide an example > of the Redux reducer > https://redux.js.org/basics/reducers#splitting-reducers -- this would be > a switch expression implementation. > > function todoApp(state = initialState, action) => switch > (action.type) { > case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > action.filter }; > case ADD_TODO => { > ...state, todos: [ > ...state.todos, > { > text: action.text, > completed: false > } > ] > }; > case TOGGLE_TODO => { > ...state, > todos: state.todos.map((todo, index) => (index === > action.index) ? { ...todo, completed: !todo.completed } : todo) > }; > default => state; > } > > > > _______________________________________________ > 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 > > _______________________________________________ > 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/20190228/ecd5c5cc/attachment-0001.html>
Kai,
Thanks for the feedback and the real world example.
Most of my examples have been focused on simple cases. The full example is something that would support the following style:
const food = switch (animal) {
case Animal.DOG, Animal.CAT => {
// larger block expression
// which spans multiple lines
return "dry food";
}
case Animal.TIGER, Animal.LION, Animal.CHEETA => {
// larger block expression
// which spans multiple lines
return "fresh meat";
}
case Animal.ELEPHANT => "hay";
default => { throw new Error("Unsuppored Animal"); };
};
The basic things that I'm focusing on are:
- The evaluation of the switch is an expression that returns a value
- The case statement doesn't fall through and supports multiple descriminators
- Use arrow expressions to return values, these can be either simple values or blocks returning a value.
- Syntax that is quickly recognizable with existing JS patterns
Kai, Thanks for the feedback and the real world example. Most of my examples have been focused on simple cases. The full example is something that would support the following style: ``` const food = switch (animal) { case Animal.DOG, Animal.CAT => { // larger block expression // which spans multiple lines return "dry food"; } case Animal.TIGER, Animal.LION, Animal.CHEETA => { // larger block expression // which spans multiple lines return "fresh meat"; } case Animal.ELEPHANT => "hay"; default => { throw new Error("Unsuppored Animal"); }; }; ``` The basic things that I'm focusing on are: * The evaluation of the switch is an expression that returns a value * The case statement doesn't fall through and supports multiple descriminators * Use arrow expressions to return values, these can be either simple values or blocks returning a value. * Syntax that is quickly recognizable with existing JS patterns On 2/28/19 1:04 AM, kai zhu wrote: >> >> This is unmaintainable -- >> >> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> > i feel proposed switch-expressions are no more readable/maintainable > than ternary-operators, if you follow jslint's style-guide. i'll like > to see more convincing evidence/use-case that they are better: > > ```javascript > /*jslint*/ > "use strict"; > const v = "foo"; > const x = ( > v === "foo" > ? 1 > : v === "bar" > ? 3 > : v === "baz" > ? 6 > : 99 > ); > ``` > > here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > > ```javascript > $ node -e ' > /*jslint devel*/ > "use strict"; > function renderRecent(date) { > /* > * this function will render <date> to "xxx ago" > */ > date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; > return ( > !Number.isFinite(date) > ? "" > : date < 60 > ? date + " sec ago" > : date < 3600 > ? Math.round(date / 60) + " min ago" > : date < 86400 > ? Math.round(date / 3600) + " hr ago" > : date < 129600 > ? "1 day ago" > : Math.round(date / 86400) + " days ago" > ); > } > > console.log(renderRecent(new Date().toISOString())); // "0 sec ago" > console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" > console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" > console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" > console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" > ' > > 0 sec ago > 10 sec ago > 5 min ago > 18 min ago > 2 hr ago > 16 days ago > 365 days ago > > $ > ``` > >> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com >> <mailto:david at koblas.com>> wrote: >> >> Just for folks who might be interested, added a babel-plugin to see >> what was involved in making this possible. >> >> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> I'm sure I'm missing a bunch of details, but would be interested in >> some help in making this a bit more real. >> >> Thanks >> >> On 2/26/19 2:40 PM, Isiah Meadows wrote: >>> You're not alone in wanting pattern matching to be expression-based: >>> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com >>> www.isiahmeadows.com >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com >>> www.isiahmeadows.com >>> >>> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >>>> Jordan, >>>> >>>> Thanks for taking time to read and provide thoughts. >>>> >>>> I just back and re-read the pattern matching proposal and it still >>>> fails on the basic requirement of being an Expression not a >>>> Statement. The problem that I see and want to address is the need >>>> to have something that removes the need to chain trinary >>>> expressions together to have an Expression. >>>> >>>> This is unmaintainable -- >>>> >>>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >>>> >>>> This is maintainable, but is less than ideal: >>>> >>>> let x; >>>> >>>> switch (v) { >>>> case "foo": >>>> x = 1; >>>> break; >>>> case "bar": >>>> x = 3; >>>> break; >>>> case "baz": >>>> x = 6; >>>> break; >>>> default: >>>> x = 99; >>>> break; >>>> } >>>> >>>> Pattern matching does shorten the code, but you have a weird >>>> default case and also still end up with a loose variable and since >>>> pattern matching is a statement you still have a initially >>>> undefined variable. >>>> >>>> let x; >>>> >>>> case (v) { >>>> when "foo" -> x = 1; >>>> when "bar" -> x = 3; >>>> when "baz" -> x = 6; >>>> when v -> x = 99; >>>> } >>>> >>>> Let's try do expressions, I'll leave people's thoughts to themselves. >>>> >>>> const x = do { >>>> if (v === "foo") { 1; } >>>> else if (v === "bar") { 3; } >>>> else if (v === "baz") { 6; } >>>> else { 99; } >>>> } >>>> >>>> Or as another do expression variant: >>>> >>>> const x = do { >>>> switch (v) { >>>> case "foo": 1; break; >>>> case "bar": 3; break; >>>> case "baz": 6; break; >>>> default: 99; break; >>>> } >>>> } >>>> >>>> And as I'm thinking about switch expressions: >>>> >>>> const x = switch (v) { >>>> case "foo" => 1; >>>> case "bar" => 3; >>>> case "baz" => 6; >>>> default => 99; >>>> } >>>> >>>> What I really like is that it preserves all of the normal >>>> JavaScript syntax with the small change that a switch is allowed in >>>> an expression provided that all of the cases evaluate to >>>> expressions hence the use of the '=>' as an indicator. >>>> Fundamentally this is a very basic concept where you have a state >>>> machine and need it switch based on the current state and evaluate >>>> to the new state. >>>> >>>> const nextState = switch (currentState) { >>>> case ... => >>>> } >>>> >>>> >>>> >>>> On 2/25/19 4:00 PM, Jordan Harband wrote: >>>> >>>> Pattern Matching is still at stage 1; so there's not really any >>>> permanent decisions that have been made - the repo theoretically >>>> should contain rationales for decisions up to this point. >>>> >>>> I can speak for myself (as "not a champion" of that proposal, just >>>> a fan) that any similarity to the reviled and terrible `switch` is >>>> something I'll be pushing back against - I want a replacement that >>>> lacks the footguns and pitfalls of `switch`, and that is easily >>>> teachable and googleable as a different, distinct thing. >>>> >>>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >>>>> Jordan, >>>>> >>>>> One question that I have lingering from pattern matching is why is >>>>> the syntax so different? IMHO it is still a switch statement with >>>>> a variation of the match on the case rather than a whole new >>>>> construct. >>>>> >>>>> Is there somewhere I can find a bit of discussion about the >>>>> history of the syntax decisions? >>>>> >>>>> --David >>>>> >>>>> >>>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>>>> >>>>> Additionally, https://github.com/tc39/proposal-pattern-matching - >>>>> switch statements are something I hope we'll soon be able to >>>>> relegate to the dustbin of history. >>>>> >>>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>>>>> I quite aware that it’s covered in do expressions. Personally I >>>>>> find do expressions non-JavaScript in style and it’s also not >>>>>> necessarily going to make it into the language. >>>>>> >>>>>> Hence why I wanted to put out there the idea of switch expressions. >>>>>> >>>>>> --David >>>>>> >>>>>> >>>>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>>>>> >>>>>> Hi, >>>>>> >>>>>> This would be covered by do expressions. You could just do: >>>>>> >>>>>> ```js >>>>>> const category = do { >>>>>> switch (...) { >>>>>> ... >>>>>> }; >>>>>> }; >>>>>> ``` >>>>>> >>>>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> >>>>>> wrote: >>>>>>> After looking at a bunch of code in our system noted that there >>>>>>> are many >>>>>>> cases where our code base has a pattern similar to this: >>>>>>> >>>>>>> let category = data.category; >>>>>>> >>>>>>> if (category === undefined) { >>>>>>> // Even if Tax is not enabled, we have defaults for >>>>>>> incomeCode >>>>>>> switch (session.merchant.settings.tax.incomeCode) { >>>>>>> case TaxIncomeCode.RENTS_14: >>>>>>> category = PaymentCategory.RENT; >>>>>>> break; >>>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>>> category = PaymentCategory.SERVICES; >>>>>>> break; >>>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>>> category = PaymentCategory.SERVICES; >>>>>>> break; >>>>>>> } >>>>>>> } >>>>>>> >>>>>>> I also bumped into a block of go code that also implemented similar >>>>>>> patterns, which really demonstrated to me that there while you >>>>>>> could go >>>>>>> crazy with triary nesting there should be a better way. Looked >>>>>>> at the >>>>>>> pattern matching proposal and while could possibly help looked >>>>>>> like it >>>>>>> was overkill for the typical use case that I'm seeing. The most >>>>>>> relevant >>>>>>> example I noted was switch expressions from Java. When applied >>>>>>> to this >>>>>>> problem really create a simple result: >>>>>>> >>>>>>> const category = data.category || switch (setting.incomeCode) { >>>>>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>>>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>>>>> PaymentCategory.ROYALTIES; >>>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>>>>> PaymentCategory.SERVICES; >>>>>>> default => PaymentCategory.OTHER; >>>>>>> } >>>>>>> >>>>>>> Note; the instead of using the '->' as Java, continue to use => >>>>>>> and with >>>>>>> the understanding that the right hand side is fundamentally >>>>>>> function. >>>>>>> So similar things to this are natural, note this proposal should >>>>>>> remove >>>>>>> "fall through" breaks and allow for multiple cases as such. >>>>>>> >>>>>>> const quarter = switch (foo) { >>>>>>> case "Jan", "Feb", "Mar" => "Q1"; >>>>>>> case "Apr", "May", "Jun" => "Q2"; >>>>>>> case "Jul", "Aug", "Sep" => "Q3"; >>>>>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>>>>> default => { throw new Error("Invalid Month") }; >>>>>>> } >>>>>>> >>>>>>> Also compared this to the do expression proposal, it also provides a >>>>>>> substantial simplification, but in a way that is more consistent >>>>>>> with >>>>>>> the existing language. In one of their examples they provide an >>>>>>> example >>>>>>> of the Redux reducer >>>>>>> https://redux.js.org/basics/reducers#splitting-reducers -- this >>>>>>> would be >>>>>>> a switch expression implementation. >>>>>>> >>>>>>> function todoApp(state = initialState, action) => switch >>>>>>> (action.type) { >>>>>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>>>>> action.filter }; >>>>>>> case ADD_TODO => { >>>>>>> ...state, todos: [ >>>>>>> ...state.todos, >>>>>>> { >>>>>>> text: action.text, >>>>>>> completed: false >>>>>>> } >>>>>>> ] >>>>>>> }; >>>>>>> case TOGGLE_TODO => { >>>>>>> ...state, >>>>>>> todos: state.todos.map((todo, index) => (index === >>>>>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>>>>> }; >>>>>>> default => state; >>>>>>> } >>>>>>> >>>>>>> >>>>>>> >>>>>>> _______________________________________________ >>>>>>> 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 >>>> On 2/25/19 3:42 PM, David Koblas wrote: >>>> >>>> Jordan, >>>> >>>> One question that I have lingering from pattern matching is why is >>>> the syntax so different? IMHO it is still a switch statement with >>>> a variation of the match on the case rather than a whole new construct. >>>> >>>> Is there somewhere I can find a bit of discussion about the history >>>> of the syntax decisions? >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>>> >>>> Additionally, https://github.com/tc39/proposal-pattern-matching - >>>> switch statements are something I hope we'll soon be able to >>>> relegate to the dustbin of history. >>>> >>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>>>> I quite aware that it’s covered in do expressions. Personally I >>>>> find do expressions non-JavaScript in style and it’s also not >>>>> necessarily going to make it into the language. >>>>> >>>>> Hence why I wanted to put out there the idea of switch expressions. >>>>> >>>>> --David >>>>> >>>>> >>>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>>>> >>>>> Hi, >>>>> >>>>> This would be covered by do expressions. You could just do: >>>>> >>>>> ```js >>>>> const category = do { >>>>> switch (...) { >>>>> ... >>>>> }; >>>>> }; >>>>> ``` >>>>> >>>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> >>>>> wrote: >>>>>> After looking at a bunch of code in our system noted that there >>>>>> are many >>>>>> cases where our code base has a pattern similar to this: >>>>>> >>>>>> let category = data.category; >>>>>> >>>>>> if (category === undefined) { >>>>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>>>> switch (session.merchant.settings.tax.incomeCode) { >>>>>> case TaxIncomeCode.RENTS_14: >>>>>> category = PaymentCategory.RENT; >>>>>> break; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>> category = PaymentCategory.SERVICES; >>>>>> break; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>> category = PaymentCategory.SERVICES; >>>>>> break; >>>>>> } >>>>>> } >>>>>> >>>>>> I also bumped into a block of go code that also implemented similar >>>>>> patterns, which really demonstrated to me that there while you >>>>>> could go >>>>>> crazy with triary nesting there should be a better way. Looked >>>>>> at the >>>>>> pattern matching proposal and while could possibly help looked >>>>>> like it >>>>>> was overkill for the typical use case that I'm seeing. The most >>>>>> relevant >>>>>> example I noted was switch expressions from Java. When applied >>>>>> to this >>>>>> problem really create a simple result: >>>>>> >>>>>> const category = data.category || switch (setting.incomeCode) { >>>>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>>>> PaymentCategory.ROYALTIES; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>>>> PaymentCategory.SERVICES; >>>>>> default => PaymentCategory.OTHER; >>>>>> } >>>>>> >>>>>> Note; the instead of using the '->' as Java, continue to use => >>>>>> and with >>>>>> the understanding that the right hand side is fundamentally function. >>>>>> So similar things to this are natural, note this proposal should >>>>>> remove >>>>>> "fall through" breaks and allow for multiple cases as such. >>>>>> >>>>>> const quarter = switch (foo) { >>>>>> case "Jan", "Feb", "Mar" => "Q1"; >>>>>> case "Apr", "May", "Jun" => "Q2"; >>>>>> case "Jul", "Aug", "Sep" => "Q3"; >>>>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>>>> default => { throw new Error("Invalid Month") }; >>>>>> } >>>>>> >>>>>> Also compared this to the do expression proposal, it also provides a >>>>>> substantial simplification, but in a way that is more consistent with >>>>>> the existing language. In one of their examples they provide an >>>>>> example >>>>>> of the Redux reducer >>>>>> https://redux.js.org/basics/reducers#splitting-reducers -- this >>>>>> would be >>>>>> a switch expression implementation. >>>>>> >>>>>> function todoApp(state = initialState, action) => switch >>>>>> (action.type) { >>>>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>>>> action.filter }; >>>>>> case ADD_TODO => { >>>>>> ...state, todos: [ >>>>>> ...state.todos, >>>>>> { >>>>>> text: action.text, >>>>>> completed: false >>>>>> } >>>>>> ] >>>>>> }; >>>>>> case TOGGLE_TODO => { >>>>>> ...state, >>>>>> todos: state.todos.map((todo, index) => (index === >>>>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>>>> }; >>>>>> default => state; >>>>>> } >>>>>> >>>>>> >>>>>> >>>>>> _______________________________________________ >>>>>> 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 >>>> >>>> _______________________________________________ >>>> 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 <mailto: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/20190228/45512e48/attachment-0001.html>
Naveen,
Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example:
switch (animal) {
case Animal.DOG, Animal.CAT => {
// larger block expression
// which spans multiple lines
return "dry food";
}
case Animal.TIGER, Animal.LION, Animal.CHEETA => {
// larger block expression
// which spans multiple lines
return "fresh meat";
}
case Animal.ELEPHANT => "hay";
default => { throw new Error("Unsupported Animal"); };
}
While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this.
function houseAnimal() {
// larger block expression
// which spans multiple lines
return "dry food";
}
function wildCatFood() {
// larger block expression
// which spans multiple lines
return "fresh meat";
}
const cases = {
[Animal.DOG]: houseAnimal,
[Animal.CAT]: houseAnimal,
[Animal.LION]: wildCatFood,
[Animal.TIGER]: wildCatFood,
[Animal.CHEETA]: wildCatFood,
}
const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})();
As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach.
Naveen, Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: ``` switch (animal) { case Animal.DOG, Animal.CAT => { // larger block expression // which spans multiple lines return "dry food"; } case Animal.TIGER, Animal.LION, Animal.CHEETA => { // larger block expression // which spans multiple lines return "fresh meat"; } case Animal.ELEPHANT => "hay"; default => { throw new Error("Unsupported Animal"); }; } ``` While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. ``` function houseAnimal() { // larger block expression // which spans multiple lines return "dry food"; } function wildCatFood() { // larger block expression // which spans multiple lines return "fresh meat"; } const cases = { [Animal.DOG]: houseAnimal, [Animal.CAT]: houseAnimal, [Animal.LION]: wildCatFood, [Animal.TIGER]: wildCatFood, [Animal.CHEETA]: wildCatFood, } const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); ``` As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. On 2/28/19 4:37 AM, Naveen Chawla wrote: > Isn't the best existing pattern an object literal? > > const > cases = > { > foo: ()=>1, > bar: ()=>3, > baz: ()=>6 > } > , > x = > cases[v] ? > cases[v]() : > 99 > ; > > What does any proposal have that is better than this? With optional > chaining feature: > > const > x = > { > foo: ()=>1, > bar: ()=>3, > baz: ()=>6 > }[v]?.() > || > 99 > ; > > Do let me know your thoughts guys > > > On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com > <mailto:kaizhu256 at gmail.com>> wrote: > >> This is unmaintainable -- >> >> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 >> : 99; >> > i feel proposed switch-expressions are no more > readable/maintainable than ternary-operators, if you follow > jslint's style-guide. i'll like to see more convincing > evidence/use-case that they are better: > > ```javascript > /*jslint*/ > "use strict"; > const v = "foo"; > const x = ( > v === "foo" > ? 1 > : v === "bar" > ? 3 > : v === "baz" > ? 6 > : 99 > ); > ``` > > here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > > ```javascript > $ node -e ' > /*jslint devel*/ > "use strict"; > function renderRecent(date) { > /* > * this function will render <date> to "xxx ago" > */ > date = Math.ceil((Date.now() - new Date(date).getTime()) * > 0.0001) * 10; > return ( > !Number.isFinite(date) > ? "" > : date < 60 > ? date + " sec ago" > : date < 3600 > ? Math.round(date / 60) + " min ago" > : date < 86400 > ? Math.round(date / 3600) + " hr ago" > : date < 129600 > ? "1 day ago" > : Math.round(date / 86400) + " days ago" > ); > } > > console.log(renderRecent(new Date().toISOString())); // "0 sec ago" > console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" > console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" > console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" > console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" > ' > > 0 sec ago > 10 sec ago > 5 min ago > 18 min ago > 2 hr ago > 16 days ago > 365 days ago > > $ > ``` > >> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com >> <mailto:david at koblas.com>> wrote: >> >> Just for folks who might be interested, added a babel-plugin to >> see what was involved in making this possible. >> >> Pull request available here -- >> https://github.com/babel/babel/pull/9604 >> >> I'm sure I'm missing a bunch of details, but would be interested >> in some help in making this a bit more real. >> >> Thanks >> >> On 2/26/19 2:40 PM, Isiah Meadows wrote: >>> You're not alone in wanting pattern matching to be expression-based: >>> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com <mailto:contact at isiahmeadows.com> >>> www.isiahmeadows.com <http://www.isiahmeadows.com> >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com <mailto:contact at isiahmeadows.com> >>> www.isiahmeadows.com <http://www.isiahmeadows.com> >>> >>> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com >>> <mailto:david at koblas.com>> wrote: >>>> Jordan, >>>> >>>> Thanks for taking time to read and provide thoughts. >>>> >>>> I just back and re-read the pattern matching proposal and it >>>> still fails on the basic requirement of being an Expression not >>>> a Statement. The problem that I see and want to address is the >>>> need to have something that removes the need to chain trinary >>>> expressions together to have an Expression. >>>> >>>> This is unmaintainable -- >>>> >>>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? >>>> 6 : 99; >>>> >>>> This is maintainable, but is less than ideal: >>>> >>>> let x; >>>> >>>> switch (v) { >>>> case "foo": >>>> x = 1; >>>> break; >>>> case "bar": >>>> x = 3; >>>> break; >>>> case "baz": >>>> x = 6; >>>> break; >>>> default: >>>> x = 99; >>>> break; >>>> } >>>> >>>> Pattern matching does shorten the code, but you have a weird >>>> default case and also still end up with a loose variable and >>>> since pattern matching is a statement you still have a >>>> initially undefined variable. >>>> >>>> let x; >>>> >>>> case (v) { >>>> when "foo" -> x = 1; >>>> when "bar" -> x = 3; >>>> when "baz" -> x = 6; >>>> when v -> x = 99; >>>> } >>>> >>>> Let's try do expressions, I'll leave people's thoughts to >>>> themselves. >>>> >>>> const x = do { >>>> if (v === "foo") { 1; } >>>> else if (v === "bar") { 3; } >>>> else if (v === "baz") { 6; } >>>> else { 99; } >>>> } >>>> >>>> Or as another do expression variant: >>>> >>>> const x = do { >>>> switch (v) { >>>> case "foo": 1; break; >>>> case "bar": 3; break; >>>> case "baz": 6; break; >>>> default: 99; break; >>>> } >>>> } >>>> >>>> And as I'm thinking about switch expressions: >>>> >>>> const x = switch (v) { >>>> case "foo" => 1; >>>> case "bar" => 3; >>>> case "baz" => 6; >>>> default => 99; >>>> } >>>> >>>> What I really like is that it preserves all of the normal >>>> JavaScript syntax with the small change that a switch is >>>> allowed in an expression provided that all of the cases >>>> evaluate to expressions hence the use of the '=>' as an >>>> indicator. Fundamentally this is a very basic concept where >>>> you have a state machine and need it switch based on the >>>> current state and evaluate to the new state. >>>> >>>> const nextState = switch (currentState) { >>>> case ... => >>>> } >>>> >>>> >>>> >>>> On 2/25/19 4:00 PM, Jordan Harband wrote: >>>> >>>> Pattern Matching is still at stage 1; so there's not really any >>>> permanent decisions that have been made - the repo >>>> theoretically should contain rationales for decisions up to >>>> this point. >>>> >>>> I can speak for myself (as "not a champion" of that proposal, >>>> just a fan) that any similarity to the reviled and terrible >>>> `switch` is something I'll be pushing back against - I want a >>>> replacement that lacks the footguns and pitfalls of `switch`, >>>> and that is easily teachable and googleable as a different, >>>> distinct thing. >>>> >>>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com >>>> <mailto:david at koblas.com>> wrote: >>>>> Jordan, >>>>> >>>>> One question that I have lingering from pattern matching is >>>>> why is the syntax so different? IMHO it is still a switch >>>>> statement with a variation of the match on the case rather >>>>> than a whole new construct. >>>>> >>>>> Is there somewhere I can find a bit of discussion about the >>>>> history of the syntax decisions? >>>>> >>>>> --David >>>>> >>>>> >>>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com >>>>> <mailto:ljharb at gmail.com>> wrote: >>>>> >>>>> Additionally, >>>>> https://github.com/tc39/proposal-pattern-matching - switch >>>>> statements are something I hope we'll soon be able to relegate >>>>> to the dustbin of history. >>>>> >>>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com >>>>> <mailto:david at koblas.com>> wrote: >>>>>> I quite aware that it’s covered in do expressions. Personally >>>>>> I find do expressions non-JavaScript in style and it’s also >>>>>> not necessarily going to make it into the language. >>>>>> >>>>>> Hence why I wanted to put out there the idea of switch >>>>>> expressions. >>>>>> >>>>>> --David >>>>>> >>>>>> >>>>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com >>>>>> <mailto:blueshuk2 at gmail.com>> wrote: >>>>>> >>>>>> Hi, >>>>>> >>>>>> This would be covered by do expressions. You could just do: >>>>>> >>>>>> ```js >>>>>> const category = do { >>>>>> switch (...) { >>>>>> ... >>>>>> }; >>>>>> }; >>>>>> ``` >>>>>> >>>>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas >>>>>> <david at koblas.com <mailto:david at koblas.com>> wrote: >>>>>>> After looking at a bunch of code in our system noted that >>>>>>> there are many >>>>>>> cases where our code base has a pattern similar to this: >>>>>>> >>>>>>> let category = data.category; >>>>>>> >>>>>>> if (category === undefined) { >>>>>>> // Even if Tax is not enabled, we have defaults for >>>>>>> incomeCode >>>>>>> switch (session.merchant.settings.tax.incomeCode) { >>>>>>> case TaxIncomeCode.RENTS_14: >>>>>>> category = PaymentCategory.RENT; >>>>>>> break; >>>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>>> category = PaymentCategory.SERVICES; >>>>>>> break; >>>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>>> category = PaymentCategory.SERVICES; >>>>>>> break; >>>>>>> } >>>>>>> } >>>>>>> >>>>>>> I also bumped into a block of go code that also implemented >>>>>>> similar >>>>>>> patterns, which really demonstrated to me that there while >>>>>>> you could go >>>>>>> crazy with triary nesting there should be a better way. >>>>>>> Looked at the >>>>>>> pattern matching proposal and while could possibly help >>>>>>> looked like it >>>>>>> was overkill for the typical use case that I'm seeing. The >>>>>>> most relevant >>>>>>> example I noted was switch expressions from Java. When >>>>>>> applied to this >>>>>>> problem really create a simple result: >>>>>>> >>>>>>> const category = data.category || switch >>>>>>> (setting.incomeCode) { >>>>>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>>>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>>>>> PaymentCategory.ROYALTIES; >>>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>>>>> PaymentCategory.SERVICES; >>>>>>> default => PaymentCategory.OTHER; >>>>>>> } >>>>>>> >>>>>>> Note; the instead of using the '->' as Java, continue to use >>>>>>> => and with >>>>>>> the understanding that the right hand side is fundamentally >>>>>>> function. >>>>>>> So similar things to this are natural, note this proposal >>>>>>> should remove >>>>>>> "fall through" breaks and allow for multiple cases as such. >>>>>>> >>>>>>> const quarter = switch (foo) { >>>>>>> case "Jan", "Feb", "Mar" => "Q1"; >>>>>>> case "Apr", "May", "Jun" => "Q2"; >>>>>>> case "Jul", "Aug", "Sep" => "Q3"; >>>>>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>>>>> default => { throw new Error("Invalid Month") }; >>>>>>> } >>>>>>> >>>>>>> Also compared this to the do expression proposal, it also >>>>>>> provides a >>>>>>> substantial simplification, but in a way that is more >>>>>>> consistent with >>>>>>> the existing language. In one of their examples they >>>>>>> provide an example >>>>>>> of the Redux reducer >>>>>>> https://redux.js.org/basics/reducers#splitting-reducers -- >>>>>>> this would be >>>>>>> a switch expression implementation. >>>>>>> >>>>>>> function todoApp(state = initialState, action) => switch >>>>>>> (action.type) { >>>>>>> case SET_VISIBILITY_FILTER => { ...state, >>>>>>> visibilityFilter: >>>>>>> action.filter }; >>>>>>> case ADD_TODO => { >>>>>>> ...state, todos: [ >>>>>>> ...state.todos, >>>>>>> { >>>>>>> text: action.text, >>>>>>> completed: false >>>>>>> } >>>>>>> ] >>>>>>> }; >>>>>>> case TOGGLE_TODO => { >>>>>>> ...state, >>>>>>> todos: state.todos.map((todo, index) => (index === >>>>>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>>>>> }; >>>>>>> default => state; >>>>>>> } >>>>>>> >>>>>>> >>>>>>> >>>>>>> _______________________________________________ >>>>>>> es-discuss mailing list >>>>>>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>> _______________________________________________ >>>>>> es-discuss mailing list >>>>>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>> On 2/25/19 3:42 PM, David Koblas wrote: >>>> >>>> Jordan, >>>> >>>> One question that I have lingering from pattern matching is why >>>> is the syntax so different? IMHO it is still a switch statement >>>> with a variation of the match on the case rather than a whole >>>> new construct. >>>> >>>> Is there somewhere I can find a bit of discussion about the >>>> history of the syntax decisions? >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com >>>> <mailto:ljharb at gmail.com>> wrote: >>>> >>>> Additionally, https://github.com/tc39/proposal-pattern-matching >>>> - switch statements are something I hope we'll soon be able to >>>> relegate to the dustbin of history. >>>> >>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com >>>> <mailto:david at koblas.com>> wrote: >>>>> I quite aware that it’s covered in do expressions. Personally >>>>> I find do expressions non-JavaScript in style and it’s also >>>>> not necessarily going to make it into the language. >>>>> >>>>> Hence why I wanted to put out there the idea of switch >>>>> expressions. >>>>> >>>>> --David >>>>> >>>>> >>>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com >>>>> <mailto:blueshuk2 at gmail.com>> wrote: >>>>> >>>>> Hi, >>>>> >>>>> This would be covered by do expressions. You could just do: >>>>> >>>>> ```js >>>>> const category = do { >>>>> switch (...) { >>>>> ... >>>>> }; >>>>> }; >>>>> ``` >>>>> >>>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas >>>>> <david at koblas.com <mailto:david at koblas.com>> wrote: >>>>>> After looking at a bunch of code in our system noted that >>>>>> there are many >>>>>> cases where our code base has a pattern similar to this: >>>>>> >>>>>> let category = data.category; >>>>>> >>>>>> if (category === undefined) { >>>>>> // Even if Tax is not enabled, we have defaults for >>>>>> incomeCode >>>>>> switch (session.merchant.settings.tax.incomeCode) { >>>>>> case TaxIncomeCode.RENTS_14: >>>>>> category = PaymentCategory.RENT; >>>>>> break; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>> category = PaymentCategory.SERVICES; >>>>>> break; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>>>> category = PaymentCategory.SERVICES; >>>>>> break; >>>>>> } >>>>>> } >>>>>> >>>>>> I also bumped into a block of go code that also implemented >>>>>> similar >>>>>> patterns, which really demonstrated to me that there while >>>>>> you could go >>>>>> crazy with triary nesting there should be a better way. >>>>>> Looked at the >>>>>> pattern matching proposal and while could possibly help >>>>>> looked like it >>>>>> was overkill for the typical use case that I'm seeing. The >>>>>> most relevant >>>>>> example I noted was switch expressions from Java. When >>>>>> applied to this >>>>>> problem really create a simple result: >>>>>> >>>>>> const category = data.category || switch >>>>>> (setting.incomeCode) { >>>>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>>>> PaymentCategory.ROYALTIES; >>>>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>>>> PaymentCategory.SERVICES; >>>>>> default => PaymentCategory.OTHER; >>>>>> } >>>>>> >>>>>> Note; the instead of using the '->' as Java, continue to use >>>>>> => and with >>>>>> the understanding that the right hand side is fundamentally >>>>>> function. >>>>>> So similar things to this are natural, note this proposal >>>>>> should remove >>>>>> "fall through" breaks and allow for multiple cases as such. >>>>>> >>>>>> const quarter = switch (foo) { >>>>>> case "Jan", "Feb", "Mar" => "Q1"; >>>>>> case "Apr", "May", "Jun" => "Q2"; >>>>>> case "Jul", "Aug", "Sep" => "Q3"; >>>>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>>>> default => { throw new Error("Invalid Month") }; >>>>>> } >>>>>> >>>>>> Also compared this to the do expression proposal, it also >>>>>> provides a >>>>>> substantial simplification, but in a way that is more >>>>>> consistent with >>>>>> the existing language. In one of their examples they provide >>>>>> an example >>>>>> of the Redux reducer >>>>>> https://redux.js.org/basics/reducers#splitting-reducers -- >>>>>> this would be >>>>>> a switch expression implementation. >>>>>> >>>>>> function todoApp(state = initialState, action) => switch >>>>>> (action.type) { >>>>>> case SET_VISIBILITY_FILTER => { ...state, >>>>>> visibilityFilter: >>>>>> action.filter }; >>>>>> case ADD_TODO => { >>>>>> ...state, todos: [ >>>>>> ...state.todos, >>>>>> { >>>>>> text: action.text, >>>>>> completed: false >>>>>> } >>>>>> ] >>>>>> }; >>>>>> case TOGGLE_TODO => { >>>>>> ...state, >>>>>> todos: state.todos.map((todo, index) => (index === >>>>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>>>> }; >>>>>> default => state; >>>>>> } >>>>>> >>>>>> >>>>>> >>>>>> _______________________________________________ >>>>>> es-discuss mailing list >>>>>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>> _______________________________________________ >>>>> es-discuss mailing list >>>>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >>>> https://mail.mozilla.org/listinfo/es-discuss >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >> https://mail.mozilla.org/listinfo/es-discuss > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org <mailto: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/20190228/d37b376f/attachment-0001.html>
Your last example would, I think, be better served by classes and inheritance, than switch.
Dogs are house animals which are animals Cheetas are wild cats which are animals
Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately.
The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results.
Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want.
All thoughts on this are welcome. Do let me know
Hi David! Your last example would, I think, be better served by classes and inheritance, than switch. Dogs are house animals which are animals Cheetas are wild cats which are animals Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. All thoughts on this are welcome. Do let me know On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: > Naveen, > > Thanks for your observation. The example that I gave might have been too > simplistic, here's a more complete example: > > ``` > > switch (animal) { > case Animal.DOG, Animal.CAT => { > // larger block expression > // which spans multiple lines > > return "dry food"; > } > case Animal.TIGER, Animal.LION, Animal.CHEETA => { > // larger block expression > // which spans multiple lines > > return "fresh meat"; > } > case Animal.ELEPHANT => "hay"; > default => { throw new Error("Unsupported Animal"); }; > } > > ``` > > While you give examples that would totally work. Things that bother me > about the approach are, when taken to something more complex than a quick > value for value switch you end up with something that looks like this. > > ``` > > function houseAnimal() { > > // larger block expression // which spans multiple lines return "dry food"; } > > function wildCatFood() { > > // larger block expression // which spans multiple lines return "fresh meat"; } > > const cases = { [Animal.DOG]: houseAnimal, [Animal.CAT]: houseAnimal, [Animal.LION]: wildCatFood, [Animal.TIGER]: wildCatFood, [Animal.CHEETA]: wildCatFood, } const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); > > ``` > > As we all know once any language reaches a basic level of functionality > anything is possible. What I think is that JavaScript would benefit by > having a cleaner approach. > On 2/28/19 4:37 AM, Naveen Chawla wrote: > > Isn't the best existing pattern an object literal? > > const > cases = > { > foo: ()=>1, > bar: ()=>3, > baz: ()=>6 > } > , > x = > cases[v] ? > cases[v]() : > 99 > ; > > What does any proposal have that is better than this? With optional > chaining feature: > > const > x = > { > foo: ()=>1, > bar: ()=>3, > baz: ()=>6 > }[v]?.() > || > 99 > ; > > Do let me know your thoughts guys > > > On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: > >> This is unmaintainable -- >> >> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> i feel proposed switch-expressions are no more readable/maintainable than >> ternary-operators, if you follow jslint's style-guide. i'll like to see >> more convincing evidence/use-case that they are better: >> ```javascript >> /*jslint*/ >> "use strict"; >> const v = "foo"; >> const x = ( >> v === "foo" >> ? 1 >> : v === "bar" >> ? 3 >> : v === "baz" >> ? 6 >> : 99 >> ); >> ``` >> >> here's another example from real-world production-code, where >> switch-expressions probably wouldn't help: >> >> ```javascript >> $ node -e ' >> /*jslint devel*/ >> "use strict"; >> function renderRecent(date) { >> /* >> * this function will render <date> to "xxx ago" >> */ >> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * >> 10; >> return ( >> !Number.isFinite(date) >> ? "" >> : date < 60 >> ? date + " sec ago" >> : date < 3600 >> ? Math.round(date / 60) + " min ago" >> : date < 86400 >> ? Math.round(date / 3600) + " hr ago" >> : date < 129600 >> ? "1 day ago" >> : Math.round(date / 86400) + " days ago" >> ); >> } >> >> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> ' >> >> 0 sec ago >> 10 sec ago >> 5 min ago >> 18 min ago >> 2 hr ago >> 16 days ago >> 365 days ago >> >> $ >> ``` >> >> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >> Just for folks who might be interested, added a babel-plugin to see what >> was involved in making this possible. >> >> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> I'm sure I'm missing a bunch of details, but would be interested in some >> help in making this a bit more real. >> >> Thanks >> >> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >> You're not alone in wanting pattern matching to be expression-based: >> >> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >> Jordan, >> >> Thanks for taking time to read and provide thoughts. >> >> I just back and re-read the pattern matching proposal and it still fails >> on the basic requirement of being an Expression not a Statement. The >> problem that I see and want to address is the need to have something that >> removes the need to chain trinary expressions together to have an >> Expression. >> >> This is unmaintainable -- >> >> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> This is maintainable, but is less than ideal: >> >> let x; >> >> switch (v) { >> case "foo": >> x = 1; >> break; >> case "bar": >> x = 3; >> break; >> case "baz": >> x = 6; >> break; >> default: >> x = 99; >> break; >> } >> >> Pattern matching does shorten the code, but you have a weird default case >> and also still end up with a loose variable and since pattern matching is a >> statement you still have a initially undefined variable. >> >> let x; >> >> case (v) { >> when "foo" -> x = 1; >> when "bar" -> x = 3; >> when "baz" -> x = 6; >> when v -> x = 99; >> } >> >> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> const x = do { >> if (v === "foo") { 1; } >> else if (v === "bar") { 3; } >> else if (v === "baz") { 6; } >> else { 99; } >> } >> >> Or as another do expression variant: >> >> const x = do { >> switch (v) { >> case "foo": 1; break; >> case "bar": 3; break; >> case "baz": 6; break; >> default: 99; break; >> } >> } >> >> And as I'm thinking about switch expressions: >> >> const x = switch (v) { >> case "foo" => 1; >> case "bar" => 3; >> case "baz" => 6; >> default => 99; >> } >> >> What I really like is that it preserves all of the normal JavaScript >> syntax with the small change that a switch is allowed in an expression >> provided that all of the cases evaluate to expressions hence the use of the >> '=>' as an indicator. Fundamentally this is a very basic concept where you >> have a state machine and need it switch based on the current state and >> evaluate to the new state. >> >> const nextState = switch (currentState) { >> case ... => >> } >> >> >> >> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> Pattern Matching is still at stage 1; so there's not really any permanent >> decisions that have been made - the repo theoretically should contain >> rationales for decisions up to this point. >> >> I can speak for myself (as "not a champion" of that proposal, just a fan) >> that any similarity to the reviled and terrible `switch` is something I'll >> be pushing back against - I want a replacement that lacks the footguns and >> pitfalls of `switch`, and that is easily teachable and googleable as a >> different, distinct thing. >> >> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> Jordan, >> >> One question that I have lingering from pattern matching is why is the >> syntax so different? IMHO it is still a switch statement with a variation >> of the match on the case rather than a whole new construct. >> >> Is there somewhere I can find a bit of discussion about the history of >> the syntax decisions? >> >> --David >> >> >> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> Additionally, https://github.com/tc39/proposal-pattern-matching - switch >> statements are something I hope we'll soon be able to relegate to the >> dustbin of history. >> >> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> I quite aware that it’s covered in do expressions. Personally I find do >> expressions non-JavaScript in style and it’s also not necessarily going to >> make it into the language. >> >> Hence why I wanted to put out there the idea of switch expressions. >> >> --David >> >> >> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> Hi, >> >> This would be covered by do expressions. You could just do: >> >> ```js >> const category = do { >> switch (...) { >> ... >> }; >> }; >> ``` >> >> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> After looking at a bunch of code in our system noted that there are many >> cases where our code base has a pattern similar to this: >> >> let category = data.category; >> >> if (category === undefined) { >> // Even if Tax is not enabled, we have defaults for incomeCode >> switch (session.merchant.settings.tax.incomeCode) { >> case TaxIncomeCode.RENTS_14: >> category = PaymentCategory.RENT; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> } >> } >> >> I also bumped into a block of go code that also implemented similar >> patterns, which really demonstrated to me that there while you could go >> crazy with triary nesting there should be a better way. Looked at the >> pattern matching proposal and while could possibly help looked like it >> was overkill for the typical use case that I'm seeing. The most relevant >> example I noted was switch expressions from Java. When applied to this >> problem really create a simple result: >> >> const category = data.category || switch (setting.incomeCode) { >> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> PaymentCategory.ROYALTIES; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> PaymentCategory.SERVICES; >> default => PaymentCategory.OTHER; >> } >> >> Note; the instead of using the '->' as Java, continue to use => and with >> the understanding that the right hand side is fundamentally function. >> So similar things to this are natural, note this proposal should remove >> "fall through" breaks and allow for multiple cases as such. >> >> const quarter = switch (foo) { >> case "Jan", "Feb", "Mar" => "Q1"; >> case "Apr", "May", "Jun" => "Q2"; >> case "Jul", "Aug", "Sep" => "Q3"; >> case "Oct", "Nov", "Dec" => { return "Q4" }; >> default => { throw new Error("Invalid Month") }; >> } >> >> Also compared this to the do expression proposal, it also provides a >> substantial simplification, but in a way that is more consistent with >> the existing language. In one of their examples they provide an example >> of the Redux reducer >> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> a switch expression implementation. >> >> function todoApp(state = initialState, action) => switch >> (action.type) { >> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> action.filter }; >> case ADD_TODO => { >> ...state, todos: [ >> ...state.todos, >> { >> text: action.text, >> completed: false >> } >> ] >> }; >> case TOGGLE_TODO => { >> ...state, >> todos: state.todos.map((todo, index) => (index === >> action.index) ? { ...todo, completed: !todo.completed } : todo) >> }; >> default => state; >> } >> >> >> >> _______________________________________________ >> 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 >> >> On 2/25/19 3:42 PM, David Koblas wrote: >> >> Jordan, >> >> One question that I have lingering from pattern matching is why is the >> syntax so different? IMHO it is still a switch statement with a variation >> of the match on the case rather than a whole new construct. >> >> Is there somewhere I can find a bit of discussion about the history of >> the syntax decisions? >> >> --David >> >> >> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> Additionally, https://github.com/tc39/proposal-pattern-matching - switch >> statements are something I hope we'll soon be able to relegate to the >> dustbin of history. >> >> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> I quite aware that it’s covered in do expressions. Personally I find do >> expressions non-JavaScript in style and it’s also not necessarily going to >> make it into the language. >> >> Hence why I wanted to put out there the idea of switch expressions. >> >> --David >> >> >> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> Hi, >> >> This would be covered by do expressions. You could just do: >> >> ```js >> const category = do { >> switch (...) { >> ... >> }; >> }; >> ``` >> >> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> After looking at a bunch of code in our system noted that there are many >> cases where our code base has a pattern similar to this: >> >> let category = data.category; >> >> if (category === undefined) { >> // Even if Tax is not enabled, we have defaults for incomeCode >> switch (session.merchant.settings.tax.incomeCode) { >> case TaxIncomeCode.RENTS_14: >> category = PaymentCategory.RENT; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> category = PaymentCategory.SERVICES; >> break; >> } >> } >> >> I also bumped into a block of go code that also implemented similar >> patterns, which really demonstrated to me that there while you could go >> crazy with triary nesting there should be a better way. Looked at the >> pattern matching proposal and while could possibly help looked like it >> was overkill for the typical use case that I'm seeing. The most relevant >> example I noted was switch expressions from Java. When applied to this >> problem really create a simple result: >> >> const category = data.category || switch (setting.incomeCode) { >> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> PaymentCategory.ROYALTIES; >> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> PaymentCategory.SERVICES; >> default => PaymentCategory.OTHER; >> } >> >> Note; the instead of using the '->' as Java, continue to use => and with >> the understanding that the right hand side is fundamentally function. >> So similar things to this are natural, note this proposal should remove >> "fall through" breaks and allow for multiple cases as such. >> >> const quarter = switch (foo) { >> case "Jan", "Feb", "Mar" => "Q1"; >> case "Apr", "May", "Jun" => "Q2"; >> case "Jul", "Aug", "Sep" => "Q3"; >> case "Oct", "Nov", "Dec" => { return "Q4" }; >> default => { throw new Error("Invalid Month") }; >> } >> >> Also compared this to the do expression proposal, it also provides a >> substantial simplification, but in a way that is more consistent with >> the existing language. In one of their examples they provide an example >> of the Redux reducer >> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> a switch expression implementation. >> >> function todoApp(state = initialState, action) => switch >> (action.type) { >> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> action.filter }; >> case ADD_TODO => { >> ...state, todos: [ >> ...state.todos, >> { >> text: action.text, >> completed: false >> } >> ] >> }; >> case TOGGLE_TODO => { >> ...state, >> todos: state.todos.map((todo, index) => (index === >> action.index) ? { ...todo, completed: !todo.completed } : todo) >> }; >> default => state; >> } >> >> >> >> _______________________________________________ >> 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 >> >> _______________________________________________ >> 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/20190228/acf67d23/attachment-0001.html>
Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want.
Sometimes, this is actually desired, and most cases where I could've
used this, inheritance was not involved anywhere. Also, in
performance-sensitive contexts (like games, which heavily use
switch
/case
), method dispatch is far slower than a simple
switch
statement, so that pattern doesn't apply everywhere.
BTW, I prefer tc39/proposal-pattern-matching over this anyways - it covers more use cases and is all around more flexible, so I get more bang for the buck.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
> Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. Sometimes, this is actually *desired*, and most cases where I could've used this, inheritance was not involved *anywhere*. Also, in performance-sensitive contexts (like games, which *heavily* use `switch`/`case`), method dispatch is *far* slower than a simple `switch` statement, so that pattern doesn't apply everywhere. BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over this anyways - it covers more use cases and is all around more flexible, so I get more bang for the buck. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: > > Hi David! > > Your last example would, I think, be better served by classes and inheritance, than switch. > > Dogs are house animals which are animals > Cheetas are wild cats which are animals > > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. > > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. > > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. > > All thoughts on this are welcome. Do let me know > > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> Naveen, >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> ``` >> >> switch (animal) { >> case Animal.DOG, Animal.CAT => { >> // larger block expression >> // which spans multiple lines >> >> return "dry food"; >> } >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> // larger block expression >> // which spans multiple lines >> >> return "fresh meat"; >> } >> case Animal.ELEPHANT => "hay"; >> default => { throw new Error("Unsupported Animal"); }; >> } >> >> ``` >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> ``` >> >> function houseAnimal() { >> >> // larger block expression >> // which spans multiple lines >> >> return "dry food"; >> } >> >> function wildCatFood() { >> >> // larger block expression >> // which spans multiple lines >> >> return "fresh meat"; >> } >> >> >> const cases = { >> [Animal.DOG]: houseAnimal, >> [Animal.CAT]: houseAnimal, >> [Animal.LION]: wildCatFood, >> [Animal.TIGER]: wildCatFood, >> [Animal.CHEETA]: wildCatFood, >> } >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> ``` >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> Isn't the best existing pattern an object literal? >> >> const >> cases = >> { >> foo: ()=>1, >> bar: ()=>3, >> baz: ()=>6 >> } >> , >> x = >> cases[v] ? >> cases[v]() : >> 99 >> ; >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> const >> x = >> { >> foo: ()=>1, >> bar: ()=>3, >> baz: ()=>6 >> }[v]?.() >> || >> 99 >> ; >> >> Do let me know your thoughts guys >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >>> >>> This is unmaintainable -- >>> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >>> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >>> >>> ```javascript >>> /*jslint*/ >>> "use strict"; >>> const v = "foo"; >>> const x = ( >>> v === "foo" >>> ? 1 >>> : v === "bar" >>> ? 3 >>> : v === "baz" >>> ? 6 >>> : 99 >>> ); >>> ``` >>> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >>> >>> ```javascript >>> $ node -e ' >>> /*jslint devel*/ >>> "use strict"; >>> function renderRecent(date) { >>> /* >>> * this function will render <date> to "xxx ago" >>> */ >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >>> return ( >>> !Number.isFinite(date) >>> ? "" >>> : date < 60 >>> ? date + " sec ago" >>> : date < 3600 >>> ? Math.round(date / 60) + " min ago" >>> : date < 86400 >>> ? Math.round(date / 3600) + " hr ago" >>> : date < 129600 >>> ? "1 day ago" >>> : Math.round(date / 86400) + " days ago" >>> ); >>> } >>> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >>> ' >>> >>> 0 sec ago >>> 10 sec ago >>> 5 min ago >>> 18 min ago >>> 2 hr ago >>> 16 days ago >>> 365 days ago >>> >>> $ >>> ``` >>> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >>> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >>> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >>> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >>> >>> Thanks >>> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >>> >>> You're not alone in wanting pattern matching to be expression-based: >>> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com >>> www.isiahmeadows.com >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com >>> www.isiahmeadows.com >>> >>> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >>> >>> Jordan, >>> >>> Thanks for taking time to read and provide thoughts. >>> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >>> >>> This is unmaintainable -- >>> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >>> >>> This is maintainable, but is less than ideal: >>> >>> let x; >>> >>> switch (v) { >>> case "foo": >>> x = 1; >>> break; >>> case "bar": >>> x = 3; >>> break; >>> case "baz": >>> x = 6; >>> break; >>> default: >>> x = 99; >>> break; >>> } >>> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >>> >>> let x; >>> >>> case (v) { >>> when "foo" -> x = 1; >>> when "bar" -> x = 3; >>> when "baz" -> x = 6; >>> when v -> x = 99; >>> } >>> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >>> >>> const x = do { >>> if (v === "foo") { 1; } >>> else if (v === "bar") { 3; } >>> else if (v === "baz") { 6; } >>> else { 99; } >>> } >>> >>> Or as another do expression variant: >>> >>> const x = do { >>> switch (v) { >>> case "foo": 1; break; >>> case "bar": 3; break; >>> case "baz": 6; break; >>> default: 99; break; >>> } >>> } >>> >>> And as I'm thinking about switch expressions: >>> >>> const x = switch (v) { >>> case "foo" => 1; >>> case "bar" => 3; >>> case "baz" => 6; >>> default => 99; >>> } >>> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >>> >>> const nextState = switch (currentState) { >>> case ... => >>> } >>> >>> >>> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >>> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >>> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >>> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >>> >>> Jordan, >>> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >>> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >>> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>> >>> Hence why I wanted to put out there the idea of switch expressions. >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>> >>> Hi, >>> >>> This would be covered by do expressions. You could just do: >>> >>> ```js >>> const category = do { >>> switch (...) { >>> ... >>> }; >>> }; >>> ``` >>> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>> >>> After looking at a bunch of code in our system noted that there are many >>> cases where our code base has a pattern similar to this: >>> >>> let category = data.category; >>> >>> if (category === undefined) { >>> // Even if Tax is not enabled, we have defaults for incomeCode >>> switch (session.merchant.settings.tax.incomeCode) { >>> case TaxIncomeCode.RENTS_14: >>> category = PaymentCategory.RENT; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> } >>> } >>> >>> I also bumped into a block of go code that also implemented similar >>> patterns, which really demonstrated to me that there while you could go >>> crazy with triary nesting there should be a better way. Looked at the >>> pattern matching proposal and while could possibly help looked like it >>> was overkill for the typical use case that I'm seeing. The most relevant >>> example I noted was switch expressions from Java. When applied to this >>> problem really create a simple result: >>> >>> const category = data.category || switch (setting.incomeCode) { >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>> PaymentCategory.ROYALTIES; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>> PaymentCategory.SERVICES; >>> default => PaymentCategory.OTHER; >>> } >>> >>> Note; the instead of using the '->' as Java, continue to use => and with >>> the understanding that the right hand side is fundamentally function. >>> So similar things to this are natural, note this proposal should remove >>> "fall through" breaks and allow for multiple cases as such. >>> >>> const quarter = switch (foo) { >>> case "Jan", "Feb", "Mar" => "Q1"; >>> case "Apr", "May", "Jun" => "Q2"; >>> case "Jul", "Aug", "Sep" => "Q3"; >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>> default => { throw new Error("Invalid Month") }; >>> } >>> >>> Also compared this to the do expression proposal, it also provides a >>> substantial simplification, but in a way that is more consistent with >>> the existing language. In one of their examples they provide an example >>> of the Redux reducer >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>> a switch expression implementation. >>> >>> function todoApp(state = initialState, action) => switch >>> (action.type) { >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>> action.filter }; >>> case ADD_TODO => { >>> ...state, todos: [ >>> ...state.todos, >>> { >>> text: action.text, >>> completed: false >>> } >>> ] >>> }; >>> case TOGGLE_TODO => { >>> ...state, >>> todos: state.todos.map((todo, index) => (index === >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>> }; >>> default => state; >>> } >>> >>> >>> >>> _______________________________________________ >>> 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 >>> >>> On 2/25/19 3:42 PM, David Koblas wrote: >>> >>> Jordan, >>> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >>> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >>> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>> >>> Hence why I wanted to put out there the idea of switch expressions. >>> >>> --David >>> >>> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>> >>> Hi, >>> >>> This would be covered by do expressions. You could just do: >>> >>> ```js >>> const category = do { >>> switch (...) { >>> ... >>> }; >>> }; >>> ``` >>> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>> >>> After looking at a bunch of code in our system noted that there are many >>> cases where our code base has a pattern similar to this: >>> >>> let category = data.category; >>> >>> if (category === undefined) { >>> // Even if Tax is not enabled, we have defaults for incomeCode >>> switch (session.merchant.settings.tax.incomeCode) { >>> case TaxIncomeCode.RENTS_14: >>> category = PaymentCategory.RENT; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>> category = PaymentCategory.SERVICES; >>> break; >>> } >>> } >>> >>> I also bumped into a block of go code that also implemented similar >>> patterns, which really demonstrated to me that there while you could go >>> crazy with triary nesting there should be a better way. Looked at the >>> pattern matching proposal and while could possibly help looked like it >>> was overkill for the typical use case that I'm seeing. The most relevant >>> example I noted was switch expressions from Java. When applied to this >>> problem really create a simple result: >>> >>> const category = data.category || switch (setting.incomeCode) { >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>> PaymentCategory.ROYALTIES; >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>> PaymentCategory.SERVICES; >>> default => PaymentCategory.OTHER; >>> } >>> >>> Note; the instead of using the '->' as Java, continue to use => and with >>> the understanding that the right hand side is fundamentally function. >>> So similar things to this are natural, note this proposal should remove >>> "fall through" breaks and allow for multiple cases as such. >>> >>> const quarter = switch (foo) { >>> case "Jan", "Feb", "Mar" => "Q1"; >>> case "Apr", "May", "Jun" => "Q2"; >>> case "Jul", "Aug", "Sep" => "Q3"; >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>> default => { throw new Error("Invalid Month") }; >>> } >>> >>> Also compared this to the do expression proposal, it also provides a >>> substantial simplification, but in a way that is more consistent with >>> the existing language. In one of their examples they provide an example >>> of the Redux reducer >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>> a switch expression implementation. >>> >>> function todoApp(state = initialState, action) => switch >>> (action.type) { >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>> action.filter }; >>> case ADD_TODO => { >>> ...state, todos: [ >>> ...state.todos, >>> { >>> text: action.text, >>> completed: false >>> } >>> ] >>> }; >>> case TOGGLE_TODO => { >>> ...state, >>> todos: state.todos.map((todo, index) => (index === >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>> }; >>> default => state; >>> } >>> >>> >>> >>> _______________________________________________ >>> 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 >>> >>> _______________________________________________ >>> 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 > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss
Isiah,
While the pattern-matching proposal does cover a much richer matching, it still doesn't target the issue of being a statement vs an expression. Part of my initial motivation is that the evaluation of the switch returns a value, which pattern-matching doesn't resolve.
Very much enjoying the discussion,
Isiah, While the pattern-matching proposal does cover a much richer matching, it still doesn't target the issue of being a statement vs an expression. Part of my initial motivation is that the evaluation of the switch returns a value, which pattern-matching doesn't resolve. Very much enjoying the discussion, David On 2/28/19 12:07 PM, Isiah Meadows wrote: >> Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. > Sometimes, this is actually *desired*, and most cases where I could've > used this, inheritance was not involved *anywhere*. Also, in > performance-sensitive contexts (like games, which *heavily* use > `switch`/`case`), method dispatch is *far* slower than a simple > `switch` statement, so that pattern doesn't apply everywhere. > > BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over > this anyways - it covers more use cases and is all around more > flexible, so I get more bang for the buck. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> Hi David! >> >> Your last example would, I think, be better served by classes and inheritance, than switch. >> >> Dogs are house animals which are animals >> Cheetas are wild cats which are animals >> >> Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> >> The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> >> Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> All thoughts on this are welcome. Do let me know >> >> On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >>> Naveen, >>> >>> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >>> >>> ``` >>> >>> switch (animal) { >>> case Animal.DOG, Animal.CAT => { >>> // larger block expression >>> // which spans multiple lines >>> >>> return "dry food"; >>> } >>> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >>> // larger block expression >>> // which spans multiple lines >>> >>> return "fresh meat"; >>> } >>> case Animal.ELEPHANT => "hay"; >>> default => { throw new Error("Unsupported Animal"); }; >>> } >>> >>> ``` >>> >>> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >>> >>> ``` >>> >>> function houseAnimal() { >>> >>> // larger block expression >>> // which spans multiple lines >>> >>> return "dry food"; >>> } >>> >>> function wildCatFood() { >>> >>> // larger block expression >>> // which spans multiple lines >>> >>> return "fresh meat"; >>> } >>> >>> >>> const cases = { >>> [Animal.DOG]: houseAnimal, >>> [Animal.CAT]: houseAnimal, >>> [Animal.LION]: wildCatFood, >>> [Animal.TIGER]: wildCatFood, >>> [Animal.CHEETA]: wildCatFood, >>> } >>> >>> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >>> >>> ``` >>> >>> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >>> >>> On 2/28/19 4:37 AM, Naveen Chawla wrote: >>> >>> Isn't the best existing pattern an object literal? >>> >>> const >>> cases = >>> { >>> foo: ()=>1, >>> bar: ()=>3, >>> baz: ()=>6 >>> } >>> , >>> x = >>> cases[v] ? >>> cases[v]() : >>> 99 >>> ; >>> >>> What does any proposal have that is better than this? With optional chaining feature: >>> >>> const >>> x = >>> { >>> foo: ()=>1, >>> bar: ()=>3, >>> baz: ()=>6 >>> }[v]?.() >>> || >>> 99 >>> ; >>> >>> Do let me know your thoughts guys >>> >>> >>> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >>>> This is unmaintainable -- >>>> >>>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >>>> >>>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >>>> >>>> ```javascript >>>> /*jslint*/ >>>> "use strict"; >>>> const v = "foo"; >>>> const x = ( >>>> v === "foo" >>>> ? 1 >>>> : v === "bar" >>>> ? 3 >>>> : v === "baz" >>>> ? 6 >>>> : 99 >>>> ); >>>> ``` >>>> >>>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >>>> >>>> ```javascript >>>> $ node -e ' >>>> /*jslint devel*/ >>>> "use strict"; >>>> function renderRecent(date) { >>>> /* >>>> * this function will render <date> to "xxx ago" >>>> */ >>>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >>>> return ( >>>> !Number.isFinite(date) >>>> ? "" >>>> : date < 60 >>>> ? date + " sec ago" >>>> : date < 3600 >>>> ? Math.round(date / 60) + " min ago" >>>> : date < 86400 >>>> ? Math.round(date / 3600) + " hr ago" >>>> : date < 129600 >>>> ? "1 day ago" >>>> : Math.round(date / 86400) + " days ago" >>>> ); >>>> } >>>> >>>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >>>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >>>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >>>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >>>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >>>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >>>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >>>> ' >>>> >>>> 0 sec ago >>>> 10 sec ago >>>> 5 min ago >>>> 18 min ago >>>> 2 hr ago >>>> 16 days ago >>>> 365 days ago >>>> >>>> $ >>>> ``` >>>> >>>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >>>> >>>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >>>> >>>> Pull request available here -- https://github.com/babel/babel/pull/9604 >>>> >>>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >>>> >>>> Thanks >>>> >>>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >>>> >>>> You're not alone in wanting pattern matching to be expression-based: >>>> >>>> https://github.com/tc39/proposal-pattern-matching/issues/116 >>>> >>>> ----- >>>> >>>> Isiah Meadows >>>> contact at isiahmeadows.com >>>> www.isiahmeadows.com >>>> >>>> ----- >>>> >>>> Isiah Meadows >>>> contact at isiahmeadows.com >>>> www.isiahmeadows.com >>>> >>>> >>>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >>>> >>>> Jordan, >>>> >>>> Thanks for taking time to read and provide thoughts. >>>> >>>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >>>> >>>> This is unmaintainable -- >>>> >>>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >>>> >>>> This is maintainable, but is less than ideal: >>>> >>>> let x; >>>> >>>> switch (v) { >>>> case "foo": >>>> x = 1; >>>> break; >>>> case "bar": >>>> x = 3; >>>> break; >>>> case "baz": >>>> x = 6; >>>> break; >>>> default: >>>> x = 99; >>>> break; >>>> } >>>> >>>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >>>> >>>> let x; >>>> >>>> case (v) { >>>> when "foo" -> x = 1; >>>> when "bar" -> x = 3; >>>> when "baz" -> x = 6; >>>> when v -> x = 99; >>>> } >>>> >>>> Let's try do expressions, I'll leave people's thoughts to themselves. >>>> >>>> const x = do { >>>> if (v === "foo") { 1; } >>>> else if (v === "bar") { 3; } >>>> else if (v === "baz") { 6; } >>>> else { 99; } >>>> } >>>> >>>> Or as another do expression variant: >>>> >>>> const x = do { >>>> switch (v) { >>>> case "foo": 1; break; >>>> case "bar": 3; break; >>>> case "baz": 6; break; >>>> default: 99; break; >>>> } >>>> } >>>> >>>> And as I'm thinking about switch expressions: >>>> >>>> const x = switch (v) { >>>> case "foo" => 1; >>>> case "bar" => 3; >>>> case "baz" => 6; >>>> default => 99; >>>> } >>>> >>>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >>>> >>>> const nextState = switch (currentState) { >>>> case ... => >>>> } >>>> >>>> >>>> >>>> On 2/25/19 4:00 PM, Jordan Harband wrote: >>>> >>>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >>>> >>>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >>>> >>>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >>>> >>>> Jordan, >>>> >>>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >>>> >>>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>>> >>>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >>>> >>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>>> >>>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>>> >>>> Hence why I wanted to put out there the idea of switch expressions. >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>>> >>>> Hi, >>>> >>>> This would be covered by do expressions. You could just do: >>>> >>>> ```js >>>> const category = do { >>>> switch (...) { >>>> ... >>>> }; >>>> }; >>>> ``` >>>> >>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>> >>>> After looking at a bunch of code in our system noted that there are many >>>> cases where our code base has a pattern similar to this: >>>> >>>> let category = data.category; >>>> >>>> if (category === undefined) { >>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>> switch (session.merchant.settings.tax.incomeCode) { >>>> case TaxIncomeCode.RENTS_14: >>>> category = PaymentCategory.RENT; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> } >>>> } >>>> >>>> I also bumped into a block of go code that also implemented similar >>>> patterns, which really demonstrated to me that there while you could go >>>> crazy with triary nesting there should be a better way. Looked at the >>>> pattern matching proposal and while could possibly help looked like it >>>> was overkill for the typical use case that I'm seeing. The most relevant >>>> example I noted was switch expressions from Java. When applied to this >>>> problem really create a simple result: >>>> >>>> const category = data.category || switch (setting.incomeCode) { >>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>> PaymentCategory.ROYALTIES; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>> PaymentCategory.SERVICES; >>>> default => PaymentCategory.OTHER; >>>> } >>>> >>>> Note; the instead of using the '->' as Java, continue to use => and with >>>> the understanding that the right hand side is fundamentally function. >>>> So similar things to this are natural, note this proposal should remove >>>> "fall through" breaks and allow for multiple cases as such. >>>> >>>> const quarter = switch (foo) { >>>> case "Jan", "Feb", "Mar" => "Q1"; >>>> case "Apr", "May", "Jun" => "Q2"; >>>> case "Jul", "Aug", "Sep" => "Q3"; >>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>> default => { throw new Error("Invalid Month") }; >>>> } >>>> >>>> Also compared this to the do expression proposal, it also provides a >>>> substantial simplification, but in a way that is more consistent with >>>> the existing language. In one of their examples they provide an example >>>> of the Redux reducer >>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>> a switch expression implementation. >>>> >>>> function todoApp(state = initialState, action) => switch >>>> (action.type) { >>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>> action.filter }; >>>> case ADD_TODO => { >>>> ...state, todos: [ >>>> ...state.todos, >>>> { >>>> text: action.text, >>>> completed: false >>>> } >>>> ] >>>> }; >>>> case TOGGLE_TODO => { >>>> ...state, >>>> todos: state.todos.map((todo, index) => (index === >>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>> }; >>>> default => state; >>>> } >>>> >>>> >>>> >>>> _______________________________________________ >>>> 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 >>>> >>>> On 2/25/19 3:42 PM, David Koblas wrote: >>>> >>>> Jordan, >>>> >>>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >>>> >>>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >>>> >>>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >>>> >>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >>>> >>>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >>>> >>>> Hence why I wanted to put out there the idea of switch expressions. >>>> >>>> --David >>>> >>>> >>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >>>> >>>> Hi, >>>> >>>> This would be covered by do expressions. You could just do: >>>> >>>> ```js >>>> const category = do { >>>> switch (...) { >>>> ... >>>> }; >>>> }; >>>> ``` >>>> >>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >>>> >>>> After looking at a bunch of code in our system noted that there are many >>>> cases where our code base has a pattern similar to this: >>>> >>>> let category = data.category; >>>> >>>> if (category === undefined) { >>>> // Even if Tax is not enabled, we have defaults for incomeCode >>>> switch (session.merchant.settings.tax.incomeCode) { >>>> case TaxIncomeCode.RENTS_14: >>>> category = PaymentCategory.RENT; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >>>> category = PaymentCategory.SERVICES; >>>> break; >>>> } >>>> } >>>> >>>> I also bumped into a block of go code that also implemented similar >>>> patterns, which really demonstrated to me that there while you could go >>>> crazy with triary nesting there should be a better way. Looked at the >>>> pattern matching proposal and while could possibly help looked like it >>>> was overkill for the typical use case that I'm seeing. The most relevant >>>> example I noted was switch expressions from Java. When applied to this >>>> problem really create a simple result: >>>> >>>> const category = data.category || switch (setting.incomeCode) { >>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >>>> PaymentCategory.ROYALTIES; >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >>>> PaymentCategory.SERVICES; >>>> default => PaymentCategory.OTHER; >>>> } >>>> >>>> Note; the instead of using the '->' as Java, continue to use => and with >>>> the understanding that the right hand side is fundamentally function. >>>> So similar things to this are natural, note this proposal should remove >>>> "fall through" breaks and allow for multiple cases as such. >>>> >>>> const quarter = switch (foo) { >>>> case "Jan", "Feb", "Mar" => "Q1"; >>>> case "Apr", "May", "Jun" => "Q2"; >>>> case "Jul", "Aug", "Sep" => "Q3"; >>>> case "Oct", "Nov", "Dec" => { return "Q4" }; >>>> default => { throw new Error("Invalid Month") }; >>>> } >>>> >>>> Also compared this to the do expression proposal, it also provides a >>>> substantial simplification, but in a way that is more consistent with >>>> the existing language. In one of their examples they provide an example >>>> of the Redux reducer >>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >>>> a switch expression implementation. >>>> >>>> function todoApp(state = initialState, action) => switch >>>> (action.type) { >>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >>>> action.filter }; >>>> case ADD_TODO => { >>>> ...state, todos: [ >>>> ...state.todos, >>>> { >>>> text: action.text, >>>> completed: false >>>> } >>>> ] >>>> }; >>>> case TOGGLE_TODO => { >>>> ...state, >>>> todos: state.todos.map((todo, index) => (index === >>>> action.index) ? { ...todo, completed: !todo.completed } : todo) >>>> }; >>>> default => state; >>>> } >>>> >>>> >>>> >>>> _______________________________________________ >>>> 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 >>>> >>>> _______________________________________________ >>>> 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 >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss
While the pattern-matching proposal does cover a much richer matching, it still doesn't target the issue of being a statement vs an expression. Part of my initial motivation is that the evaluation of the switch returns a value, which pattern-matching doesn't resolve.
That's still something a lot of people want to see end up in the proposal - in fact, tc39/proposal-pattern-matching#116 was filed by a TC39 committee member. I wouldn't dismiss the possibility of pattern matching expressions before then.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
> While the pattern-matching proposal does cover a much richer matching, it still doesn't target the issue of being a statement vs an expression. Part of my initial motivation is that the evaluation of the switch returns a value, which pattern-matching doesn't resolve. That's still something a lot of people *want* to see end up in the proposal - in fact, https://github.com/tc39/proposal-pattern-matching/issues/116 was filed by a TC39 committee member. I wouldn't dismiss the possibility of pattern matching *expressions* before then. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Thu, Feb 28, 2019 at 12:28 PM David Koblas <david at koblas.com> wrote: > > Isiah, > > While the pattern-matching proposal does cover a much richer matching, > it still doesn't target the issue of being a statement vs an > expression. Part of my initial motivation is that the evaluation of the > switch returns a value, which pattern-matching doesn't resolve. > > Very much enjoying the discussion, > David > > On 2/28/19 12:07 PM, Isiah Meadows wrote: > >> Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. > > Sometimes, this is actually *desired*, and most cases where I could've > > used this, inheritance was not involved *anywhere*. Also, in > > performance-sensitive contexts (like games, which *heavily* use > > `switch`/`case`), method dispatch is *far* slower than a simple > > `switch` statement, so that pattern doesn't apply everywhere. > > > > BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over > > this anyways - it covers more use cases and is all around more > > flexible, so I get more bang for the buck. > > > > ----- > > > > Isiah Meadows > > contact at isiahmeadows.com > > www.isiahmeadows.com > > > > On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: > >> Hi David! > >> > >> Your last example would, I think, be better served by classes and inheritance, than switch. > >> > >> Dogs are house animals which are animals > >> Cheetas are wild cats which are animals > >> > >> Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. > >> > >> The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. > >> > >> Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. > >> > >> All thoughts on this are welcome. Do let me know > >> > >> On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: > >>> Naveen, > >>> > >>> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: > >>> > >>> ``` > >>> > >>> switch (animal) { > >>> case Animal.DOG, Animal.CAT => { > >>> // larger block expression > >>> // which spans multiple lines > >>> > >>> return "dry food"; > >>> } > >>> case Animal.TIGER, Animal.LION, Animal.CHEETA => { > >>> // larger block expression > >>> // which spans multiple lines > >>> > >>> return "fresh meat"; > >>> } > >>> case Animal.ELEPHANT => "hay"; > >>> default => { throw new Error("Unsupported Animal"); }; > >>> } > >>> > >>> ``` > >>> > >>> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. > >>> > >>> ``` > >>> > >>> function houseAnimal() { > >>> > >>> // larger block expression > >>> // which spans multiple lines > >>> > >>> return "dry food"; > >>> } > >>> > >>> function wildCatFood() { > >>> > >>> // larger block expression > >>> // which spans multiple lines > >>> > >>> return "fresh meat"; > >>> } > >>> > >>> > >>> const cases = { > >>> [Animal.DOG]: houseAnimal, > >>> [Animal.CAT]: houseAnimal, > >>> [Animal.LION]: wildCatFood, > >>> [Animal.TIGER]: wildCatFood, > >>> [Animal.CHEETA]: wildCatFood, > >>> } > >>> > >>> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); > >>> > >>> ``` > >>> > >>> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. > >>> > >>> On 2/28/19 4:37 AM, Naveen Chawla wrote: > >>> > >>> Isn't the best existing pattern an object literal? > >>> > >>> const > >>> cases = > >>> { > >>> foo: ()=>1, > >>> bar: ()=>3, > >>> baz: ()=>6 > >>> } > >>> , > >>> x = > >>> cases[v] ? > >>> cases[v]() : > >>> 99 > >>> ; > >>> > >>> What does any proposal have that is better than this? With optional chaining feature: > >>> > >>> const > >>> x = > >>> { > >>> foo: ()=>1, > >>> bar: ()=>3, > >>> baz: ()=>6 > >>> }[v]?.() > >>> || > >>> 99 > >>> ; > >>> > >>> Do let me know your thoughts guys > >>> > >>> > >>> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: > >>>> This is unmaintainable -- > >>>> > >>>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > >>>> > >>>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: > >>>> > >>>> ```javascript > >>>> /*jslint*/ > >>>> "use strict"; > >>>> const v = "foo"; > >>>> const x = ( > >>>> v === "foo" > >>>> ? 1 > >>>> : v === "bar" > >>>> ? 3 > >>>> : v === "baz" > >>>> ? 6 > >>>> : 99 > >>>> ); > >>>> ``` > >>>> > >>>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: > >>>> > >>>> ```javascript > >>>> $ node -e ' > >>>> /*jslint devel*/ > >>>> "use strict"; > >>>> function renderRecent(date) { > >>>> /* > >>>> * this function will render <date> to "xxx ago" > >>>> */ > >>>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; > >>>> return ( > >>>> !Number.isFinite(date) > >>>> ? "" > >>>> : date < 60 > >>>> ? date + " sec ago" > >>>> : date < 3600 > >>>> ? Math.round(date / 60) + " min ago" > >>>> : date < 86400 > >>>> ? Math.round(date / 3600) + " hr ago" > >>>> : date < 129600 > >>>> ? "1 day ago" > >>>> : Math.round(date / 86400) + " days ago" > >>>> ); > >>>> } > >>>> > >>>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" > >>>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" > >>>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > >>>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" > >>>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > >>>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" > >>>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" > >>>> ' > >>>> > >>>> 0 sec ago > >>>> 10 sec ago > >>>> 5 min ago > >>>> 18 min ago > >>>> 2 hr ago > >>>> 16 days ago > >>>> 365 days ago > >>>> > >>>> $ > >>>> ``` > >>>> > >>>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: > >>>> > >>>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. > >>>> > >>>> Pull request available here -- https://github.com/babel/babel/pull/9604 > >>>> > >>>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. > >>>> > >>>> Thanks > >>>> > >>>> On 2/26/19 2:40 PM, Isiah Meadows wrote: > >>>> > >>>> You're not alone in wanting pattern matching to be expression-based: > >>>> > >>>> https://github.com/tc39/proposal-pattern-matching/issues/116 > >>>> > >>>> ----- > >>>> > >>>> Isiah Meadows > >>>> contact at isiahmeadows.com > >>>> www.isiahmeadows.com > >>>> > >>>> ----- > >>>> > >>>> Isiah Meadows > >>>> contact at isiahmeadows.com > >>>> www.isiahmeadows.com > >>>> > >>>> > >>>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: > >>>> > >>>> Jordan, > >>>> > >>>> Thanks for taking time to read and provide thoughts. > >>>> > >>>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. > >>>> > >>>> This is unmaintainable -- > >>>> > >>>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > >>>> > >>>> This is maintainable, but is less than ideal: > >>>> > >>>> let x; > >>>> > >>>> switch (v) { > >>>> case "foo": > >>>> x = 1; > >>>> break; > >>>> case "bar": > >>>> x = 3; > >>>> break; > >>>> case "baz": > >>>> x = 6; > >>>> break; > >>>> default: > >>>> x = 99; > >>>> break; > >>>> } > >>>> > >>>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. > >>>> > >>>> let x; > >>>> > >>>> case (v) { > >>>> when "foo" -> x = 1; > >>>> when "bar" -> x = 3; > >>>> when "baz" -> x = 6; > >>>> when v -> x = 99; > >>>> } > >>>> > >>>> Let's try do expressions, I'll leave people's thoughts to themselves. > >>>> > >>>> const x = do { > >>>> if (v === "foo") { 1; } > >>>> else if (v === "bar") { 3; } > >>>> else if (v === "baz") { 6; } > >>>> else { 99; } > >>>> } > >>>> > >>>> Or as another do expression variant: > >>>> > >>>> const x = do { > >>>> switch (v) { > >>>> case "foo": 1; break; > >>>> case "bar": 3; break; > >>>> case "baz": 6; break; > >>>> default: 99; break; > >>>> } > >>>> } > >>>> > >>>> And as I'm thinking about switch expressions: > >>>> > >>>> const x = switch (v) { > >>>> case "foo" => 1; > >>>> case "bar" => 3; > >>>> case "baz" => 6; > >>>> default => 99; > >>>> } > >>>> > >>>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. > >>>> > >>>> const nextState = switch (currentState) { > >>>> case ... => > >>>> } > >>>> > >>>> > >>>> > >>>> On 2/25/19 4:00 PM, Jordan Harband wrote: > >>>> > >>>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. > >>>> > >>>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. > >>>> > >>>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: > >>>> > >>>> Jordan, > >>>> > >>>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. > >>>> > >>>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? > >>>> > >>>> --David > >>>> > >>>> > >>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > >>>> > >>>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. > >>>> > >>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > >>>> > >>>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. > >>>> > >>>> Hence why I wanted to put out there the idea of switch expressions. > >>>> > >>>> --David > >>>> > >>>> > >>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > >>>> > >>>> Hi, > >>>> > >>>> This would be covered by do expressions. You could just do: > >>>> > >>>> ```js > >>>> const category = do { > >>>> switch (...) { > >>>> ... > >>>> }; > >>>> }; > >>>> ``` > >>>> > >>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: > >>>> > >>>> After looking at a bunch of code in our system noted that there are many > >>>> cases where our code base has a pattern similar to this: > >>>> > >>>> let category = data.category; > >>>> > >>>> if (category === undefined) { > >>>> // Even if Tax is not enabled, we have defaults for incomeCode > >>>> switch (session.merchant.settings.tax.incomeCode) { > >>>> case TaxIncomeCode.RENTS_14: > >>>> category = PaymentCategory.RENT; > >>>> break; > >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>>> category = PaymentCategory.SERVICES; > >>>> break; > >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>>> category = PaymentCategory.SERVICES; > >>>> break; > >>>> } > >>>> } > >>>> > >>>> I also bumped into a block of go code that also implemented similar > >>>> patterns, which really demonstrated to me that there while you could go > >>>> crazy with triary nesting there should be a better way. Looked at the > >>>> pattern matching proposal and while could possibly help looked like it > >>>> was overkill for the typical use case that I'm seeing. The most relevant > >>>> example I noted was switch expressions from Java. When applied to this > >>>> problem really create a simple result: > >>>> > >>>> const category = data.category || switch (setting.incomeCode) { > >>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >>>> PaymentCategory.ROYALTIES; > >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >>>> PaymentCategory.SERVICES; > >>>> default => PaymentCategory.OTHER; > >>>> } > >>>> > >>>> Note; the instead of using the '->' as Java, continue to use => and with > >>>> the understanding that the right hand side is fundamentally function. > >>>> So similar things to this are natural, note this proposal should remove > >>>> "fall through" breaks and allow for multiple cases as such. > >>>> > >>>> const quarter = switch (foo) { > >>>> case "Jan", "Feb", "Mar" => "Q1"; > >>>> case "Apr", "May", "Jun" => "Q2"; > >>>> case "Jul", "Aug", "Sep" => "Q3"; > >>>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >>>> default => { throw new Error("Invalid Month") }; > >>>> } > >>>> > >>>> Also compared this to the do expression proposal, it also provides a > >>>> substantial simplification, but in a way that is more consistent with > >>>> the existing language. In one of their examples they provide an example > >>>> of the Redux reducer > >>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be > >>>> a switch expression implementation. > >>>> > >>>> function todoApp(state = initialState, action) => switch > >>>> (action.type) { > >>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > >>>> action.filter }; > >>>> case ADD_TODO => { > >>>> ...state, todos: [ > >>>> ...state.todos, > >>>> { > >>>> text: action.text, > >>>> completed: false > >>>> } > >>>> ] > >>>> }; > >>>> case TOGGLE_TODO => { > >>>> ...state, > >>>> todos: state.todos.map((todo, index) => (index === > >>>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >>>> }; > >>>> default => state; > >>>> } > >>>> > >>>> > >>>> > >>>> _______________________________________________ > >>>> 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 > >>>> > >>>> On 2/25/19 3:42 PM, David Koblas wrote: > >>>> > >>>> Jordan, > >>>> > >>>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. > >>>> > >>>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? > >>>> > >>>> --David > >>>> > >>>> > >>>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > >>>> > >>>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. > >>>> > >>>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > >>>> > >>>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. > >>>> > >>>> Hence why I wanted to put out there the idea of switch expressions. > >>>> > >>>> --David > >>>> > >>>> > >>>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > >>>> > >>>> Hi, > >>>> > >>>> This would be covered by do expressions. You could just do: > >>>> > >>>> ```js > >>>> const category = do { > >>>> switch (...) { > >>>> ... > >>>> }; > >>>> }; > >>>> ``` > >>>> > >>>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: > >>>> > >>>> After looking at a bunch of code in our system noted that there are many > >>>> cases where our code base has a pattern similar to this: > >>>> > >>>> let category = data.category; > >>>> > >>>> if (category === undefined) { > >>>> // Even if Tax is not enabled, we have defaults for incomeCode > >>>> switch (session.merchant.settings.tax.incomeCode) { > >>>> case TaxIncomeCode.RENTS_14: > >>>> category = PaymentCategory.RENT; > >>>> break; > >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>>> category = PaymentCategory.SERVICES; > >>>> break; > >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>>> category = PaymentCategory.SERVICES; > >>>> break; > >>>> } > >>>> } > >>>> > >>>> I also bumped into a block of go code that also implemented similar > >>>> patterns, which really demonstrated to me that there while you could go > >>>> crazy with triary nesting there should be a better way. Looked at the > >>>> pattern matching proposal and while could possibly help looked like it > >>>> was overkill for the typical use case that I'm seeing. The most relevant > >>>> example I noted was switch expressions from Java. When applied to this > >>>> problem really create a simple result: > >>>> > >>>> const category = data.category || switch (setting.incomeCode) { > >>>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >>>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >>>> PaymentCategory.ROYALTIES; > >>>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >>>> PaymentCategory.SERVICES; > >>>> default => PaymentCategory.OTHER; > >>>> } > >>>> > >>>> Note; the instead of using the '->' as Java, continue to use => and with > >>>> the understanding that the right hand side is fundamentally function. > >>>> So similar things to this are natural, note this proposal should remove > >>>> "fall through" breaks and allow for multiple cases as such. > >>>> > >>>> const quarter = switch (foo) { > >>>> case "Jan", "Feb", "Mar" => "Q1"; > >>>> case "Apr", "May", "Jun" => "Q2"; > >>>> case "Jul", "Aug", "Sep" => "Q3"; > >>>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >>>> default => { throw new Error("Invalid Month") }; > >>>> } > >>>> > >>>> Also compared this to the do expression proposal, it also provides a > >>>> substantial simplification, but in a way that is more consistent with > >>>> the existing language. In one of their examples they provide an example > >>>> of the Redux reducer > >>>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be > >>>> a switch expression implementation. > >>>> > >>>> function todoApp(state = initialState, action) => switch > >>>> (action.type) { > >>>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > >>>> action.filter }; > >>>> case ADD_TODO => { > >>>> ...state, todos: [ > >>>> ...state.todos, > >>>> { > >>>> text: action.text, > >>>> completed: false > >>>> } > >>>> ] > >>>> }; > >>>> case TOGGLE_TODO => { > >>>> ...state, > >>>> todos: state.todos.map((todo, index) => (index === > >>>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >>>> }; > >>>> default => state; > >>>> } > >>>> > >>>> > >>>> > >>>> _______________________________________________ > >>>> 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 > >>>> > >>>> _______________________________________________ > >>>> 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 > >> _______________________________________________ > >> es-discuss mailing list > >> es-discuss at mozilla.org > >> https://mail.mozilla.org/listinfo/es-discuss
I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself.
If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs.
As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from.
I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: > > Using a "switch" here forces you to group classes of objects together > and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that > you might later want. > > Sometimes, this is actually *desired*, and most cases where I could've > used this, inheritance was not involved *anywhere*. Also, in > performance-sensitive contexts (like games, which *heavily* use > `switch`/`case`), method dispatch is *far* slower than a simple > `switch` statement, so that pattern doesn't apply everywhere. > > BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over > this anyways - it covers more use cases and is all around more > flexible, so I get more bang for the buck. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > > > > Hi David! > > > > Your last example would, I think, be better served by classes and > inheritance, than switch. > > > > Dogs are house animals which are animals > > Cheetas are wild cats which are animals > > > > Each could have overridden methods, entirely optionally, where the > method gets called and resolves appropriately. > > > > The input argument could be the class name, from which it is trivial to > instantiate a new instance and get required results. > > > > Using a "switch" here forces you to group classes of objects together > and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that > you might later want. > > > > All thoughts on this are welcome. Do let me know > > > > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: > >> > >> Naveen, > >> > >> Thanks for your observation. The example that I gave might have been > too simplistic, here's a more complete example: > >> > >> ``` > >> > >> switch (animal) { > >> case Animal.DOG, Animal.CAT => { > >> // larger block expression > >> // which spans multiple lines > >> > >> return "dry food"; > >> } > >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { > >> // larger block expression > >> // which spans multiple lines > >> > >> return "fresh meat"; > >> } > >> case Animal.ELEPHANT => "hay"; > >> default => { throw new Error("Unsupported Animal"); }; > >> } > >> > >> ``` > >> > >> While you give examples that would totally work. Things that bother me > about the approach are, when taken to something more complex than a quick > value for value switch you end up with something that looks like this. > >> > >> ``` > >> > >> function houseAnimal() { > >> > >> // larger block expression > >> // which spans multiple lines > >> > >> return "dry food"; > >> } > >> > >> function wildCatFood() { > >> > >> // larger block expression > >> // which spans multiple lines > >> > >> return "fresh meat"; > >> } > >> > >> > >> const cases = { > >> [Animal.DOG]: houseAnimal, > >> [Animal.CAT]: houseAnimal, > >> [Animal.LION]: wildCatFood, > >> [Animal.TIGER]: wildCatFood, > >> [Animal.CHEETA]: wildCatFood, > >> } > >> > >> const food = cases[animal] ? cases[animal]() : (() => {throw new > Error("Unsuppored Animal")})(); > >> > >> ``` > >> > >> As we all know once any language reaches a basic level of functionality > anything is possible. What I think is that JavaScript would benefit by > having a cleaner approach. > >> > >> On 2/28/19 4:37 AM, Naveen Chawla wrote: > >> > >> Isn't the best existing pattern an object literal? > >> > >> const > >> cases = > >> { > >> foo: ()=>1, > >> bar: ()=>3, > >> baz: ()=>6 > >> } > >> , > >> x = > >> cases[v] ? > >> cases[v]() : > >> 99 > >> ; > >> > >> What does any proposal have that is better than this? With optional > chaining feature: > >> > >> const > >> x = > >> { > >> foo: ()=>1, > >> bar: ()=>3, > >> baz: ()=>6 > >> }[v]?.() > >> || > >> 99 > >> ; > >> > >> Do let me know your thoughts guys > >> > >> > >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: > >>> > >>> This is unmaintainable -- > >>> > >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > >>> > >>> i feel proposed switch-expressions are no more readable/maintainable > than ternary-operators, if you follow jslint's style-guide. i'll like to > see more convincing evidence/use-case that they are better: > >>> > >>> ```javascript > >>> /*jslint*/ > >>> "use strict"; > >>> const v = "foo"; > >>> const x = ( > >>> v === "foo" > >>> ? 1 > >>> : v === "bar" > >>> ? 3 > >>> : v === "baz" > >>> ? 6 > >>> : 99 > >>> ); > >>> ``` > >>> > >>> here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > >>> > >>> ```javascript > >>> $ node -e ' > >>> /*jslint devel*/ > >>> "use strict"; > >>> function renderRecent(date) { > >>> /* > >>> * this function will render <date> to "xxx ago" > >>> */ > >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) > * 10; > >>> return ( > >>> !Number.isFinite(date) > >>> ? "" > >>> : date < 60 > >>> ? date + " sec ago" > >>> : date < 3600 > >>> ? Math.round(date / 60) + " min ago" > >>> : date < 86400 > >>> ? Math.round(date / 3600) + " hr ago" > >>> : date < 129600 > >>> ? "1 day ago" > >>> : Math.round(date / 86400) + " days ago" > >>> ); > >>> } > >>> > >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" > >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" > >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" > >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" > >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" > >>> ' > >>> > >>> 0 sec ago > >>> 10 sec ago > >>> 5 min ago > >>> 18 min ago > >>> 2 hr ago > >>> 16 days ago > >>> 365 days ago > >>> > >>> $ > >>> ``` > >>> > >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: > >>> > >>> Just for folks who might be interested, added a babel-plugin to see > what was involved in making this possible. > >>> > >>> Pull request available here -- > https://github.com/babel/babel/pull/9604 > >>> > >>> I'm sure I'm missing a bunch of details, but would be interested in > some help in making this a bit more real. > >>> > >>> Thanks > >>> > >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: > >>> > >>> You're not alone in wanting pattern matching to be expression-based: > >>> > >>> https://github.com/tc39/proposal-pattern-matching/issues/116 > >>> > >>> ----- > >>> > >>> Isiah Meadows > >>> contact at isiahmeadows.com > >>> www.isiahmeadows.com > >>> > >>> ----- > >>> > >>> Isiah Meadows > >>> contact at isiahmeadows.com > >>> www.isiahmeadows.com > >>> > >>> > >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: > >>> > >>> Jordan, > >>> > >>> Thanks for taking time to read and provide thoughts. > >>> > >>> I just back and re-read the pattern matching proposal and it still > fails on the basic requirement of being an Expression not a Statement. The > problem that I see and want to address is the need to have something that > removes the need to chain trinary expressions together to have an > Expression. > >>> > >>> This is unmaintainable -- > >>> > >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; > >>> > >>> This is maintainable, but is less than ideal: > >>> > >>> let x; > >>> > >>> switch (v) { > >>> case "foo": > >>> x = 1; > >>> break; > >>> case "bar": > >>> x = 3; > >>> break; > >>> case "baz": > >>> x = 6; > >>> break; > >>> default: > >>> x = 99; > >>> break; > >>> } > >>> > >>> Pattern matching does shorten the code, but you have a weird default > case and also still end up with a loose variable and since pattern matching > is a statement you still have a initially undefined variable. > >>> > >>> let x; > >>> > >>> case (v) { > >>> when "foo" -> x = 1; > >>> when "bar" -> x = 3; > >>> when "baz" -> x = 6; > >>> when v -> x = 99; > >>> } > >>> > >>> Let's try do expressions, I'll leave people's thoughts to themselves. > >>> > >>> const x = do { > >>> if (v === "foo") { 1; } > >>> else if (v === "bar") { 3; } > >>> else if (v === "baz") { 6; } > >>> else { 99; } > >>> } > >>> > >>> Or as another do expression variant: > >>> > >>> const x = do { > >>> switch (v) { > >>> case "foo": 1; break; > >>> case "bar": 3; break; > >>> case "baz": 6; break; > >>> default: 99; break; > >>> } > >>> } > >>> > >>> And as I'm thinking about switch expressions: > >>> > >>> const x = switch (v) { > >>> case "foo" => 1; > >>> case "bar" => 3; > >>> case "baz" => 6; > >>> default => 99; > >>> } > >>> > >>> What I really like is that it preserves all of the normal JavaScript > syntax with the small change that a switch is allowed in an expression > provided that all of the cases evaluate to expressions hence the use of the > '=>' as an indicator. Fundamentally this is a very basic concept where you > have a state machine and need it switch based on the current state and > evaluate to the new state. > >>> > >>> const nextState = switch (currentState) { > >>> case ... => > >>> } > >>> > >>> > >>> > >>> On 2/25/19 4:00 PM, Jordan Harband wrote: > >>> > >>> Pattern Matching is still at stage 1; so there's not really any > permanent decisions that have been made - the repo theoretically should > contain rationales for decisions up to this point. > >>> > >>> I can speak for myself (as "not a champion" of that proposal, just a > fan) that any similarity to the reviled and terrible `switch` is something > I'll be pushing back against - I want a replacement that lacks the footguns > and pitfalls of `switch`, and that is easily teachable and googleable as a > different, distinct thing. > >>> > >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> > wrote: > >>> > >>> Jordan, > >>> > >>> One question that I have lingering from pattern matching is why is the > syntax so different? IMHO it is still a switch statement with a variation > of the match on the case rather than a whole new construct. > >>> > >>> Is there somewhere I can find a bit of discussion about the history of > the syntax decisions? > >>> > >>> --David > >>> > >>> > >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > >>> > >>> Additionally, https://github.com/tc39/proposal-pattern-matching - > switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >>> > >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > >>> > >>> I quite aware that it’s covered in do expressions. Personally I find > do expressions non-JavaScript in style and it’s also not necessarily going > to make it into the language. > >>> > >>> Hence why I wanted to put out there the idea of switch expressions. > >>> > >>> --David > >>> > >>> > >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > >>> > >>> Hi, > >>> > >>> This would be covered by do expressions. You could just do: > >>> > >>> ```js > >>> const category = do { > >>> switch (...) { > >>> ... > >>> }; > >>> }; > >>> ``` > >>> > >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >>> > >>> After looking at a bunch of code in our system noted that there are > many > >>> cases where our code base has a pattern similar to this: > >>> > >>> let category = data.category; > >>> > >>> if (category === undefined) { > >>> // Even if Tax is not enabled, we have defaults for incomeCode > >>> switch (session.merchant.settings.tax.incomeCode) { > >>> case TaxIncomeCode.RENTS_14: > >>> category = PaymentCategory.RENT; > >>> break; > >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>> category = PaymentCategory.SERVICES; > >>> break; > >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>> category = PaymentCategory.SERVICES; > >>> break; > >>> } > >>> } > >>> > >>> I also bumped into a block of go code that also implemented similar > >>> patterns, which really demonstrated to me that there while you could go > >>> crazy with triary nesting there should be a better way. Looked at the > >>> pattern matching proposal and while could possibly help looked like it > >>> was overkill for the typical use case that I'm seeing. The most > relevant > >>> example I noted was switch expressions from Java. When applied to this > >>> problem really create a simple result: > >>> > >>> const category = data.category || switch (setting.incomeCode) { > >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >>> PaymentCategory.ROYALTIES; > >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >>> PaymentCategory.SERVICES; > >>> default => PaymentCategory.OTHER; > >>> } > >>> > >>> Note; the instead of using the '->' as Java, continue to use => and > with > >>> the understanding that the right hand side is fundamentally function. > >>> So similar things to this are natural, note this proposal should remove > >>> "fall through" breaks and allow for multiple cases as such. > >>> > >>> const quarter = switch (foo) { > >>> case "Jan", "Feb", "Mar" => "Q1"; > >>> case "Apr", "May", "Jun" => "Q2"; > >>> case "Jul", "Aug", "Sep" => "Q3"; > >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >>> default => { throw new Error("Invalid Month") }; > >>> } > >>> > >>> Also compared this to the do expression proposal, it also provides a > >>> substantial simplification, but in a way that is more consistent with > >>> the existing language. In one of their examples they provide an > example > >>> of the Redux reducer > >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would > be > >>> a switch expression implementation. > >>> > >>> function todoApp(state = initialState, action) => switch > >>> (action.type) { > >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > >>> action.filter }; > >>> case ADD_TODO => { > >>> ...state, todos: [ > >>> ...state.todos, > >>> { > >>> text: action.text, > >>> completed: false > >>> } > >>> ] > >>> }; > >>> case TOGGLE_TODO => { > >>> ...state, > >>> todos: state.todos.map((todo, index) => (index === > >>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >>> }; > >>> default => state; > >>> } > >>> > >>> > >>> > >>> _______________________________________________ > >>> 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 > >>> > >>> On 2/25/19 3:42 PM, David Koblas wrote: > >>> > >>> Jordan, > >>> > >>> One question that I have lingering from pattern matching is why is the > syntax so different? IMHO it is still a switch statement with a variation > of the match on the case rather than a whole new construct. > >>> > >>> Is there somewhere I can find a bit of discussion about the history of > the syntax decisions? > >>> > >>> --David > >>> > >>> > >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: > >>> > >>> Additionally, https://github.com/tc39/proposal-pattern-matching - > switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >>> > >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: > >>> > >>> I quite aware that it’s covered in do expressions. Personally I find > do expressions non-JavaScript in style and it’s also not necessarily going > to make it into the language. > >>> > >>> Hence why I wanted to put out there the idea of switch expressions. > >>> > >>> --David > >>> > >>> > >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > >>> > >>> Hi, > >>> > >>> This would be covered by do expressions. You could just do: > >>> > >>> ```js > >>> const category = do { > >>> switch (...) { > >>> ... > >>> }; > >>> }; > >>> ``` > >>> > >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >>> > >>> After looking at a bunch of code in our system noted that there are > many > >>> cases where our code base has a pattern similar to this: > >>> > >>> let category = data.category; > >>> > >>> if (category === undefined) { > >>> // Even if Tax is not enabled, we have defaults for incomeCode > >>> switch (session.merchant.settings.tax.incomeCode) { > >>> case TaxIncomeCode.RENTS_14: > >>> category = PaymentCategory.RENT; > >>> break; > >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>> category = PaymentCategory.SERVICES; > >>> break; > >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >>> category = PaymentCategory.SERVICES; > >>> break; > >>> } > >>> } > >>> > >>> I also bumped into a block of go code that also implemented similar > >>> patterns, which really demonstrated to me that there while you could go > >>> crazy with triary nesting there should be a better way. Looked at the > >>> pattern matching proposal and while could possibly help looked like it > >>> was overkill for the typical use case that I'm seeing. The most > relevant > >>> example I noted was switch expressions from Java. When applied to this > >>> problem really create a simple result: > >>> > >>> const category = data.category || switch (setting.incomeCode) { > >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >>> PaymentCategory.ROYALTIES; > >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >>> PaymentCategory.SERVICES; > >>> default => PaymentCategory.OTHER; > >>> } > >>> > >>> Note; the instead of using the '->' as Java, continue to use => and > with > >>> the understanding that the right hand side is fundamentally function. > >>> So similar things to this are natural, note this proposal should remove > >>> "fall through" breaks and allow for multiple cases as such. > >>> > >>> const quarter = switch (foo) { > >>> case "Jan", "Feb", "Mar" => "Q1"; > >>> case "Apr", "May", "Jun" => "Q2"; > >>> case "Jul", "Aug", "Sep" => "Q3"; > >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >>> default => { throw new Error("Invalid Month") }; > >>> } > >>> > >>> Also compared this to the do expression proposal, it also provides a > >>> substantial simplification, but in a way that is more consistent with > >>> the existing language. In one of their examples they provide an > example > >>> of the Redux reducer > >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would > be > >>> a switch expression implementation. > >>> > >>> function todoApp(state = initialState, action) => switch > >>> (action.type) { > >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > >>> action.filter }; > >>> case ADD_TODO => { > >>> ...state, todos: [ > >>> ...state.todos, > >>> { > >>> text: action.text, > >>> completed: false > >>> } > >>> ] > >>> }; > >>> case TOGGLE_TODO => { > >>> ...state, > >>> todos: state.todos.map((todo, index) => (index === > >>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >>> }; > >>> default => state; > >>> } > >>> > >>> > >>> > >>> _______________________________________________ > >>> 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 > >>> > >>> _______________________________________________ > >>> 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 > > > > _______________________________________________ > > 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/20190228/b77c2cbd/attachment-0001.html>
I'm looking at Three.js's code base, and I'm not seeing any method
overriding or abstract methods used except at the API level for
cloning and copying. Instead, you update properties on the supertype.
As far as I can tell, the entirety of Three.js could almost be
mechanically refactored in terms of components instead of inheritance,
without substantially modifying the API apart from a few extra
.geometry
/etc. property accesses when calling supertype methods.
It's data-driven and almost ECS. (It uses .isObject3D
,
.isPerspectiveCamera
, and similar brand checks, but those don't
need to be inherited to work.)
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
I'm looking at Three.js's code base, and I'm not seeing any method overriding or abstract methods used except at the API level for cloning and copying. Instead, you update properties on the supertype. As far as I can tell, the entirety of Three.js could almost be mechanically refactored in terms of components instead of inheritance, without substantially modifying the API apart from a few extra `.geometry`/etc. property accesses when calling supertype methods. It's data-driven and almost ECS. (It uses `.isObject3D`, `.isPerspectiveCamera`, and similar brand checks, but those don't *need* to be inherited to work.) ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: > > I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. > > If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. > > As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. > > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> Sometimes, this is actually *desired*, and most cases where I could've >> used this, inheritance was not involved *anywhere*. Also, in >> performance-sensitive contexts (like games, which *heavily* use >> `switch`/`case`), method dispatch is *far* slower than a simple >> `switch` statement, so that pattern doesn't apply everywhere. >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over >> this anyways - it covers more use cases and is all around more >> flexible, so I get more bang for the buck. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> > >> > Hi David! >> > >> > Your last example would, I think, be better served by classes and inheritance, than switch. >> > >> > Dogs are house animals which are animals >> > Cheetas are wild cats which are animals >> > >> > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> > >> > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> > >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> > >> > All thoughts on this are welcome. Do let me know >> > >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> >> >> Naveen, >> >> >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> >> >> ``` >> >> >> >> switch (animal) { >> >> case Animal.DOG, Animal.CAT => { >> >> // larger block expression >> >> // which spans multiple lines >> >> >> >> return "dry food"; >> >> } >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> >> // larger block expression >> >> // which spans multiple lines >> >> >> >> return "fresh meat"; >> >> } >> >> case Animal.ELEPHANT => "hay"; >> >> default => { throw new Error("Unsupported Animal"); }; >> >> } >> >> >> >> ``` >> >> >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> >> >> ``` >> >> >> >> function houseAnimal() { >> >> >> >> // larger block expression >> >> // which spans multiple lines >> >> >> >> return "dry food"; >> >> } >> >> >> >> function wildCatFood() { >> >> >> >> // larger block expression >> >> // which spans multiple lines >> >> >> >> return "fresh meat"; >> >> } >> >> >> >> >> >> const cases = { >> >> [Animal.DOG]: houseAnimal, >> >> [Animal.CAT]: houseAnimal, >> >> [Animal.LION]: wildCatFood, >> >> [Animal.TIGER]: wildCatFood, >> >> [Animal.CHEETA]: wildCatFood, >> >> } >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> >> >> ``` >> >> >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> >> >> Isn't the best existing pattern an object literal? >> >> >> >> const >> >> cases = >> >> { >> >> foo: ()=>1, >> >> bar: ()=>3, >> >> baz: ()=>6 >> >> } >> >> , >> >> x = >> >> cases[v] ? >> >> cases[v]() : >> >> 99 >> >> ; >> >> >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> >> >> const >> >> x = >> >> { >> >> foo: ()=>1, >> >> bar: ()=>3, >> >> baz: ()=>6 >> >> }[v]?.() >> >> || >> >> 99 >> >> ; >> >> >> >> Do let me know your thoughts guys >> >> >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >> >>> >> >>> This is unmaintainable -- >> >>> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >>> >> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >> >>> >> >>> ```javascript >> >>> /*jslint*/ >> >>> "use strict"; >> >>> const v = "foo"; >> >>> const x = ( >> >>> v === "foo" >> >>> ? 1 >> >>> : v === "bar" >> >>> ? 3 >> >>> : v === "baz" >> >>> ? 6 >> >>> : 99 >> >>> ); >> >>> ``` >> >>> >> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >> >>> >> >>> ```javascript >> >>> $ node -e ' >> >>> /*jslint devel*/ >> >>> "use strict"; >> >>> function renderRecent(date) { >> >>> /* >> >>> * this function will render <date> to "xxx ago" >> >>> */ >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >> >>> return ( >> >>> !Number.isFinite(date) >> >>> ? "" >> >>> : date < 60 >> >>> ? date + " sec ago" >> >>> : date < 3600 >> >>> ? Math.round(date / 60) + " min ago" >> >>> : date < 86400 >> >>> ? Math.round(date / 3600) + " hr ago" >> >>> : date < 129600 >> >>> ? "1 day ago" >> >>> : Math.round(date / 86400) + " days ago" >> >>> ); >> >>> } >> >>> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> >>> ' >> >>> >> >>> 0 sec ago >> >>> 10 sec ago >> >>> 5 min ago >> >>> 18 min ago >> >>> 2 hr ago >> >>> 16 days ago >> >>> 365 days ago >> >>> >> >>> $ >> >>> ``` >> >>> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >>> >> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >> >>> >> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >>> >> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >> >>> >> >>> Thanks >> >>> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >>> >> >>> You're not alone in wanting pattern matching to be expression-based: >> >>> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >>> >> >>> ----- >> >>> >> >>> Isiah Meadows >> >>> contact at isiahmeadows.com >> >>> www.isiahmeadows.com >> >>> >> >>> ----- >> >>> >> >>> Isiah Meadows >> >>> contact at isiahmeadows.com >> >>> www.isiahmeadows.com >> >>> >> >>> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >>> >> >>> Jordan, >> >>> >> >>> Thanks for taking time to read and provide thoughts. >> >>> >> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >>> >> >>> This is unmaintainable -- >> >>> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >>> >> >>> This is maintainable, but is less than ideal: >> >>> >> >>> let x; >> >>> >> >>> switch (v) { >> >>> case "foo": >> >>> x = 1; >> >>> break; >> >>> case "bar": >> >>> x = 3; >> >>> break; >> >>> case "baz": >> >>> x = 6; >> >>> break; >> >>> default: >> >>> x = 99; >> >>> break; >> >>> } >> >>> >> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >>> >> >>> let x; >> >>> >> >>> case (v) { >> >>> when "foo" -> x = 1; >> >>> when "bar" -> x = 3; >> >>> when "baz" -> x = 6; >> >>> when v -> x = 99; >> >>> } >> >>> >> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >> >>> >> >>> const x = do { >> >>> if (v === "foo") { 1; } >> >>> else if (v === "bar") { 3; } >> >>> else if (v === "baz") { 6; } >> >>> else { 99; } >> >>> } >> >>> >> >>> Or as another do expression variant: >> >>> >> >>> const x = do { >> >>> switch (v) { >> >>> case "foo": 1; break; >> >>> case "bar": 3; break; >> >>> case "baz": 6; break; >> >>> default: 99; break; >> >>> } >> >>> } >> >>> >> >>> And as I'm thinking about switch expressions: >> >>> >> >>> const x = switch (v) { >> >>> case "foo" => 1; >> >>> case "bar" => 3; >> >>> case "baz" => 6; >> >>> default => 99; >> >>> } >> >>> >> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >>> >> >>> const nextState = switch (currentState) { >> >>> case ... => >> >>> } >> >>> >> >>> >> >>> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >>> >> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >>> >> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >>> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >>> >> >>> Jordan, >> >>> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >>> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >>> >> >>> --David >> >>> >> >>> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >>> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >>> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >>> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >>> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >>> >> >>> --David >> >>> >> >>> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >>> >> >>> Hi, >> >>> >> >>> This would be covered by do expressions. You could just do: >> >>> >> >>> ```js >> >>> const category = do { >> >>> switch (...) { >> >>> ... >> >>> }; >> >>> }; >> >>> ``` >> >>> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >>> >> >>> After looking at a bunch of code in our system noted that there are many >> >>> cases where our code base has a pattern similar to this: >> >>> >> >>> let category = data.category; >> >>> >> >>> if (category === undefined) { >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >>> case TaxIncomeCode.RENTS_14: >> >>> category = PaymentCategory.RENT; >> >>> break; >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >>> category = PaymentCategory.SERVICES; >> >>> break; >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >>> category = PaymentCategory.SERVICES; >> >>> break; >> >>> } >> >>> } >> >>> >> >>> I also bumped into a block of go code that also implemented similar >> >>> patterns, which really demonstrated to me that there while you could go >> >>> crazy with triary nesting there should be a better way. Looked at the >> >>> pattern matching proposal and while could possibly help looked like it >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >>> example I noted was switch expressions from Java. When applied to this >> >>> problem really create a simple result: >> >>> >> >>> const category = data.category || switch (setting.incomeCode) { >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >>> PaymentCategory.ROYALTIES; >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >>> PaymentCategory.SERVICES; >> >>> default => PaymentCategory.OTHER; >> >>> } >> >>> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >>> the understanding that the right hand side is fundamentally function. >> >>> So similar things to this are natural, note this proposal should remove >> >>> "fall through" breaks and allow for multiple cases as such. >> >>> >> >>> const quarter = switch (foo) { >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >>> case "Apr", "May", "Jun" => "Q2"; >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >>> default => { throw new Error("Invalid Month") }; >> >>> } >> >>> >> >>> Also compared this to the do expression proposal, it also provides a >> >>> substantial simplification, but in a way that is more consistent with >> >>> the existing language. In one of their examples they provide an example >> >>> of the Redux reducer >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >>> a switch expression implementation. >> >>> >> >>> function todoApp(state = initialState, action) => switch >> >>> (action.type) { >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >>> action.filter }; >> >>> case ADD_TODO => { >> >>> ...state, todos: [ >> >>> ...state.todos, >> >>> { >> >>> text: action.text, >> >>> completed: false >> >>> } >> >>> ] >> >>> }; >> >>> case TOGGLE_TODO => { >> >>> ...state, >> >>> todos: state.todos.map((todo, index) => (index === >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >>> }; >> >>> default => state; >> >>> } >> >>> >> >>> >> >>> >> >>> _______________________________________________ >> >>> 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 >> >>> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: >> >>> >> >>> Jordan, >> >>> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >>> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >>> >> >>> --David >> >>> >> >>> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >>> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >>> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >>> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >>> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >>> >> >>> --David >> >>> >> >>> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >>> >> >>> Hi, >> >>> >> >>> This would be covered by do expressions. You could just do: >> >>> >> >>> ```js >> >>> const category = do { >> >>> switch (...) { >> >>> ... >> >>> }; >> >>> }; >> >>> ``` >> >>> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >>> >> >>> After looking at a bunch of code in our system noted that there are many >> >>> cases where our code base has a pattern similar to this: >> >>> >> >>> let category = data.category; >> >>> >> >>> if (category === undefined) { >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >>> case TaxIncomeCode.RENTS_14: >> >>> category = PaymentCategory.RENT; >> >>> break; >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >>> category = PaymentCategory.SERVICES; >> >>> break; >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >>> category = PaymentCategory.SERVICES; >> >>> break; >> >>> } >> >>> } >> >>> >> >>> I also bumped into a block of go code that also implemented similar >> >>> patterns, which really demonstrated to me that there while you could go >> >>> crazy with triary nesting there should be a better way. Looked at the >> >>> pattern matching proposal and while could possibly help looked like it >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >>> example I noted was switch expressions from Java. When applied to this >> >>> problem really create a simple result: >> >>> >> >>> const category = data.category || switch (setting.incomeCode) { >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >>> PaymentCategory.ROYALTIES; >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >>> PaymentCategory.SERVICES; >> >>> default => PaymentCategory.OTHER; >> >>> } >> >>> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >>> the understanding that the right hand side is fundamentally function. >> >>> So similar things to this are natural, note this proposal should remove >> >>> "fall through" breaks and allow for multiple cases as such. >> >>> >> >>> const quarter = switch (foo) { >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >>> case "Apr", "May", "Jun" => "Q2"; >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >>> default => { throw new Error("Invalid Month") }; >> >>> } >> >>> >> >>> Also compared this to the do expression proposal, it also provides a >> >>> substantial simplification, but in a way that is more consistent with >> >>> the existing language. In one of their examples they provide an example >> >>> of the Redux reducer >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >>> a switch expression implementation. >> >>> >> >>> function todoApp(state = initialState, action) => switch >> >>> (action.type) { >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >>> action.filter }; >> >>> case ADD_TODO => { >> >>> ...state, todos: [ >> >>> ...state.todos, >> >>> { >> >>> text: action.text, >> >>> completed: false >> >>> } >> >>> ] >> >>> }; >> >>> case TOGGLE_TODO => { >> >>> ...state, >> >>> todos: state.todos.map((todo, index) => (index === >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >>> }; >> >>> default => state; >> >>> } >> >>> >> >>> >> >>> >> >>> _______________________________________________ >> >>> 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 >> >>> >> >>> _______________________________________________ >> >>> 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 >> > >> > _______________________________________________ >> > es-discuss mailing list >> > es-discuss at mozilla.org >> > https://mail.mozilla.org/listinfo/es-discuss
The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway).
I'm curious where you got the idea that method invocation is "far" slower than explicit code branching?
The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway). I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> wrote: > I'm looking at Three.js's code base, and I'm not seeing any method > overriding or abstract methods used except at the API level for > cloning and copying. Instead, you update properties on the supertype. > As far as I can tell, the entirety of Three.js could almost be > mechanically refactored in terms of components instead of inheritance, > without substantially modifying the API apart from a few extra > `.geometry`/etc. property accesses when calling supertype methods. > It's data-driven and almost ECS. (It uses `.isObject3D`, > `.isPerspectiveCamera`, and similar brand checks, but those don't > *need* to be inherited to work.) > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > > > > I'm not sure that pattern matching handles deep levels of inheritance > more elegantly than inheritance itself. > > > > If there is a conceptual type hierarchy, then the ability to call > "super", combine it with specialized functionality, etc. is a lot more > manageable using localized, separated logic where you don't feel forced to > read "other patterns" to understand whether your target functionality will > resolve correctly. And hence, a lower chance of bugs. > > > > As for performance, I'd have to see modern benchmarks. But it's not > necessarily clear that pattern matching will be particularly fast either. > I've done game programming with method overriding (Three.js uses it too > throughout) and there is no notable performance hit from doing so. So I'm > not clear where you have got this information from. > > > > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> > >> > Using a "switch" here forces you to group classes of objects together > and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that > you might later want. > >> > >> Sometimes, this is actually *desired*, and most cases where I could've > >> used this, inheritance was not involved *anywhere*. Also, in > >> performance-sensitive contexts (like games, which *heavily* use > >> `switch`/`case`), method dispatch is *far* slower than a simple > >> `switch` statement, so that pattern doesn't apply everywhere. > >> > >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over > >> this anyways - it covers more use cases and is all around more > >> flexible, so I get more bang for the buck. > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> > > >> > Hi David! > >> > > >> > Your last example would, I think, be better served by classes and > inheritance, than switch. > >> > > >> > Dogs are house animals which are animals > >> > Cheetas are wild cats which are animals > >> > > >> > Each could have overridden methods, entirely optionally, where the > method gets called and resolves appropriately. > >> > > >> > The input argument could be the class name, from which it is trivial > to instantiate a new instance and get required results. > >> > > >> > Using a "switch" here forces you to group classes of objects together > and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that > you might later want. > >> > > >> > All thoughts on this are welcome. Do let me know > >> > > >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: > >> >> > >> >> Naveen, > >> >> > >> >> Thanks for your observation. The example that I gave might have > been too simplistic, here's a more complete example: > >> >> > >> >> ``` > >> >> > >> >> switch (animal) { > >> >> case Animal.DOG, Animal.CAT => { > >> >> // larger block expression > >> >> // which spans multiple lines > >> >> > >> >> return "dry food"; > >> >> } > >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { > >> >> // larger block expression > >> >> // which spans multiple lines > >> >> > >> >> return "fresh meat"; > >> >> } > >> >> case Animal.ELEPHANT => "hay"; > >> >> default => { throw new Error("Unsupported Animal"); }; > >> >> } > >> >> > >> >> ``` > >> >> > >> >> While you give examples that would totally work. Things that bother > me about the approach are, when taken to something more complex than a > quick value for value switch you end up with something that looks like this. > >> >> > >> >> ``` > >> >> > >> >> function houseAnimal() { > >> >> > >> >> // larger block expression > >> >> // which spans multiple lines > >> >> > >> >> return "dry food"; > >> >> } > >> >> > >> >> function wildCatFood() { > >> >> > >> >> // larger block expression > >> >> // which spans multiple lines > >> >> > >> >> return "fresh meat"; > >> >> } > >> >> > >> >> > >> >> const cases = { > >> >> [Animal.DOG]: houseAnimal, > >> >> [Animal.CAT]: houseAnimal, > >> >> [Animal.LION]: wildCatFood, > >> >> [Animal.TIGER]: wildCatFood, > >> >> [Animal.CHEETA]: wildCatFood, > >> >> } > >> >> > >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new > Error("Unsuppored Animal")})(); > >> >> > >> >> ``` > >> >> > >> >> As we all know once any language reaches a basic level of > functionality anything is possible. What I think is that JavaScript would > benefit by having a cleaner approach. > >> >> > >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: > >> >> > >> >> Isn't the best existing pattern an object literal? > >> >> > >> >> const > >> >> cases = > >> >> { > >> >> foo: ()=>1, > >> >> bar: ()=>3, > >> >> baz: ()=>6 > >> >> } > >> >> , > >> >> x = > >> >> cases[v] ? > >> >> cases[v]() : > >> >> 99 > >> >> ; > >> >> > >> >> What does any proposal have that is better than this? With optional > chaining feature: > >> >> > >> >> const > >> >> x = > >> >> { > >> >> foo: ()=>1, > >> >> bar: ()=>3, > >> >> baz: ()=>6 > >> >> }[v]?.() > >> >> || > >> >> 99 > >> >> ; > >> >> > >> >> Do let me know your thoughts guys > >> >> > >> >> > >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: > >> >>> > >> >>> This is unmaintainable -- > >> >>> > >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : > 99; > >> >>> > >> >>> i feel proposed switch-expressions are no more > readable/maintainable than ternary-operators, if you follow jslint's > style-guide. i'll like to see more convincing evidence/use-case that they > are better: > >> >>> > >> >>> ```javascript > >> >>> /*jslint*/ > >> >>> "use strict"; > >> >>> const v = "foo"; > >> >>> const x = ( > >> >>> v === "foo" > >> >>> ? 1 > >> >>> : v === "bar" > >> >>> ? 3 > >> >>> : v === "baz" > >> >>> ? 6 > >> >>> : 99 > >> >>> ); > >> >>> ``` > >> >>> > >> >>> here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > >> >>> > >> >>> ```javascript > >> >>> $ node -e ' > >> >>> /*jslint devel*/ > >> >>> "use strict"; > >> >>> function renderRecent(date) { > >> >>> /* > >> >>> * this function will render <date> to "xxx ago" > >> >>> */ > >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * > 0.0001) * 10; > >> >>> return ( > >> >>> !Number.isFinite(date) > >> >>> ? "" > >> >>> : date < 60 > >> >>> ? date + " sec ago" > >> >>> : date < 3600 > >> >>> ? Math.round(date / 60) + " min ago" > >> >>> : date < 86400 > >> >>> ? Math.round(date / 3600) + " hr ago" > >> >>> : date < 129600 > >> >>> ? "1 day ago" > >> >>> : Math.round(date / 86400) + " days ago" > >> >>> ); > >> >>> } > >> >>> > >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" > >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" > >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" > >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" > >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" > >> >>> ' > >> >>> > >> >>> 0 sec ago > >> >>> 10 sec ago > >> >>> 5 min ago > >> >>> 18 min ago > >> >>> 2 hr ago > >> >>> 16 days ago > >> >>> 365 days ago > >> >>> > >> >>> $ > >> >>> ``` > >> >>> > >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: > >> >>> > >> >>> Just for folks who might be interested, added a babel-plugin to see > what was involved in making this possible. > >> >>> > >> >>> Pull request available here -- > https://github.com/babel/babel/pull/9604 > >> >>> > >> >>> I'm sure I'm missing a bunch of details, but would be interested in > some help in making this a bit more real. > >> >>> > >> >>> Thanks > >> >>> > >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: > >> >>> > >> >>> You're not alone in wanting pattern matching to be expression-based: > >> >>> > >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 > >> >>> > >> >>> ----- > >> >>> > >> >>> Isiah Meadows > >> >>> contact at isiahmeadows.com > >> >>> www.isiahmeadows.com > >> >>> > >> >>> ----- > >> >>> > >> >>> Isiah Meadows > >> >>> contact at isiahmeadows.com > >> >>> www.isiahmeadows.com > >> >>> > >> >>> > >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> > wrote: > >> >>> > >> >>> Jordan, > >> >>> > >> >>> Thanks for taking time to read and provide thoughts. > >> >>> > >> >>> I just back and re-read the pattern matching proposal and it still > fails on the basic requirement of being an Expression not a Statement. The > problem that I see and want to address is the need to have something that > removes the need to chain trinary expressions together to have an > Expression. > >> >>> > >> >>> This is unmaintainable -- > >> >>> > >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : > 99; > >> >>> > >> >>> This is maintainable, but is less than ideal: > >> >>> > >> >>> let x; > >> >>> > >> >>> switch (v) { > >> >>> case "foo": > >> >>> x = 1; > >> >>> break; > >> >>> case "bar": > >> >>> x = 3; > >> >>> break; > >> >>> case "baz": > >> >>> x = 6; > >> >>> break; > >> >>> default: > >> >>> x = 99; > >> >>> break; > >> >>> } > >> >>> > >> >>> Pattern matching does shorten the code, but you have a weird > default case and also still end up with a loose variable and since pattern > matching is a statement you still have a initially undefined variable. > >> >>> > >> >>> let x; > >> >>> > >> >>> case (v) { > >> >>> when "foo" -> x = 1; > >> >>> when "bar" -> x = 3; > >> >>> when "baz" -> x = 6; > >> >>> when v -> x = 99; > >> >>> } > >> >>> > >> >>> Let's try do expressions, I'll leave people's thoughts to > themselves. > >> >>> > >> >>> const x = do { > >> >>> if (v === "foo") { 1; } > >> >>> else if (v === "bar") { 3; } > >> >>> else if (v === "baz") { 6; } > >> >>> else { 99; } > >> >>> } > >> >>> > >> >>> Or as another do expression variant: > >> >>> > >> >>> const x = do { > >> >>> switch (v) { > >> >>> case "foo": 1; break; > >> >>> case "bar": 3; break; > >> >>> case "baz": 6; break; > >> >>> default: 99; break; > >> >>> } > >> >>> } > >> >>> > >> >>> And as I'm thinking about switch expressions: > >> >>> > >> >>> const x = switch (v) { > >> >>> case "foo" => 1; > >> >>> case "bar" => 3; > >> >>> case "baz" => 6; > >> >>> default => 99; > >> >>> } > >> >>> > >> >>> What I really like is that it preserves all of the normal > JavaScript syntax with the small change that a switch is allowed in an > expression provided that all of the cases evaluate to expressions hence the > use of the '=>' as an indicator. Fundamentally this is a very basic > concept where you have a state machine and need it switch based on the > current state and evaluate to the new state. > >> >>> > >> >>> const nextState = switch (currentState) { > >> >>> case ... => > >> >>> } > >> >>> > >> >>> > >> >>> > >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: > >> >>> > >> >>> Pattern Matching is still at stage 1; so there's not really any > permanent decisions that have been made - the repo theoretically should > contain rationales for decisions up to this point. > >> >>> > >> >>> I can speak for myself (as "not a champion" of that proposal, just > a fan) that any similarity to the reviled and terrible `switch` is > something I'll be pushing back against - I want a replacement that lacks > the footguns and pitfalls of `switch`, and that is easily teachable and > googleable as a different, distinct thing. > >> >>> > >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> > wrote: > >> >>> > >> >>> Jordan, > >> >>> > >> >>> One question that I have lingering from pattern matching is why is > the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >>> > >> >>> Is there somewhere I can find a bit of discussion about the history > of the syntax decisions? > >> >>> > >> >>> --David > >> >>> > >> >>> > >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> > wrote: > >> >>> > >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - > switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >> >>> > >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> > wrote: > >> >>> > >> >>> I quite aware that it’s covered in do expressions. Personally I > find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >>> > >> >>> Hence why I wanted to put out there the idea of switch expressions. > >> >>> > >> >>> --David > >> >>> > >> >>> > >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > >> >>> > >> >>> Hi, > >> >>> > >> >>> This would be covered by do expressions. You could just do: > >> >>> > >> >>> ```js > >> >>> const category = do { > >> >>> switch (...) { > >> >>> ... > >> >>> }; > >> >>> }; > >> >>> ``` > >> >>> > >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >> >>> > >> >>> After looking at a bunch of code in our system noted that there are > many > >> >>> cases where our code base has a pattern similar to this: > >> >>> > >> >>> let category = data.category; > >> >>> > >> >>> if (category === undefined) { > >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >>> case TaxIncomeCode.RENTS_14: > >> >>> category = PaymentCategory.RENT; > >> >>> break; > >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >>> category = PaymentCategory.SERVICES; > >> >>> break; > >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >>> category = PaymentCategory.SERVICES; > >> >>> break; > >> >>> } > >> >>> } > >> >>> > >> >>> I also bumped into a block of go code that also implemented similar > >> >>> patterns, which really demonstrated to me that there while you > could go > >> >>> crazy with triary nesting there should be a better way. Looked at > the > >> >>> pattern matching proposal and while could possibly help looked like > it > >> >>> was overkill for the typical use case that I'm seeing. The most > relevant > >> >>> example I noted was switch expressions from Java. When applied to > this > >> >>> problem really create a simple result: > >> >>> > >> >>> const category = data.category || switch (setting.incomeCode) { > >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >>> PaymentCategory.ROYALTIES; > >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >> >>> PaymentCategory.SERVICES; > >> >>> default => PaymentCategory.OTHER; > >> >>> } > >> >>> > >> >>> Note; the instead of using the '->' as Java, continue to use => and > with > >> >>> the understanding that the right hand side is fundamentally > function. > >> >>> So similar things to this are natural, note this proposal should > remove > >> >>> "fall through" breaks and allow for multiple cases as such. > >> >>> > >> >>> const quarter = switch (foo) { > >> >>> case "Jan", "Feb", "Mar" => "Q1"; > >> >>> case "Apr", "May", "Jun" => "Q2"; > >> >>> case "Jul", "Aug", "Sep" => "Q3"; > >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >> >>> default => { throw new Error("Invalid Month") }; > >> >>> } > >> >>> > >> >>> Also compared this to the do expression proposal, it also provides a > >> >>> substantial simplification, but in a way that is more consistent > with > >> >>> the existing language. In one of their examples they provide an > example > >> >>> of the Redux reducer > >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this > would be > >> >>> a switch expression implementation. > >> >>> > >> >>> function todoApp(state = initialState, action) => switch > >> >>> (action.type) { > >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > >> >>> action.filter }; > >> >>> case ADD_TODO => { > >> >>> ...state, todos: [ > >> >>> ...state.todos, > >> >>> { > >> >>> text: action.text, > >> >>> completed: false > >> >>> } > >> >>> ] > >> >>> }; > >> >>> case TOGGLE_TODO => { > >> >>> ...state, > >> >>> todos: state.todos.map((todo, index) => (index === > >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >> >>> }; > >> >>> default => state; > >> >>> } > >> >>> > >> >>> > >> >>> > >> >>> _______________________________________________ > >> >>> 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 > >> >>> > >> >>> On 2/25/19 3:42 PM, David Koblas wrote: > >> >>> > >> >>> Jordan, > >> >>> > >> >>> One question that I have lingering from pattern matching is why is > the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >>> > >> >>> Is there somewhere I can find a bit of discussion about the history > of the syntax decisions? > >> >>> > >> >>> --David > >> >>> > >> >>> > >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> > wrote: > >> >>> > >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - > switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >> >>> > >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> > wrote: > >> >>> > >> >>> I quite aware that it’s covered in do expressions. Personally I > find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >>> > >> >>> Hence why I wanted to put out there the idea of switch expressions. > >> >>> > >> >>> --David > >> >>> > >> >>> > >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: > >> >>> > >> >>> Hi, > >> >>> > >> >>> This would be covered by do expressions. You could just do: > >> >>> > >> >>> ```js > >> >>> const category = do { > >> >>> switch (...) { > >> >>> ... > >> >>> }; > >> >>> }; > >> >>> ``` > >> >>> > >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >> >>> > >> >>> After looking at a bunch of code in our system noted that there are > many > >> >>> cases where our code base has a pattern similar to this: > >> >>> > >> >>> let category = data.category; > >> >>> > >> >>> if (category === undefined) { > >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >>> case TaxIncomeCode.RENTS_14: > >> >>> category = PaymentCategory.RENT; > >> >>> break; > >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >>> category = PaymentCategory.SERVICES; > >> >>> break; > >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >>> category = PaymentCategory.SERVICES; > >> >>> break; > >> >>> } > >> >>> } > >> >>> > >> >>> I also bumped into a block of go code that also implemented similar > >> >>> patterns, which really demonstrated to me that there while you > could go > >> >>> crazy with triary nesting there should be a better way. Looked at > the > >> >>> pattern matching proposal and while could possibly help looked like > it > >> >>> was overkill for the typical use case that I'm seeing. The most > relevant > >> >>> example I noted was switch expressions from Java. When applied to > this > >> >>> problem really create a simple result: > >> >>> > >> >>> const category = data.category || switch (setting.incomeCode) { > >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >>> PaymentCategory.ROYALTIES; > >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >> >>> PaymentCategory.SERVICES; > >> >>> default => PaymentCategory.OTHER; > >> >>> } > >> >>> > >> >>> Note; the instead of using the '->' as Java, continue to use => and > with > >> >>> the understanding that the right hand side is fundamentally > function. > >> >>> So similar things to this are natural, note this proposal should > remove > >> >>> "fall through" breaks and allow for multiple cases as such. > >> >>> > >> >>> const quarter = switch (foo) { > >> >>> case "Jan", "Feb", "Mar" => "Q1"; > >> >>> case "Apr", "May", "Jun" => "Q2"; > >> >>> case "Jul", "Aug", "Sep" => "Q3"; > >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >> >>> default => { throw new Error("Invalid Month") }; > >> >>> } > >> >>> > >> >>> Also compared this to the do expression proposal, it also provides a > >> >>> substantial simplification, but in a way that is more consistent > with > >> >>> the existing language. In one of their examples they provide an > example > >> >>> of the Redux reducer > >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this > would be > >> >>> a switch expression implementation. > >> >>> > >> >>> function todoApp(state = initialState, action) => switch > >> >>> (action.type) { > >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: > >> >>> action.filter }; > >> >>> case ADD_TODO => { > >> >>> ...state, todos: [ > >> >>> ...state.todos, > >> >>> { > >> >>> text: action.text, > >> >>> completed: false > >> >>> } > >> >>> ] > >> >>> }; > >> >>> case TOGGLE_TODO => { > >> >>> ...state, > >> >>> todos: state.todos.map((todo, index) => (index === > >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >> >>> }; > >> >>> default => state; > >> >>> } > >> >>> > >> >>> > >> >>> > >> >>> _______________________________________________ > >> >>> 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 > >> >>> > >> >>> _______________________________________________ > >> >>> 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 > >> > > >> > _______________________________________________ > >> > 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/20190301/aaf49d5f/attachment-0001.html>
It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render
But it frequently does internally. For example:
- Calculating object projections: mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1243-L1351
- Rendering object lists: mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1353-L1411
- Setting the rendering mode and controlling basic rendering: mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L802-L874
Obviously, it exposes a data-oriented, object oriented API. And it does appear it's not exclusively conditionals:
- It invokes an dynamic
render
method for "immediate render objects": mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L636-L644 - In
renderBufferDirect
, it does virtual method dispatch onrender
based on one of two possible types, but it otherwise uses conditionals for everything:* mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L707-L876 - It uses a fair bit of functional programming in
compile
: mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1009-L1054
However, I'm finding exceptions in its core and renderers, and it doesn't appear virtual dispatch is that broadly and pervasively used, even though it uses methods a lot.
* This seems like overkill when the diff between the two renderers in
question 1, 2 consist of an extra method + 2 extra variables 3, 4, a few changed method invocations 5, 6, and the rest just due
to a different name and a useless var
.
I'm curious where you got the idea that method invocation is "far" slower than explicit code branching?
- In C++: stackoverflow.com/a/8866789
- JS benchmark with 4 variants (typed method dispatch is polymorphic): jsben.ch/fbJQH
- JS benchmark with 12 variants (typed method dispatch is megamorphic): jsben.ch/aWNDN
And in my experience, the speed difference in real-world performance-critical code is not unlike that microbenchmark and is sometimes even more drastic, especially if it's a linked list instead of just a simple array lookup.
I'd like to emphasize I'm specifically referring to the case where the engine can't reliably assume a single method receiver, i.e. when it has to fall back to dynamic dispatch.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
> It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render But it frequently does internally. For example: - Calculating object projections: https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1243-L1351 - Rendering object *lists*: https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1353-L1411 - Setting the rendering mode and controlling basic rendering: https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L802-L874 Obviously, it exposes a data-oriented, object oriented API. And it does appear it's not *exclusively* conditionals: - It invokes an dynamic `render` method for "immediate render objects": https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L636-L644 - In `renderBufferDirect`, it does virtual method dispatch on `render` based on one of two possible types, but it otherwise uses conditionals for everything:\* https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L707-L876 - It uses a fair bit of functional programming in `compile`: https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1009-L1054 However, I'm finding exceptions in its core and renderers, and it doesn't appear virtual dispatch is *that* broadly and pervasively used, even though it uses methods a lot. \* This seems like overkill when the diff between the two renderers in question [1] [2] consist of an extra method + 2 extra variables [3] [4], a few changed method invocations [5] [6], and the rest just due to a different name and a useless `var`. [1]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLBufferRenderer.js [2]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js [3]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L15-L22 [4]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L61 [5]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L26 [6]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L53 > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? - In C++: https://stackoverflow.com/a/8866789 - JS benchmark with 4 variants (typed method dispatch is polymorphic): http://jsben.ch/fbJQH - JS benchmark with 12 variants (typed method dispatch is megamorphic): http://jsben.ch/aWNDN And in my experience, the speed difference in real-world performance-critical code is not unlike that microbenchmark and is sometimes even more drastic, especially if it's a linked list instead of just a simple array lookup. I'd like to emphasize I'm specifically referring to the case where the engine can't reliably assume a single method receiver, i.e. when it *has* to fall back to dynamic dispatch. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: > > The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway). > > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? > > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> I'm looking at Three.js's code base, and I'm not seeing any method >> overriding or abstract methods used except at the API level for >> cloning and copying. Instead, you update properties on the supertype. >> As far as I can tell, the entirety of Three.js could almost be >> mechanically refactored in terms of components instead of inheritance, >> without substantially modifying the API apart from a few extra >> `.geometry`/etc. property accesses when calling supertype methods. >> It's data-driven and almost ECS. (It uses `.isObject3D`, >> `.isPerspectiveCamera`, and similar brand checks, but those don't >> *need* to be inherited to work.) >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> > >> > I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. >> > >> > If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. >> > >> > As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. >> > >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> Sometimes, this is actually *desired*, and most cases where I could've >> >> used this, inheritance was not involved *anywhere*. Also, in >> >> performance-sensitive contexts (like games, which *heavily* use >> >> `switch`/`case`), method dispatch is *far* slower than a simple >> >> `switch` statement, so that pattern doesn't apply everywhere. >> >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over >> >> this anyways - it covers more use cases and is all around more >> >> flexible, so I get more bang for the buck. >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> > >> >> > Hi David! >> >> > >> >> > Your last example would, I think, be better served by classes and inheritance, than switch. >> >> > >> >> > Dogs are house animals which are animals >> >> > Cheetas are wild cats which are animals >> >> > >> >> > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> >> > >> >> > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> >> > >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> > >> >> > All thoughts on this are welcome. Do let me know >> >> > >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> >> >> >> >> Naveen, >> >> >> >> >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> >> >> >> >> ``` >> >> >> >> >> >> switch (animal) { >> >> >> case Animal.DOG, Animal.CAT => { >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "dry food"; >> >> >> } >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "fresh meat"; >> >> >> } >> >> >> case Animal.ELEPHANT => "hay"; >> >> >> default => { throw new Error("Unsupported Animal"); }; >> >> >> } >> >> >> >> >> >> ``` >> >> >> >> >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> >> >> >> >> ``` >> >> >> >> >> >> function houseAnimal() { >> >> >> >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "dry food"; >> >> >> } >> >> >> >> >> >> function wildCatFood() { >> >> >> >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "fresh meat"; >> >> >> } >> >> >> >> >> >> >> >> >> const cases = { >> >> >> [Animal.DOG]: houseAnimal, >> >> >> [Animal.CAT]: houseAnimal, >> >> >> [Animal.LION]: wildCatFood, >> >> >> [Animal.TIGER]: wildCatFood, >> >> >> [Animal.CHEETA]: wildCatFood, >> >> >> } >> >> >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> >> >> >> >> ``` >> >> >> >> >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> >> >> >> >> Isn't the best existing pattern an object literal? >> >> >> >> >> >> const >> >> >> cases = >> >> >> { >> >> >> foo: ()=>1, >> >> >> bar: ()=>3, >> >> >> baz: ()=>6 >> >> >> } >> >> >> , >> >> >> x = >> >> >> cases[v] ? >> >> >> cases[v]() : >> >> >> 99 >> >> >> ; >> >> >> >> >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> >> >> >> >> const >> >> >> x = >> >> >> { >> >> >> foo: ()=>1, >> >> >> bar: ()=>3, >> >> >> baz: ()=>6 >> >> >> }[v]?.() >> >> >> || >> >> >> 99 >> >> >> ; >> >> >> >> >> >> Do let me know your thoughts guys >> >> >> >> >> >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >> >> >>> >> >> >>> This is unmaintainable -- >> >> >>> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >>> >> >> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >> >> >>> >> >> >>> ```javascript >> >> >>> /*jslint*/ >> >> >>> "use strict"; >> >> >>> const v = "foo"; >> >> >>> const x = ( >> >> >>> v === "foo" >> >> >>> ? 1 >> >> >>> : v === "bar" >> >> >>> ? 3 >> >> >>> : v === "baz" >> >> >>> ? 6 >> >> >>> : 99 >> >> >>> ); >> >> >>> ``` >> >> >>> >> >> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >> >> >>> >> >> >>> ```javascript >> >> >>> $ node -e ' >> >> >>> /*jslint devel*/ >> >> >>> "use strict"; >> >> >>> function renderRecent(date) { >> >> >>> /* >> >> >>> * this function will render <date> to "xxx ago" >> >> >>> */ >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >> >> >>> return ( >> >> >>> !Number.isFinite(date) >> >> >>> ? "" >> >> >>> : date < 60 >> >> >>> ? date + " sec ago" >> >> >>> : date < 3600 >> >> >>> ? Math.round(date / 60) + " min ago" >> >> >>> : date < 86400 >> >> >>> ? Math.round(date / 3600) + " hr ago" >> >> >>> : date < 129600 >> >> >>> ? "1 day ago" >> >> >>> : Math.round(date / 86400) + " days ago" >> >> >>> ); >> >> >>> } >> >> >>> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> >> >>> ' >> >> >>> >> >> >>> 0 sec ago >> >> >>> 10 sec ago >> >> >>> 5 min ago >> >> >>> 18 min ago >> >> >>> 2 hr ago >> >> >>> 16 days ago >> >> >>> 365 days ago >> >> >>> >> >> >>> $ >> >> >>> ``` >> >> >>> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >> >> >>> >> >> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> >>> >> >> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >> >> >>> >> >> >>> Thanks >> >> >>> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >> >>> >> >> >>> You're not alone in wanting pattern matching to be expression-based: >> >> >>> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> >>> >> >> >>> ----- >> >> >>> >> >> >>> Isiah Meadows >> >> >>> contact at isiahmeadows.com >> >> >>> www.isiahmeadows.com >> >> >>> >> >> >>> ----- >> >> >>> >> >> >>> Isiah Meadows >> >> >>> contact at isiahmeadows.com >> >> >>> www.isiahmeadows.com >> >> >>> >> >> >>> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> Jordan, >> >> >>> >> >> >>> Thanks for taking time to read and provide thoughts. >> >> >>> >> >> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >> >>> >> >> >>> This is unmaintainable -- >> >> >>> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >>> >> >> >>> This is maintainable, but is less than ideal: >> >> >>> >> >> >>> let x; >> >> >>> >> >> >>> switch (v) { >> >> >>> case "foo": >> >> >>> x = 1; >> >> >>> break; >> >> >>> case "bar": >> >> >>> x = 3; >> >> >>> break; >> >> >>> case "baz": >> >> >>> x = 6; >> >> >>> break; >> >> >>> default: >> >> >>> x = 99; >> >> >>> break; >> >> >>> } >> >> >>> >> >> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >> >>> >> >> >>> let x; >> >> >>> >> >> >>> case (v) { >> >> >>> when "foo" -> x = 1; >> >> >>> when "bar" -> x = 3; >> >> >>> when "baz" -> x = 6; >> >> >>> when v -> x = 99; >> >> >>> } >> >> >>> >> >> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> >>> >> >> >>> const x = do { >> >> >>> if (v === "foo") { 1; } >> >> >>> else if (v === "bar") { 3; } >> >> >>> else if (v === "baz") { 6; } >> >> >>> else { 99; } >> >> >>> } >> >> >>> >> >> >>> Or as another do expression variant: >> >> >>> >> >> >>> const x = do { >> >> >>> switch (v) { >> >> >>> case "foo": 1; break; >> >> >>> case "bar": 3; break; >> >> >>> case "baz": 6; break; >> >> >>> default: 99; break; >> >> >>> } >> >> >>> } >> >> >>> >> >> >>> And as I'm thinking about switch expressions: >> >> >>> >> >> >>> const x = switch (v) { >> >> >>> case "foo" => 1; >> >> >>> case "bar" => 3; >> >> >>> case "baz" => 6; >> >> >>> default => 99; >> >> >>> } >> >> >>> >> >> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >> >>> >> >> >>> const nextState = switch (currentState) { >> >> >>> case ... => >> >> >>> } >> >> >>> >> >> >>> >> >> >>> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> >>> >> >> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >> >>> >> >> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >> >>> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> Jordan, >> >> >>> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >>> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >>> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >>> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >>> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >>> >> >> >>> Hi, >> >> >>> >> >> >>> This would be covered by do expressions. You could just do: >> >> >>> >> >> >>> ```js >> >> >>> const category = do { >> >> >>> switch (...) { >> >> >>> ... >> >> >>> }; >> >> >>> }; >> >> >>> ``` >> >> >>> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >>> cases where our code base has a pattern similar to this: >> >> >>> >> >> >>> let category = data.category; >> >> >>> >> >> >>> if (category === undefined) { >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >>> category = PaymentCategory.RENT; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> } >> >> >>> } >> >> >>> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >>> problem really create a simple result: >> >> >>> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >>> PaymentCategory.ROYALTIES; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >>> PaymentCategory.SERVICES; >> >> >>> default => PaymentCategory.OTHER; >> >> >>> } >> >> >>> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >>> >> >> >>> const quarter = switch (foo) { >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >>> } >> >> >>> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >>> the existing language. In one of their examples they provide an example >> >> >>> of the Redux reducer >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >>> a switch expression implementation. >> >> >>> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >>> (action.type) { >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >>> action.filter }; >> >> >>> case ADD_TODO => { >> >> >>> ...state, todos: [ >> >> >>> ...state.todos, >> >> >>> { >> >> >>> text: action.text, >> >> >>> completed: false >> >> >>> } >> >> >>> ] >> >> >>> }; >> >> >>> case TOGGLE_TODO => { >> >> >>> ...state, >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >>> }; >> >> >>> default => state; >> >> >>> } >> >> >>> >> >> >>> >> >> >>> >> >> >>> _______________________________________________ >> >> >>> 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 >> >> >>> >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: >> >> >>> >> >> >>> Jordan, >> >> >>> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >>> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >>> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >>> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >>> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >>> >> >> >>> Hi, >> >> >>> >> >> >>> This would be covered by do expressions. You could just do: >> >> >>> >> >> >>> ```js >> >> >>> const category = do { >> >> >>> switch (...) { >> >> >>> ... >> >> >>> }; >> >> >>> }; >> >> >>> ``` >> >> >>> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >>> cases where our code base has a pattern similar to this: >> >> >>> >> >> >>> let category = data.category; >> >> >>> >> >> >>> if (category === undefined) { >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >>> category = PaymentCategory.RENT; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> } >> >> >>> } >> >> >>> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >>> problem really create a simple result: >> >> >>> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >>> PaymentCategory.ROYALTIES; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >>> PaymentCategory.SERVICES; >> >> >>> default => PaymentCategory.OTHER; >> >> >>> } >> >> >>> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >>> >> >> >>> const quarter = switch (foo) { >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >>> } >> >> >>> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >>> the existing language. In one of their examples they provide an example >> >> >>> of the Redux reducer >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >>> a switch expression implementation. >> >> >>> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >>> (action.type) { >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >>> action.filter }; >> >> >>> case ADD_TODO => { >> >> >>> ...state, todos: [ >> >> >>> ...state.todos, >> >> >>> { >> >> >>> text: action.text, >> >> >>> completed: false >> >> >>> } >> >> >>> ] >> >> >>> }; >> >> >>> case TOGGLE_TODO => { >> >> >>> ...state, >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >>> }; >> >> >>> default => state; >> >> >>> } >> >> >>> >> >> >>> >> >> >>> >> >> >>> _______________________________________________ >> >> >>> 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 >> >> >>> >> >> >>> _______________________________________________ >> >> >>> 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 >> >> > >> >> > _______________________________________________ >> >> > es-discuss mailing list >> >> > es-discuss at mozilla.org >> >> > https://mail.mozilla.org/listinfo/es-discuss On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: > > The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway). > > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? > > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> I'm looking at Three.js's code base, and I'm not seeing any method >> overriding or abstract methods used except at the API level for >> cloning and copying. Instead, you update properties on the supertype. >> As far as I can tell, the entirety of Three.js could almost be >> mechanically refactored in terms of components instead of inheritance, >> without substantially modifying the API apart from a few extra >> `.geometry`/etc. property accesses when calling supertype methods. >> It's data-driven and almost ECS. (It uses `.isObject3D`, >> `.isPerspectiveCamera`, and similar brand checks, but those don't >> *need* to be inherited to work.) >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> > >> > I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. >> > >> > If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. >> > >> > As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. >> > >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> Sometimes, this is actually *desired*, and most cases where I could've >> >> used this, inheritance was not involved *anywhere*. Also, in >> >> performance-sensitive contexts (like games, which *heavily* use >> >> `switch`/`case`), method dispatch is *far* slower than a simple >> >> `switch` statement, so that pattern doesn't apply everywhere. >> >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over >> >> this anyways - it covers more use cases and is all around more >> >> flexible, so I get more bang for the buck. >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> > >> >> > Hi David! >> >> > >> >> > Your last example would, I think, be better served by classes and inheritance, than switch. >> >> > >> >> > Dogs are house animals which are animals >> >> > Cheetas are wild cats which are animals >> >> > >> >> > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> >> > >> >> > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> >> > >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> > >> >> > All thoughts on this are welcome. Do let me know >> >> > >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> >> >> >> >> Naveen, >> >> >> >> >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> >> >> >> >> ``` >> >> >> >> >> >> switch (animal) { >> >> >> case Animal.DOG, Animal.CAT => { >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "dry food"; >> >> >> } >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "fresh meat"; >> >> >> } >> >> >> case Animal.ELEPHANT => "hay"; >> >> >> default => { throw new Error("Unsupported Animal"); }; >> >> >> } >> >> >> >> >> >> ``` >> >> >> >> >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> >> >> >> >> ``` >> >> >> >> >> >> function houseAnimal() { >> >> >> >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "dry food"; >> >> >> } >> >> >> >> >> >> function wildCatFood() { >> >> >> >> >> >> // larger block expression >> >> >> // which spans multiple lines >> >> >> >> >> >> return "fresh meat"; >> >> >> } >> >> >> >> >> >> >> >> >> const cases = { >> >> >> [Animal.DOG]: houseAnimal, >> >> >> [Animal.CAT]: houseAnimal, >> >> >> [Animal.LION]: wildCatFood, >> >> >> [Animal.TIGER]: wildCatFood, >> >> >> [Animal.CHEETA]: wildCatFood, >> >> >> } >> >> >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> >> >> >> >> ``` >> >> >> >> >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> >> >> >> >> Isn't the best existing pattern an object literal? >> >> >> >> >> >> const >> >> >> cases = >> >> >> { >> >> >> foo: ()=>1, >> >> >> bar: ()=>3, >> >> >> baz: ()=>6 >> >> >> } >> >> >> , >> >> >> x = >> >> >> cases[v] ? >> >> >> cases[v]() : >> >> >> 99 >> >> >> ; >> >> >> >> >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> >> >> >> >> const >> >> >> x = >> >> >> { >> >> >> foo: ()=>1, >> >> >> bar: ()=>3, >> >> >> baz: ()=>6 >> >> >> }[v]?.() >> >> >> || >> >> >> 99 >> >> >> ; >> >> >> >> >> >> Do let me know your thoughts guys >> >> >> >> >> >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >> >> >>> >> >> >>> This is unmaintainable -- >> >> >>> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >>> >> >> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >> >> >>> >> >> >>> ```javascript >> >> >>> /*jslint*/ >> >> >>> "use strict"; >> >> >>> const v = "foo"; >> >> >>> const x = ( >> >> >>> v === "foo" >> >> >>> ? 1 >> >> >>> : v === "bar" >> >> >>> ? 3 >> >> >>> : v === "baz" >> >> >>> ? 6 >> >> >>> : 99 >> >> >>> ); >> >> >>> ``` >> >> >>> >> >> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >> >> >>> >> >> >>> ```javascript >> >> >>> $ node -e ' >> >> >>> /*jslint devel*/ >> >> >>> "use strict"; >> >> >>> function renderRecent(date) { >> >> >>> /* >> >> >>> * this function will render <date> to "xxx ago" >> >> >>> */ >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >> >> >>> return ( >> >> >>> !Number.isFinite(date) >> >> >>> ? "" >> >> >>> : date < 60 >> >> >>> ? date + " sec ago" >> >> >>> : date < 3600 >> >> >>> ? Math.round(date / 60) + " min ago" >> >> >>> : date < 86400 >> >> >>> ? Math.round(date / 3600) + " hr ago" >> >> >>> : date < 129600 >> >> >>> ? "1 day ago" >> >> >>> : Math.round(date / 86400) + " days ago" >> >> >>> ); >> >> >>> } >> >> >>> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> >> >>> ' >> >> >>> >> >> >>> 0 sec ago >> >> >>> 10 sec ago >> >> >>> 5 min ago >> >> >>> 18 min ago >> >> >>> 2 hr ago >> >> >>> 16 days ago >> >> >>> 365 days ago >> >> >>> >> >> >>> $ >> >> >>> ``` >> >> >>> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >> >> >>> >> >> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> >>> >> >> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >> >> >>> >> >> >>> Thanks >> >> >>> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >> >>> >> >> >>> You're not alone in wanting pattern matching to be expression-based: >> >> >>> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> >>> >> >> >>> ----- >> >> >>> >> >> >>> Isiah Meadows >> >> >>> contact at isiahmeadows.com >> >> >>> www.isiahmeadows.com >> >> >>> >> >> >>> ----- >> >> >>> >> >> >>> Isiah Meadows >> >> >>> contact at isiahmeadows.com >> >> >>> www.isiahmeadows.com >> >> >>> >> >> >>> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> Jordan, >> >> >>> >> >> >>> Thanks for taking time to read and provide thoughts. >> >> >>> >> >> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >> >>> >> >> >>> This is unmaintainable -- >> >> >>> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >>> >> >> >>> This is maintainable, but is less than ideal: >> >> >>> >> >> >>> let x; >> >> >>> >> >> >>> switch (v) { >> >> >>> case "foo": >> >> >>> x = 1; >> >> >>> break; >> >> >>> case "bar": >> >> >>> x = 3; >> >> >>> break; >> >> >>> case "baz": >> >> >>> x = 6; >> >> >>> break; >> >> >>> default: >> >> >>> x = 99; >> >> >>> break; >> >> >>> } >> >> >>> >> >> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >> >>> >> >> >>> let x; >> >> >>> >> >> >>> case (v) { >> >> >>> when "foo" -> x = 1; >> >> >>> when "bar" -> x = 3; >> >> >>> when "baz" -> x = 6; >> >> >>> when v -> x = 99; >> >> >>> } >> >> >>> >> >> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> >>> >> >> >>> const x = do { >> >> >>> if (v === "foo") { 1; } >> >> >>> else if (v === "bar") { 3; } >> >> >>> else if (v === "baz") { 6; } >> >> >>> else { 99; } >> >> >>> } >> >> >>> >> >> >>> Or as another do expression variant: >> >> >>> >> >> >>> const x = do { >> >> >>> switch (v) { >> >> >>> case "foo": 1; break; >> >> >>> case "bar": 3; break; >> >> >>> case "baz": 6; break; >> >> >>> default: 99; break; >> >> >>> } >> >> >>> } >> >> >>> >> >> >>> And as I'm thinking about switch expressions: >> >> >>> >> >> >>> const x = switch (v) { >> >> >>> case "foo" => 1; >> >> >>> case "bar" => 3; >> >> >>> case "baz" => 6; >> >> >>> default => 99; >> >> >>> } >> >> >>> >> >> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >> >>> >> >> >>> const nextState = switch (currentState) { >> >> >>> case ... => >> >> >>> } >> >> >>> >> >> >>> >> >> >>> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> >>> >> >> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >> >>> >> >> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >> >>> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> Jordan, >> >> >>> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >>> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >>> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >>> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >>> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >>> >> >> >>> Hi, >> >> >>> >> >> >>> This would be covered by do expressions. You could just do: >> >> >>> >> >> >>> ```js >> >> >>> const category = do { >> >> >>> switch (...) { >> >> >>> ... >> >> >>> }; >> >> >>> }; >> >> >>> ``` >> >> >>> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >>> cases where our code base has a pattern similar to this: >> >> >>> >> >> >>> let category = data.category; >> >> >>> >> >> >>> if (category === undefined) { >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >>> category = PaymentCategory.RENT; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> } >> >> >>> } >> >> >>> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >>> problem really create a simple result: >> >> >>> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >>> PaymentCategory.ROYALTIES; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >>> PaymentCategory.SERVICES; >> >> >>> default => PaymentCategory.OTHER; >> >> >>> } >> >> >>> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >>> >> >> >>> const quarter = switch (foo) { >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >>> } >> >> >>> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >>> the existing language. In one of their examples they provide an example >> >> >>> of the Redux reducer >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >>> a switch expression implementation. >> >> >>> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >>> (action.type) { >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >>> action.filter }; >> >> >>> case ADD_TODO => { >> >> >>> ...state, todos: [ >> >> >>> ...state.todos, >> >> >>> { >> >> >>> text: action.text, >> >> >>> completed: false >> >> >>> } >> >> >>> ] >> >> >>> }; >> >> >>> case TOGGLE_TODO => { >> >> >>> ...state, >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >>> }; >> >> >>> default => state; >> >> >>> } >> >> >>> >> >> >>> >> >> >>> >> >> >>> _______________________________________________ >> >> >>> 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 >> >> >>> >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: >> >> >>> >> >> >>> Jordan, >> >> >>> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >>> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >>> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >>> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >>> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >>> >> >> >>> --David >> >> >>> >> >> >>> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >>> >> >> >>> Hi, >> >> >>> >> >> >>> This would be covered by do expressions. You could just do: >> >> >>> >> >> >>> ```js >> >> >>> const category = do { >> >> >>> switch (...) { >> >> >>> ... >> >> >>> }; >> >> >>> }; >> >> >>> ``` >> >> >>> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >>> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >>> cases where our code base has a pattern similar to this: >> >> >>> >> >> >>> let category = data.category; >> >> >>> >> >> >>> if (category === undefined) { >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >>> category = PaymentCategory.RENT; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >>> category = PaymentCategory.SERVICES; >> >> >>> break; >> >> >>> } >> >> >>> } >> >> >>> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >>> problem really create a simple result: >> >> >>> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >>> PaymentCategory.ROYALTIES; >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >>> PaymentCategory.SERVICES; >> >> >>> default => PaymentCategory.OTHER; >> >> >>> } >> >> >>> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >>> >> >> >>> const quarter = switch (foo) { >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >>> } >> >> >>> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >>> the existing language. In one of their examples they provide an example >> >> >>> of the Redux reducer >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >>> a switch expression implementation. >> >> >>> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >>> (action.type) { >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >>> action.filter }; >> >> >>> case ADD_TODO => { >> >> >>> ...state, todos: [ >> >> >>> ...state.todos, >> >> >>> { >> >> >>> text: action.text, >> >> >>> completed: false >> >> >>> } >> >> >>> ] >> >> >>> }; >> >> >>> case TOGGLE_TODO => { >> >> >>> ...state, >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >>> }; >> >> >>> default => state; >> >> >>> } >> >> >>> >> >> >>> >> >> >>> >> >> >>> _______________________________________________ >> >> >>> 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 >> >> >>> >> >> >>> _______________________________________________ >> >> >>> 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 >> >> > >> >> > _______________________________________________ >> >> > es-discuss mailing list >> >> > es-discuss at mozilla.org >> >> > https://mail.mozilla.org/listinfo/es-discuss
I don't think those benchmarks test exactly what we are talking about. They
have a dictionary/array look up followed by method dispatch, vs switch case
and execute. Removing the look up it would be: x.doStuff()
vs
switch(x.type)...
. Make sense? Don't have time to do it right now.
Logically I think the JS engine can make them perform identically, so even if benchmarks show something today, I would not be concerned for the future and so would prefer to opt for the paradigm that offers the best manageability, which I think is inheritance by a significant margin, in the cases mentioned. Other types of cases could of course be a whole different story.
I don't think those benchmarks test exactly what we are talking about. They have a dictionary/array look up followed by method dispatch, vs switch case and execute. Removing the look up it would be: `x.doStuff()` vs `switch(x.type)...`. Make sense? Don't have time to do it right now. Logically I think the JS engine can make them perform identically, so even if benchmarks show something today, I would not be concerned for the future and so would prefer to opt for the paradigm that offers the best manageability, which I think is inheritance by a significant margin, in the cases mentioned. Other types of cases could of course be a whole different story. On Sat, 2 Mar 2019, 5:24 am Isiah Meadows, <isiahmeadows at gmail.com> wrote: > > It would be unthinkable for it to use pattern matching or explicit code > branchinI'm g instead of method inheritance for type disambiguation during > render > > But it frequently does internally. For example: > > - Calculating object projections: > > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1243-L1351 > - Rendering object *lists*: > > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1353-L1411 > - Setting the rendering mode and controlling basic rendering: > > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L802-L874 > > Obviously, it exposes a data-oriented, object oriented API. And it > does appear it's not *exclusively* conditionals: > > - It invokes an dynamic `render` method for "immediate render > objects": > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L636-L644 > - In `renderBufferDirect`, it does virtual method dispatch on `render` > based on one of two possible types, but it otherwise uses conditionals > for everything:\* > > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L707-L876 > - It uses a fair bit of functional programming in `compile`: > > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1009-L1054 > > However, I'm finding exceptions in its core and renderers, and it > doesn't appear virtual dispatch is *that* broadly and pervasively > used, even though it uses methods a lot. > > \* This seems like overkill when the diff between the two renderers in > question [1] [2] consist of an extra method + 2 extra variables [3] > [4], a few changed method invocations [5] [6], and the rest just due > to a different name and a useless `var`. > > [1]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLBufferRenderer.js > [2]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js > [3]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L15-L22 > [4]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L61 > [5]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L26 > [6]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L53 > > > I'm curious where you got the idea that method invocation is "far" > slower than explicit code branching? > > - In C++: https://stackoverflow.com/a/8866789 > - JS benchmark with 4 variants (typed method dispatch is polymorphic): > http://jsben.ch/fbJQH > - JS benchmark with 12 variants (typed method dispatch is > megamorphic): http://jsben.ch/aWNDN > > And in my experience, the speed difference in real-world > performance-critical code is not unlike that microbenchmark and is > sometimes even more drastic, especially if it's a linked list instead > of just a simple array lookup. > > I'd like to emphasize I'm specifically referring to the case where the > engine can't reliably assume a single method receiver, i.e. when it > *has* to fall back to dynamic dispatch. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > > > > The entire renderers, cameras, meshes etc. hierarchy uses method > inheritance and many of those methods are called during scene rendering > (which is performance sensitive as it happens per frame). It would be > unthinkable for it to use pattern matching or explicit code branching > instead of method inheritance for type disambiguation during render, > because it would explode the code as well as making it error prone due to > initial cases potentially unintentionally swallowing up cases intended for > later code branches (or unintentionally repeating code branches if the > pattern-matching proposal doesn't have "else" behaviour, of which I'm not > sure, but it if does, it suffers from the first problem anyway). > > > > I'm curious where you got the idea that method invocation is "far" > slower than explicit code branching? > > > > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> > >> I'm looking at Three.js's code base, and I'm not seeing any method > >> overriding or abstract methods used except at the API level for > >> cloning and copying. Instead, you update properties on the supertype. > >> As far as I can tell, the entirety of Three.js could almost be > >> mechanically refactored in terms of components instead of inheritance, > >> without substantially modifying the API apart from a few extra > >> `.geometry`/etc. property accesses when calling supertype methods. > >> It's data-driven and almost ECS. (It uses `.isObject3D`, > >> `.isPerspectiveCamera`, and similar brand checks, but those don't > >> *need* to be inherited to work.) > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> > > >> > I'm not sure that pattern matching handles deep levels of inheritance > more elegantly than inheritance itself. > >> > > >> > If there is a conceptual type hierarchy, then the ability to call > "super", combine it with specialized functionality, etc. is a lot more > manageable using localized, separated logic where you don't feel forced to > read "other patterns" to understand whether your target functionality will > resolve correctly. And hence, a lower chance of bugs. > >> > > >> > As for performance, I'd have to see modern benchmarks. But it's not > necessarily clear that pattern matching will be particularly fast either. > I've done game programming with method overriding (Three.js uses it too > throughout) and there is no notable performance hit from doing so. So I'm > not clear where you have got this information from. > >> > > >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> > >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> > >> >> Sometimes, this is actually *desired*, and most cases where I > could've > >> >> used this, inheritance was not involved *anywhere*. Also, in > >> >> performance-sensitive contexts (like games, which *heavily* use > >> >> `switch`/`case`), method dispatch is *far* slower than a simple > >> >> `switch` statement, so that pattern doesn't apply everywhere. > >> >> > >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ > over > >> >> this anyways - it covers more use cases and is all around more > >> >> flexible, so I get more bang for the buck. > >> >> > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> contact at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> >> > > >> >> > Hi David! > >> >> > > >> >> > Your last example would, I think, be better served by classes and > inheritance, than switch. > >> >> > > >> >> > Dogs are house animals which are animals > >> >> > Cheetas are wild cats which are animals > >> >> > > >> >> > Each could have overridden methods, entirely optionally, where the > method gets called and resolves appropriately. > >> >> > > >> >> > The input argument could be the class name, from which it is > trivial to instantiate a new instance and get required results. > >> >> > > >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> > > >> >> > All thoughts on this are welcome. Do let me know > >> >> > > >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> > wrote: > >> >> >> > >> >> >> Naveen, > >> >> >> > >> >> >> Thanks for your observation. The example that I gave might have > been too simplistic, here's a more complete example: > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> switch (animal) { > >> >> >> case Animal.DOG, Animal.CAT => { > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "dry food"; > >> >> >> } > >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "fresh meat"; > >> >> >> } > >> >> >> case Animal.ELEPHANT => "hay"; > >> >> >> default => { throw new Error("Unsupported Animal"); }; > >> >> >> } > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> While you give examples that would totally work. Things that > bother me about the approach are, when taken to something more complex than > a quick value for value switch you end up with something that looks like > this. > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> function houseAnimal() { > >> >> >> > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "dry food"; > >> >> >> } > >> >> >> > >> >> >> function wildCatFood() { > >> >> >> > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "fresh meat"; > >> >> >> } > >> >> >> > >> >> >> > >> >> >> const cases = { > >> >> >> [Animal.DOG]: houseAnimal, > >> >> >> [Animal.CAT]: houseAnimal, > >> >> >> [Animal.LION]: wildCatFood, > >> >> >> [Animal.TIGER]: wildCatFood, > >> >> >> [Animal.CHEETA]: wildCatFood, > >> >> >> } > >> >> >> > >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw > new Error("Unsuppored Animal")})(); > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> As we all know once any language reaches a basic level of > functionality anything is possible. What I think is that JavaScript would > benefit by having a cleaner approach. > >> >> >> > >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: > >> >> >> > >> >> >> Isn't the best existing pattern an object literal? > >> >> >> > >> >> >> const > >> >> >> cases = > >> >> >> { > >> >> >> foo: ()=>1, > >> >> >> bar: ()=>3, > >> >> >> baz: ()=>6 > >> >> >> } > >> >> >> , > >> >> >> x = > >> >> >> cases[v] ? > >> >> >> cases[v]() : > >> >> >> 99 > >> >> >> ; > >> >> >> > >> >> >> What does any proposal have that is better than this? With > optional chaining feature: > >> >> >> > >> >> >> const > >> >> >> x = > >> >> >> { > >> >> >> foo: ()=>1, > >> >> >> bar: ()=>3, > >> >> >> baz: ()=>6 > >> >> >> }[v]?.() > >> >> >> || > >> >> >> 99 > >> >> >> ; > >> >> >> > >> >> >> Do let me know your thoughts guys > >> >> >> > >> >> >> > >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: > >> >> >>> > >> >> >>> This is unmaintainable -- > >> >> >>> > >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? > 6 : 99; > >> >> >>> > >> >> >>> i feel proposed switch-expressions are no more > readable/maintainable than ternary-operators, if you follow jslint's > style-guide. i'll like to see more convincing evidence/use-case that they > are better: > >> >> >>> > >> >> >>> ```javascript > >> >> >>> /*jslint*/ > >> >> >>> "use strict"; > >> >> >>> const v = "foo"; > >> >> >>> const x = ( > >> >> >>> v === "foo" > >> >> >>> ? 1 > >> >> >>> : v === "bar" > >> >> >>> ? 3 > >> >> >>> : v === "baz" > >> >> >>> ? 6 > >> >> >>> : 99 > >> >> >>> ); > >> >> >>> ``` > >> >> >>> > >> >> >>> here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > >> >> >>> > >> >> >>> ```javascript > >> >> >>> $ node -e ' > >> >> >>> /*jslint devel*/ > >> >> >>> "use strict"; > >> >> >>> function renderRecent(date) { > >> >> >>> /* > >> >> >>> * this function will render <date> to "xxx ago" > >> >> >>> */ > >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * > 0.0001) * 10; > >> >> >>> return ( > >> >> >>> !Number.isFinite(date) > >> >> >>> ? "" > >> >> >>> : date < 60 > >> >> >>> ? date + " sec ago" > >> >> >>> : date < 3600 > >> >> >>> ? Math.round(date / 60) + " min ago" > >> >> >>> : date < 86400 > >> >> >>> ? Math.round(date / 3600) + " hr ago" > >> >> >>> : date < 129600 > >> >> >>> ? "1 day ago" > >> >> >>> : Math.round(date / 86400) + " days ago" > >> >> >>> ); > >> >> >>> } > >> >> >>> > >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec > ago" > >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec > ago" > >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min > ago" > >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days > ago" > >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days > ago" > >> >> >>> ' > >> >> >>> > >> >> >>> 0 sec ago > >> >> >>> 10 sec ago > >> >> >>> 5 min ago > >> >> >>> 18 min ago > >> >> >>> 2 hr ago > >> >> >>> 16 days ago > >> >> >>> 365 days ago > >> >> >>> > >> >> >>> $ > >> >> >>> ``` > >> >> >>> > >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: > >> >> >>> > >> >> >>> Just for folks who might be interested, added a babel-plugin to > see what was involved in making this possible. > >> >> >>> > >> >> >>> Pull request available here -- > https://github.com/babel/babel/pull/9604 > >> >> >>> > >> >> >>> I'm sure I'm missing a bunch of details, but would be interested > in some help in making this a bit more real. > >> >> >>> > >> >> >>> Thanks > >> >> >>> > >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: > >> >> >>> > >> >> >>> You're not alone in wanting pattern matching to be > expression-based: > >> >> >>> > >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 > >> >> >>> > >> >> >>> ----- > >> >> >>> > >> >> >>> Isiah Meadows > >> >> >>> contact at isiahmeadows.com > >> >> >>> www.isiahmeadows.com > >> >> >>> > >> >> >>> ----- > >> >> >>> > >> >> >>> Isiah Meadows > >> >> >>> contact at isiahmeadows.com > >> >> >>> www.isiahmeadows.com > >> >> >>> > >> >> >>> > >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> Jordan, > >> >> >>> > >> >> >>> Thanks for taking time to read and provide thoughts. > >> >> >>> > >> >> >>> I just back and re-read the pattern matching proposal and it > still fails on the basic requirement of being an Expression not a > Statement. The problem that I see and want to address is the need to have > something that removes the need to chain trinary expressions together to > have an Expression. > >> >> >>> > >> >> >>> This is unmaintainable -- > >> >> >>> > >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? > 6 : 99; > >> >> >>> > >> >> >>> This is maintainable, but is less than ideal: > >> >> >>> > >> >> >>> let x; > >> >> >>> > >> >> >>> switch (v) { > >> >> >>> case "foo": > >> >> >>> x = 1; > >> >> >>> break; > >> >> >>> case "bar": > >> >> >>> x = 3; > >> >> >>> break; > >> >> >>> case "baz": > >> >> >>> x = 6; > >> >> >>> break; > >> >> >>> default: > >> >> >>> x = 99; > >> >> >>> break; > >> >> >>> } > >> >> >>> > >> >> >>> Pattern matching does shorten the code, but you have a weird > default case and also still end up with a loose variable and since pattern > matching is a statement you still have a initially undefined variable. > >> >> >>> > >> >> >>> let x; > >> >> >>> > >> >> >>> case (v) { > >> >> >>> when "foo" -> x = 1; > >> >> >>> when "bar" -> x = 3; > >> >> >>> when "baz" -> x = 6; > >> >> >>> when v -> x = 99; > >> >> >>> } > >> >> >>> > >> >> >>> Let's try do expressions, I'll leave people's thoughts to > themselves. > >> >> >>> > >> >> >>> const x = do { > >> >> >>> if (v === "foo") { 1; } > >> >> >>> else if (v === "bar") { 3; } > >> >> >>> else if (v === "baz") { 6; } > >> >> >>> else { 99; } > >> >> >>> } > >> >> >>> > >> >> >>> Or as another do expression variant: > >> >> >>> > >> >> >>> const x = do { > >> >> >>> switch (v) { > >> >> >>> case "foo": 1; break; > >> >> >>> case "bar": 3; break; > >> >> >>> case "baz": 6; break; > >> >> >>> default: 99; break; > >> >> >>> } > >> >> >>> } > >> >> >>> > >> >> >>> And as I'm thinking about switch expressions: > >> >> >>> > >> >> >>> const x = switch (v) { > >> >> >>> case "foo" => 1; > >> >> >>> case "bar" => 3; > >> >> >>> case "baz" => 6; > >> >> >>> default => 99; > >> >> >>> } > >> >> >>> > >> >> >>> What I really like is that it preserves all of the normal > JavaScript syntax with the small change that a switch is allowed in an > expression provided that all of the cases evaluate to expressions hence the > use of the '=>' as an indicator. Fundamentally this is a very basic > concept where you have a state machine and need it switch based on the > current state and evaluate to the new state. > >> >> >>> > >> >> >>> const nextState = switch (currentState) { > >> >> >>> case ... => > >> >> >>> } > >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: > >> >> >>> > >> >> >>> Pattern Matching is still at stage 1; so there's not really any > permanent decisions that have been made - the repo theoretically should > contain rationales for decisions up to this point. > >> >> >>> > >> >> >>> I can speak for myself (as "not a champion" of that proposal, > just a fan) that any similarity to the reviled and terrible `switch` is > something I'll be pushing back against - I want a replacement that lacks > the footguns and pitfalls of `switch`, and that is easily teachable and > googleable as a different, distinct thing. > >> >> >>> > >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> Jordan, > >> >> >>> > >> >> >>> One question that I have lingering from pattern matching is why > is the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >> >>> > >> >> >>> Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> > wrote: > >> >> >>> > >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching > - switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >> >> >>> > >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> I quite aware that it’s covered in do expressions. Personally I > find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >> >>> > >> >> >>> Hence why I wanted to put out there the idea of switch > expressions. > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> > wrote: > >> >> >>> > >> >> >>> Hi, > >> >> >>> > >> >> >>> This would be covered by do expressions. You could just do: > >> >> >>> > >> >> >>> ```js > >> >> >>> const category = do { > >> >> >>> switch (...) { > >> >> >>> ... > >> >> >>> }; > >> >> >>> }; > >> >> >>> ``` > >> >> >>> > >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> After looking at a bunch of code in our system noted that there > are many > >> >> >>> cases where our code base has a pattern similar to this: > >> >> >>> > >> >> >>> let category = data.category; > >> >> >>> > >> >> >>> if (category === undefined) { > >> >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14: > >> >> >>> category = PaymentCategory.RENT; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> } > >> >> >>> } > >> >> >>> > >> >> >>> I also bumped into a block of go code that also implemented > similar > >> >> >>> patterns, which really demonstrated to me that there while you > could go > >> >> >>> crazy with triary nesting there should be a better way. Looked > at the > >> >> >>> pattern matching proposal and while could possibly help looked > like it > >> >> >>> was overkill for the typical use case that I'm seeing. The most > relevant > >> >> >>> example I noted was switch expressions from Java. When applied > to this > >> >> >>> problem really create a simple result: > >> >> >>> > >> >> >>> const category = data.category || switch > (setting.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >> >>> PaymentCategory.ROYALTIES; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >> >> >>> PaymentCategory.SERVICES; > >> >> >>> default => PaymentCategory.OTHER; > >> >> >>> } > >> >> >>> > >> >> >>> Note; the instead of using the '->' as Java, continue to use => > and with > >> >> >>> the understanding that the right hand side is fundamentally > function. > >> >> >>> So similar things to this are natural, note this proposal should > remove > >> >> >>> "fall through" breaks and allow for multiple cases as such. > >> >> >>> > >> >> >>> const quarter = switch (foo) { > >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; > >> >> >>> case "Apr", "May", "Jun" => "Q2"; > >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; > >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >> >> >>> default => { throw new Error("Invalid Month") }; > >> >> >>> } > >> >> >>> > >> >> >>> Also compared this to the do expression proposal, it also > provides a > >> >> >>> substantial simplification, but in a way that is more consistent > with > >> >> >>> the existing language. In one of their examples they provide an > example > >> >> >>> of the Redux reducer > >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this > would be > >> >> >>> a switch expression implementation. > >> >> >>> > >> >> >>> function todoApp(state = initialState, action) => switch > >> >> >>> (action.type) { > >> >> >>> case SET_VISIBILITY_FILTER => { ...state, > visibilityFilter: > >> >> >>> action.filter }; > >> >> >>> case ADD_TODO => { > >> >> >>> ...state, todos: [ > >> >> >>> ...state.todos, > >> >> >>> { > >> >> >>> text: action.text, > >> >> >>> completed: false > >> >> >>> } > >> >> >>> ] > >> >> >>> }; > >> >> >>> case TOGGLE_TODO => { > >> >> >>> ...state, > >> >> >>> todos: state.todos.map((todo, index) => (index === > >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >> >> >>> }; > >> >> >>> default => state; > >> >> >>> } > >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> _______________________________________________ > >> >> >>> 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 > >> >> >>> > >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: > >> >> >>> > >> >> >>> Jordan, > >> >> >>> > >> >> >>> One question that I have lingering from pattern matching is why > is the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >> >>> > >> >> >>> Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> > wrote: > >> >> >>> > >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching > - switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >> >> >>> > >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> I quite aware that it’s covered in do expressions. Personally I > find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >> >>> > >> >> >>> Hence why I wanted to put out there the idea of switch > expressions. > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> > wrote: > >> >> >>> > >> >> >>> Hi, > >> >> >>> > >> >> >>> This would be covered by do expressions. You could just do: > >> >> >>> > >> >> >>> ```js > >> >> >>> const category = do { > >> >> >>> switch (...) { > >> >> >>> ... > >> >> >>> }; > >> >> >>> }; > >> >> >>> ``` > >> >> >>> > >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> After looking at a bunch of code in our system noted that there > are many > >> >> >>> cases where our code base has a pattern similar to this: > >> >> >>> > >> >> >>> let category = data.category; > >> >> >>> > >> >> >>> if (category === undefined) { > >> >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14: > >> >> >>> category = PaymentCategory.RENT; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> } > >> >> >>> } > >> >> >>> > >> >> >>> I also bumped into a block of go code that also implemented > similar > >> >> >>> patterns, which really demonstrated to me that there while you > could go > >> >> >>> crazy with triary nesting there should be a better way. Looked > at the > >> >> >>> pattern matching proposal and while could possibly help looked > like it > >> >> >>> was overkill for the typical use case that I'm seeing. The most > relevant > >> >> >>> example I noted was switch expressions from Java. When applied > to this > >> >> >>> problem really create a simple result: > >> >> >>> > >> >> >>> const category = data.category || switch > (setting.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >> >>> PaymentCategory.ROYALTIES; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >> >> >>> PaymentCategory.SERVICES; > >> >> >>> default => PaymentCategory.OTHER; > >> >> >>> } > >> >> >>> > >> >> >>> Note; the instead of using the '->' as Java, continue to use => > and with > >> >> >>> the understanding that the right hand side is fundamentally > function. > >> >> >>> So similar things to this are natural, note this proposal should > remove > >> >> >>> "fall through" breaks and allow for multiple cases as such. > >> >> >>> > >> >> >>> const quarter = switch (foo) { > >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; > >> >> >>> case "Apr", "May", "Jun" => "Q2"; > >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; > >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >> >> >>> default => { throw new Error("Invalid Month") }; > >> >> >>> } > >> >> >>> > >> >> >>> Also compared this to the do expression proposal, it also > provides a > >> >> >>> substantial simplification, but in a way that is more consistent > with > >> >> >>> the existing language. In one of their examples they provide an > example > >> >> >>> of the Redux reducer > >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this > would be > >> >> >>> a switch expression implementation. > >> >> >>> > >> >> >>> function todoApp(state = initialState, action) => switch > >> >> >>> (action.type) { > >> >> >>> case SET_VISIBILITY_FILTER => { ...state, > visibilityFilter: > >> >> >>> action.filter }; > >> >> >>> case ADD_TODO => { > >> >> >>> ...state, todos: [ > >> >> >>> ...state.todos, > >> >> >>> { > >> >> >>> text: action.text, > >> >> >>> completed: false > >> >> >>> } > >> >> >>> ] > >> >> >>> }; > >> >> >>> case TOGGLE_TODO => { > >> >> >>> ...state, > >> >> >>> todos: state.todos.map((todo, index) => (index === > >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >> >> >>> }; > >> >> >>> default => state; > >> >> >>> } > >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> _______________________________________________ > >> >> >>> 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 > >> >> >>> > >> >> >>> _______________________________________________ > >> >> >>> 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 > >> >> > > >> >> > _______________________________________________ > >> >> > es-discuss mailing list > >> >> > es-discuss at mozilla.org > >> >> > https://mail.mozilla.org/listinfo/es-discuss > > On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > > > > The entire renderers, cameras, meshes etc. hierarchy uses method > inheritance and many of those methods are called during scene rendering > (which is performance sensitive as it happens per frame). It would be > unthinkable for it to use pattern matching or explicit code branching > instead of method inheritance for type disambiguation during render, > because it would explode the code as well as making it error prone due to > initial cases potentially unintentionally swallowing up cases intended for > later code branches (or unintentionally repeating code branches if the > pattern-matching proposal doesn't have "else" behaviour, of which I'm not > sure, but it if does, it suffers from the first problem anyway). > > > > I'm curious where you got the idea that method invocation is "far" > slower than explicit code branching? > > > > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> > >> I'm looking at Three.js's code base, and I'm not seeing any method > >> overriding or abstract methods used except at the API level for > >> cloning and copying. Instead, you update properties on the supertype. > >> As far as I can tell, the entirety of Three.js could almost be > >> mechanically refactored in terms of components instead of inheritance, > >> without substantially modifying the API apart from a few extra > >> `.geometry`/etc. property accesses when calling supertype methods. > >> It's data-driven and almost ECS. (It uses `.isObject3D`, > >> `.isPerspectiveCamera`, and similar brand checks, but those don't > >> *need* to be inherited to work.) > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> > > >> > I'm not sure that pattern matching handles deep levels of inheritance > more elegantly than inheritance itself. > >> > > >> > If there is a conceptual type hierarchy, then the ability to call > "super", combine it with specialized functionality, etc. is a lot more > manageable using localized, separated logic where you don't feel forced to > read "other patterns" to understand whether your target functionality will > resolve correctly. And hence, a lower chance of bugs. > >> > > >> > As for performance, I'd have to see modern benchmarks. But it's not > necessarily clear that pattern matching will be particularly fast either. > I've done game programming with method overriding (Three.js uses it too > throughout) and there is no notable performance hit from doing so. So I'm > not clear where you have got this information from. > >> > > >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> > >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> > >> >> Sometimes, this is actually *desired*, and most cases where I > could've > >> >> used this, inheritance was not involved *anywhere*. Also, in > >> >> performance-sensitive contexts (like games, which *heavily* use > >> >> `switch`/`case`), method dispatch is *far* slower than a simple > >> >> `switch` statement, so that pattern doesn't apply everywhere. > >> >> > >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ > over > >> >> this anyways - it covers more use cases and is all around more > >> >> flexible, so I get more bang for the buck. > >> >> > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> contact at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> >> > > >> >> > Hi David! > >> >> > > >> >> > Your last example would, I think, be better served by classes and > inheritance, than switch. > >> >> > > >> >> > Dogs are house animals which are animals > >> >> > Cheetas are wild cats which are animals > >> >> > > >> >> > Each could have overridden methods, entirely optionally, where the > method gets called and resolves appropriately. > >> >> > > >> >> > The input argument could be the class name, from which it is > trivial to instantiate a new instance and get required results. > >> >> > > >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> > > >> >> > All thoughts on this are welcome. Do let me know > >> >> > > >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> > wrote: > >> >> >> > >> >> >> Naveen, > >> >> >> > >> >> >> Thanks for your observation. The example that I gave might have > been too simplistic, here's a more complete example: > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> switch (animal) { > >> >> >> case Animal.DOG, Animal.CAT => { > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "dry food"; > >> >> >> } > >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "fresh meat"; > >> >> >> } > >> >> >> case Animal.ELEPHANT => "hay"; > >> >> >> default => { throw new Error("Unsupported Animal"); }; > >> >> >> } > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> While you give examples that would totally work. Things that > bother me about the approach are, when taken to something more complex than > a quick value for value switch you end up with something that looks like > this. > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> function houseAnimal() { > >> >> >> > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "dry food"; > >> >> >> } > >> >> >> > >> >> >> function wildCatFood() { > >> >> >> > >> >> >> // larger block expression > >> >> >> // which spans multiple lines > >> >> >> > >> >> >> return "fresh meat"; > >> >> >> } > >> >> >> > >> >> >> > >> >> >> const cases = { > >> >> >> [Animal.DOG]: houseAnimal, > >> >> >> [Animal.CAT]: houseAnimal, > >> >> >> [Animal.LION]: wildCatFood, > >> >> >> [Animal.TIGER]: wildCatFood, > >> >> >> [Animal.CHEETA]: wildCatFood, > >> >> >> } > >> >> >> > >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw > new Error("Unsuppored Animal")})(); > >> >> >> > >> >> >> ``` > >> >> >> > >> >> >> As we all know once any language reaches a basic level of > functionality anything is possible. What I think is that JavaScript would > benefit by having a cleaner approach. > >> >> >> > >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: > >> >> >> > >> >> >> Isn't the best existing pattern an object literal? > >> >> >> > >> >> >> const > >> >> >> cases = > >> >> >> { > >> >> >> foo: ()=>1, > >> >> >> bar: ()=>3, > >> >> >> baz: ()=>6 > >> >> >> } > >> >> >> , > >> >> >> x = > >> >> >> cases[v] ? > >> >> >> cases[v]() : > >> >> >> 99 > >> >> >> ; > >> >> >> > >> >> >> What does any proposal have that is better than this? With > optional chaining feature: > >> >> >> > >> >> >> const > >> >> >> x = > >> >> >> { > >> >> >> foo: ()=>1, > >> >> >> bar: ()=>3, > >> >> >> baz: ()=>6 > >> >> >> }[v]?.() > >> >> >> || > >> >> >> 99 > >> >> >> ; > >> >> >> > >> >> >> Do let me know your thoughts guys > >> >> >> > >> >> >> > >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: > >> >> >>> > >> >> >>> This is unmaintainable -- > >> >> >>> > >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? > 6 : 99; > >> >> >>> > >> >> >>> i feel proposed switch-expressions are no more > readable/maintainable than ternary-operators, if you follow jslint's > style-guide. i'll like to see more convincing evidence/use-case that they > are better: > >> >> >>> > >> >> >>> ```javascript > >> >> >>> /*jslint*/ > >> >> >>> "use strict"; > >> >> >>> const v = "foo"; > >> >> >>> const x = ( > >> >> >>> v === "foo" > >> >> >>> ? 1 > >> >> >>> : v === "bar" > >> >> >>> ? 3 > >> >> >>> : v === "baz" > >> >> >>> ? 6 > >> >> >>> : 99 > >> >> >>> ); > >> >> >>> ``` > >> >> >>> > >> >> >>> here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > >> >> >>> > >> >> >>> ```javascript > >> >> >>> $ node -e ' > >> >> >>> /*jslint devel*/ > >> >> >>> "use strict"; > >> >> >>> function renderRecent(date) { > >> >> >>> /* > >> >> >>> * this function will render <date> to "xxx ago" > >> >> >>> */ > >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * > 0.0001) * 10; > >> >> >>> return ( > >> >> >>> !Number.isFinite(date) > >> >> >>> ? "" > >> >> >>> : date < 60 > >> >> >>> ? date + " sec ago" > >> >> >>> : date < 3600 > >> >> >>> ? Math.round(date / 60) + " min ago" > >> >> >>> : date < 86400 > >> >> >>> ? Math.round(date / 3600) + " hr ago" > >> >> >>> : date < 129600 > >> >> >>> ? "1 day ago" > >> >> >>> : Math.round(date / 86400) + " days ago" > >> >> >>> ); > >> >> >>> } > >> >> >>> > >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec > ago" > >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec > ago" > >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" > >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min > ago" > >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" > >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days > ago" > >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days > ago" > >> >> >>> ' > >> >> >>> > >> >> >>> 0 sec ago > >> >> >>> 10 sec ago > >> >> >>> 5 min ago > >> >> >>> 18 min ago > >> >> >>> 2 hr ago > >> >> >>> 16 days ago > >> >> >>> 365 days ago > >> >> >>> > >> >> >>> $ > >> >> >>> ``` > >> >> >>> > >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: > >> >> >>> > >> >> >>> Just for folks who might be interested, added a babel-plugin to > see what was involved in making this possible. > >> >> >>> > >> >> >>> Pull request available here -- > https://github.com/babel/babel/pull/9604 > >> >> >>> > >> >> >>> I'm sure I'm missing a bunch of details, but would be interested > in some help in making this a bit more real. > >> >> >>> > >> >> >>> Thanks > >> >> >>> > >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: > >> >> >>> > >> >> >>> You're not alone in wanting pattern matching to be > expression-based: > >> >> >>> > >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 > >> >> >>> > >> >> >>> ----- > >> >> >>> > >> >> >>> Isiah Meadows > >> >> >>> contact at isiahmeadows.com > >> >> >>> www.isiahmeadows.com > >> >> >>> > >> >> >>> ----- > >> >> >>> > >> >> >>> Isiah Meadows > >> >> >>> contact at isiahmeadows.com > >> >> >>> www.isiahmeadows.com > >> >> >>> > >> >> >>> > >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> Jordan, > >> >> >>> > >> >> >>> Thanks for taking time to read and provide thoughts. > >> >> >>> > >> >> >>> I just back and re-read the pattern matching proposal and it > still fails on the basic requirement of being an Expression not a > Statement. The problem that I see and want to address is the need to have > something that removes the need to chain trinary expressions together to > have an Expression. > >> >> >>> > >> >> >>> This is unmaintainable -- > >> >> >>> > >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? > 6 : 99; > >> >> >>> > >> >> >>> This is maintainable, but is less than ideal: > >> >> >>> > >> >> >>> let x; > >> >> >>> > >> >> >>> switch (v) { > >> >> >>> case "foo": > >> >> >>> x = 1; > >> >> >>> break; > >> >> >>> case "bar": > >> >> >>> x = 3; > >> >> >>> break; > >> >> >>> case "baz": > >> >> >>> x = 6; > >> >> >>> break; > >> >> >>> default: > >> >> >>> x = 99; > >> >> >>> break; > >> >> >>> } > >> >> >>> > >> >> >>> Pattern matching does shorten the code, but you have a weird > default case and also still end up with a loose variable and since pattern > matching is a statement you still have a initially undefined variable. > >> >> >>> > >> >> >>> let x; > >> >> >>> > >> >> >>> case (v) { > >> >> >>> when "foo" -> x = 1; > >> >> >>> when "bar" -> x = 3; > >> >> >>> when "baz" -> x = 6; > >> >> >>> when v -> x = 99; > >> >> >>> } > >> >> >>> > >> >> >>> Let's try do expressions, I'll leave people's thoughts to > themselves. > >> >> >>> > >> >> >>> const x = do { > >> >> >>> if (v === "foo") { 1; } > >> >> >>> else if (v === "bar") { 3; } > >> >> >>> else if (v === "baz") { 6; } > >> >> >>> else { 99; } > >> >> >>> } > >> >> >>> > >> >> >>> Or as another do expression variant: > >> >> >>> > >> >> >>> const x = do { > >> >> >>> switch (v) { > >> >> >>> case "foo": 1; break; > >> >> >>> case "bar": 3; break; > >> >> >>> case "baz": 6; break; > >> >> >>> default: 99; break; > >> >> >>> } > >> >> >>> } > >> >> >>> > >> >> >>> And as I'm thinking about switch expressions: > >> >> >>> > >> >> >>> const x = switch (v) { > >> >> >>> case "foo" => 1; > >> >> >>> case "bar" => 3; > >> >> >>> case "baz" => 6; > >> >> >>> default => 99; > >> >> >>> } > >> >> >>> > >> >> >>> What I really like is that it preserves all of the normal > JavaScript syntax with the small change that a switch is allowed in an > expression provided that all of the cases evaluate to expressions hence the > use of the '=>' as an indicator. Fundamentally this is a very basic > concept where you have a state machine and need it switch based on the > current state and evaluate to the new state. > >> >> >>> > >> >> >>> const nextState = switch (currentState) { > >> >> >>> case ... => > >> >> >>> } > >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: > >> >> >>> > >> >> >>> Pattern Matching is still at stage 1; so there's not really any > permanent decisions that have been made - the repo theoretically should > contain rationales for decisions up to this point. > >> >> >>> > >> >> >>> I can speak for myself (as "not a champion" of that proposal, > just a fan) that any similarity to the reviled and terrible `switch` is > something I'll be pushing back against - I want a replacement that lacks > the footguns and pitfalls of `switch`, and that is easily teachable and > googleable as a different, distinct thing. > >> >> >>> > >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> Jordan, > >> >> >>> > >> >> >>> One question that I have lingering from pattern matching is why > is the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >> >>> > >> >> >>> Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> > wrote: > >> >> >>> > >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching > - switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >> >> >>> > >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> I quite aware that it’s covered in do expressions. Personally I > find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >> >>> > >> >> >>> Hence why I wanted to put out there the idea of switch > expressions. > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> > wrote: > >> >> >>> > >> >> >>> Hi, > >> >> >>> > >> >> >>> This would be covered by do expressions. You could just do: > >> >> >>> > >> >> >>> ```js > >> >> >>> const category = do { > >> >> >>> switch (...) { > >> >> >>> ... > >> >> >>> }; > >> >> >>> }; > >> >> >>> ``` > >> >> >>> > >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> After looking at a bunch of code in our system noted that there > are many > >> >> >>> cases where our code base has a pattern similar to this: > >> >> >>> > >> >> >>> let category = data.category; > >> >> >>> > >> >> >>> if (category === undefined) { > >> >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14: > >> >> >>> category = PaymentCategory.RENT; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> } > >> >> >>> } > >> >> >>> > >> >> >>> I also bumped into a block of go code that also implemented > similar > >> >> >>> patterns, which really demonstrated to me that there while you > could go > >> >> >>> crazy with triary nesting there should be a better way. Looked > at the > >> >> >>> pattern matching proposal and while could possibly help looked > like it > >> >> >>> was overkill for the typical use case that I'm seeing. The most > relevant > >> >> >>> example I noted was switch expressions from Java. When applied > to this > >> >> >>> problem really create a simple result: > >> >> >>> > >> >> >>> const category = data.category || switch > (setting.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >> >>> PaymentCategory.ROYALTIES; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >> >> >>> PaymentCategory.SERVICES; > >> >> >>> default => PaymentCategory.OTHER; > >> >> >>> } > >> >> >>> > >> >> >>> Note; the instead of using the '->' as Java, continue to use => > and with > >> >> >>> the understanding that the right hand side is fundamentally > function. > >> >> >>> So similar things to this are natural, note this proposal should > remove > >> >> >>> "fall through" breaks and allow for multiple cases as such. > >> >> >>> > >> >> >>> const quarter = switch (foo) { > >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; > >> >> >>> case "Apr", "May", "Jun" => "Q2"; > >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; > >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >> >> >>> default => { throw new Error("Invalid Month") }; > >> >> >>> } > >> >> >>> > >> >> >>> Also compared this to the do expression proposal, it also > provides a > >> >> >>> substantial simplification, but in a way that is more consistent > with > >> >> >>> the existing language. In one of their examples they provide an > example > >> >> >>> of the Redux reducer > >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this > would be > >> >> >>> a switch expression implementation. > >> >> >>> > >> >> >>> function todoApp(state = initialState, action) => switch > >> >> >>> (action.type) { > >> >> >>> case SET_VISIBILITY_FILTER => { ...state, > visibilityFilter: > >> >> >>> action.filter }; > >> >> >>> case ADD_TODO => { > >> >> >>> ...state, todos: [ > >> >> >>> ...state.todos, > >> >> >>> { > >> >> >>> text: action.text, > >> >> >>> completed: false > >> >> >>> } > >> >> >>> ] > >> >> >>> }; > >> >> >>> case TOGGLE_TODO => { > >> >> >>> ...state, > >> >> >>> todos: state.todos.map((todo, index) => (index === > >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) > >> >> >>> }; > >> >> >>> default => state; > >> >> >>> } > >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> _______________________________________________ > >> >> >>> 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 > >> >> >>> > >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: > >> >> >>> > >> >> >>> Jordan, > >> >> >>> > >> >> >>> One question that I have lingering from pattern matching is why > is the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >> >>> > >> >> >>> Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> > wrote: > >> >> >>> > >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching > - switch statements are something I hope we'll soon be able to relegate to > the dustbin of history. > >> >> >>> > >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> I quite aware that it’s covered in do expressions. Personally I > find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >> >>> > >> >> >>> Hence why I wanted to put out there the idea of switch > expressions. > >> >> >>> > >> >> >>> --David > >> >> >>> > >> >> >>> > >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> > wrote: > >> >> >>> > >> >> >>> Hi, > >> >> >>> > >> >> >>> This would be covered by do expressions. You could just do: > >> >> >>> > >> >> >>> ```js > >> >> >>> const category = do { > >> >> >>> switch (...) { > >> >> >>> ... > >> >> >>> }; > >> >> >>> }; > >> >> >>> ``` > >> >> >>> > >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> > wrote: > >> >> >>> > >> >> >>> After looking at a bunch of code in our system noted that there > are many > >> >> >>> cases where our code base has a pattern similar to this: > >> >> >>> > >> >> >>> let category = data.category; > >> >> >>> > >> >> >>> if (category === undefined) { > >> >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14: > >> >> >>> category = PaymentCategory.RENT; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >>> category = PaymentCategory.SERVICES; > >> >> >>> break; > >> >> >>> } > >> >> >>> } > >> >> >>> > >> >> >>> I also bumped into a block of go code that also implemented > similar > >> >> >>> patterns, which really demonstrated to me that there while you > could go > >> >> >>> crazy with triary nesting there should be a better way. Looked > at the > >> >> >>> pattern matching proposal and while could possibly help looked > like it > >> >> >>> was overkill for the typical use case that I'm seeing. The most > relevant > >> >> >>> example I noted was switch expressions from Java. When applied > to this > >> >> >>> problem really create a simple result: > >> >> >>> > >> >> >>> const category = data.category || switch > (setting.incomeCode) { > >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >> >>> PaymentCategory.ROYALTIES; > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190302/c7ff9492/attachment-0001.html>
IIUC the "object dispatch integer"/"object dispatch string" benchmarks are the things you were referring to. Those simulate what the engine would see with virtual dispatch and completely different type maps, just without the source overhead of creating an entire class just for a little benchmark.
And also, engines won't be able to optimize them generally, because
there could be infinitely many type maps, and after about 200 or so
types, the switch
statement ends up much slower.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
IIUC the "object dispatch integer"/"object dispatch string" benchmarks are the things you were referring to. Those simulate what the engine would see with virtual dispatch and completely different type maps, just without the source overhead of creating an entire class just for a little benchmark. And also, engines *won't* be able to optimize them generally, because there could be infinitely many type maps, and after about 200 or so types, the `switch` statement ends up *much* slower. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Sat, Mar 2, 2019 at 3:43 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: > > I don't think those benchmarks test exactly what we are talking about. They have a dictionary/array look up followed by method dispatch, vs switch case and execute. Removing the look up it would be: `x.doStuff()` vs `switch(x.type)...`. Make sense? Don't have time to do it right now. > > Logically I think the JS engine can make them perform identically, so even if benchmarks show something today, I would not be concerned for the future and so would prefer to opt for the paradigm that offers the best manageability, which I think is inheritance by a significant margin, in the cases mentioned. Other types of cases could of course be a whole different story. > > On Sat, 2 Mar 2019, 5:24 am Isiah Meadows, <isiahmeadows at gmail.com> wrote: >> >> > It would be unthinkable for it to use pattern matching or explicit code branchinI'm g instead of method inheritance for type disambiguation during render >> >> But it frequently does internally. For example: >> >> - Calculating object projections: >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1243-L1351 >> - Rendering object *lists*: >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1353-L1411 >> - Setting the rendering mode and controlling basic rendering: >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L802-L874 >> >> Obviously, it exposes a data-oriented, object oriented API. And it >> does appear it's not *exclusively* conditionals: >> >> - It invokes an dynamic `render` method for "immediate render >> objects": https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L636-L644 >> - In `renderBufferDirect`, it does virtual method dispatch on `render` >> based on one of two possible types, but it otherwise uses conditionals >> for everything:\* >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L707-L876 >> - It uses a fair bit of functional programming in `compile`: >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1009-L1054 >> >> However, I'm finding exceptions in its core and renderers, and it >> doesn't appear virtual dispatch is *that* broadly and pervasively >> used, even though it uses methods a lot. >> >> \* This seems like overkill when the diff between the two renderers in >> question [1] [2] consist of an extra method + 2 extra variables [3] >> [4], a few changed method invocations [5] [6], and the rest just due >> to a different name and a useless `var`. >> >> [1]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLBufferRenderer.js >> [2]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js >> [3]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L15-L22 >> [4]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L61 >> [5]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L26 >> [6]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L53 >> >> > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? >> >> - In C++: https://stackoverflow.com/a/8866789 >> - JS benchmark with 4 variants (typed method dispatch is polymorphic): >> http://jsben.ch/fbJQH >> - JS benchmark with 12 variants (typed method dispatch is >> megamorphic): http://jsben.ch/aWNDN >> >> And in my experience, the speed difference in real-world >> performance-critical code is not unlike that microbenchmark and is >> sometimes even more drastic, especially if it's a linked list instead >> of just a simple array lookup. >> >> I'd like to emphasize I'm specifically referring to the case where the >> engine can't reliably assume a single method receiver, i.e. when it >> *has* to fall back to dynamic dispatch. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> > >> > The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway). >> > >> > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? >> > >> > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> I'm looking at Three.js's code base, and I'm not seeing any method >> >> overriding or abstract methods used except at the API level for >> >> cloning and copying. Instead, you update properties on the supertype. >> >> As far as I can tell, the entirety of Three.js could almost be >> >> mechanically refactored in terms of components instead of inheritance, >> >> without substantially modifying the API apart from a few extra >> >> `.geometry`/etc. property accesses when calling supertype methods. >> >> It's data-driven and almost ECS. (It uses `.isObject3D`, >> >> `.isPerspectiveCamera`, and similar brand checks, but those don't >> >> *need* to be inherited to work.) >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> > >> >> > I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. >> >> > >> >> > If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. >> >> > >> >> > As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. >> >> > >> >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> >> >> Sometimes, this is actually *desired*, and most cases where I could've >> >> >> used this, inheritance was not involved *anywhere*. Also, in >> >> >> performance-sensitive contexts (like games, which *heavily* use >> >> >> `switch`/`case`), method dispatch is *far* slower than a simple >> >> >> `switch` statement, so that pattern doesn't apply everywhere. >> >> >> >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over >> >> >> this anyways - it covers more use cases and is all around more >> >> >> flexible, so I get more bang for the buck. >> >> >> >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> contact at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> >> > >> >> >> > Hi David! >> >> >> > >> >> >> > Your last example would, I think, be better served by classes and inheritance, than switch. >> >> >> > >> >> >> > Dogs are house animals which are animals >> >> >> > Cheetas are wild cats which are animals >> >> >> > >> >> >> > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> >> >> > >> >> >> > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> >> >> > >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> > >> >> >> > All thoughts on this are welcome. Do let me know >> >> >> > >> >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> >> >> >> >> >> >> Naveen, >> >> >> >> >> >> >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> switch (animal) { >> >> >> >> case Animal.DOG, Animal.CAT => { >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> } >> >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> } >> >> >> >> case Animal.ELEPHANT => "hay"; >> >> >> >> default => { throw new Error("Unsupported Animal"); }; >> >> >> >> } >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> function houseAnimal() { >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> } >> >> >> >> >> >> >> >> function wildCatFood() { >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> } >> >> >> >> >> >> >> >> >> >> >> >> const cases = { >> >> >> >> [Animal.DOG]: houseAnimal, >> >> >> >> [Animal.CAT]: houseAnimal, >> >> >> >> [Animal.LION]: wildCatFood, >> >> >> >> [Animal.TIGER]: wildCatFood, >> >> >> >> [Animal.CHEETA]: wildCatFood, >> >> >> >> } >> >> >> >> >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> >> >> >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> >> >> >> >> >> >> Isn't the best existing pattern an object literal? >> >> >> >> >> >> >> >> const >> >> >> >> cases = >> >> >> >> { >> >> >> >> foo: ()=>1, >> >> >> >> bar: ()=>3, >> >> >> >> baz: ()=>6 >> >> >> >> } >> >> >> >> , >> >> >> >> x = >> >> >> >> cases[v] ? >> >> >> >> cases[v]() : >> >> >> >> 99 >> >> >> >> ; >> >> >> >> >> >> >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> >> >> >> >> >> >> const >> >> >> >> x = >> >> >> >> { >> >> >> >> foo: ()=>1, >> >> >> >> bar: ()=>3, >> >> >> >> baz: ()=>6 >> >> >> >> }[v]?.() >> >> >> >> || >> >> >> >> 99 >> >> >> >> ; >> >> >> >> >> >> >> >> Do let me know your thoughts guys >> >> >> >> >> >> >> >> >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >> >> >> >>> >> >> >> >>> This is unmaintainable -- >> >> >> >>> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >>> >> >> >> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >> >> >> >>> >> >> >> >>> ```javascript >> >> >> >>> /*jslint*/ >> >> >> >>> "use strict"; >> >> >> >>> const v = "foo"; >> >> >> >>> const x = ( >> >> >> >>> v === "foo" >> >> >> >>> ? 1 >> >> >> >>> : v === "bar" >> >> >> >>> ? 3 >> >> >> >>> : v === "baz" >> >> >> >>> ? 6 >> >> >> >>> : 99 >> >> >> >>> ); >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >> >> >> >>> >> >> >> >>> ```javascript >> >> >> >>> $ node -e ' >> >> >> >>> /*jslint devel*/ >> >> >> >>> "use strict"; >> >> >> >>> function renderRecent(date) { >> >> >> >>> /* >> >> >> >>> * this function will render <date> to "xxx ago" >> >> >> >>> */ >> >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >> >> >> >>> return ( >> >> >> >>> !Number.isFinite(date) >> >> >> >>> ? "" >> >> >> >>> : date < 60 >> >> >> >>> ? date + " sec ago" >> >> >> >>> : date < 3600 >> >> >> >>> ? Math.round(date / 60) + " min ago" >> >> >> >>> : date < 86400 >> >> >> >>> ? Math.round(date / 3600) + " hr ago" >> >> >> >>> : date < 129600 >> >> >> >>> ? "1 day ago" >> >> >> >>> : Math.round(date / 86400) + " days ago" >> >> >> >>> ); >> >> >> >>> } >> >> >> >>> >> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> >> >> >>> ' >> >> >> >>> >> >> >> >>> 0 sec ago >> >> >> >>> 10 sec ago >> >> >> >>> 5 min ago >> >> >> >>> 18 min ago >> >> >> >>> 2 hr ago >> >> >> >>> 16 days ago >> >> >> >>> 365 days ago >> >> >> >>> >> >> >> >>> $ >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >> >> >> >>> >> >> >> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> >> >>> >> >> >> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >> >> >> >>> >> >> >> >>> Thanks >> >> >> >>> >> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >> >> >>> >> >> >> >>> You're not alone in wanting pattern matching to be expression-based: >> >> >> >>> >> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> >> >>> >> >> >> >>> ----- >> >> >> >>> >> >> >> >>> Isiah Meadows >> >> >> >>> contact at isiahmeadows.com >> >> >> >>> www.isiahmeadows.com >> >> >> >>> >> >> >> >>> ----- >> >> >> >>> >> >> >> >>> Isiah Meadows >> >> >> >>> contact at isiahmeadows.com >> >> >> >>> www.isiahmeadows.com >> >> >> >>> >> >> >> >>> >> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> Jordan, >> >> >> >>> >> >> >> >>> Thanks for taking time to read and provide thoughts. >> >> >> >>> >> >> >> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >> >> >>> >> >> >> >>> This is unmaintainable -- >> >> >> >>> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >>> >> >> >> >>> This is maintainable, but is less than ideal: >> >> >> >>> >> >> >> >>> let x; >> >> >> >>> >> >> >> >>> switch (v) { >> >> >> >>> case "foo": >> >> >> >>> x = 1; >> >> >> >>> break; >> >> >> >>> case "bar": >> >> >> >>> x = 3; >> >> >> >>> break; >> >> >> >>> case "baz": >> >> >> >>> x = 6; >> >> >> >>> break; >> >> >> >>> default: >> >> >> >>> x = 99; >> >> >> >>> break; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >> >> >>> >> >> >> >>> let x; >> >> >> >>> >> >> >> >>> case (v) { >> >> >> >>> when "foo" -> x = 1; >> >> >> >>> when "bar" -> x = 3; >> >> >> >>> when "baz" -> x = 6; >> >> >> >>> when v -> x = 99; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> >> >>> >> >> >> >>> const x = do { >> >> >> >>> if (v === "foo") { 1; } >> >> >> >>> else if (v === "bar") { 3; } >> >> >> >>> else if (v === "baz") { 6; } >> >> >> >>> else { 99; } >> >> >> >>> } >> >> >> >>> >> >> >> >>> Or as another do expression variant: >> >> >> >>> >> >> >> >>> const x = do { >> >> >> >>> switch (v) { >> >> >> >>> case "foo": 1; break; >> >> >> >>> case "bar": 3; break; >> >> >> >>> case "baz": 6; break; >> >> >> >>> default: 99; break; >> >> >> >>> } >> >> >> >>> } >> >> >> >>> >> >> >> >>> And as I'm thinking about switch expressions: >> >> >> >>> >> >> >> >>> const x = switch (v) { >> >> >> >>> case "foo" => 1; >> >> >> >>> case "bar" => 3; >> >> >> >>> case "baz" => 6; >> >> >> >>> default => 99; >> >> >> >>> } >> >> >> >>> >> >> >> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >> >> >>> >> >> >> >>> const nextState = switch (currentState) { >> >> >> >>> case ... => >> >> >> >>> } >> >> >> >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> >> >>> >> >> >> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >> >> >>> >> >> >> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >> >> >>> >> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> Jordan, >> >> >> >>> >> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >> >>> >> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >> >>> >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >> >>> >> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Hi, >> >> >> >>> >> >> >> >>> This would be covered by do expressions. You could just do: >> >> >> >>> >> >> >> >>> ```js >> >> >> >>> const category = do { >> >> >> >>> switch (...) { >> >> >> >>> ... >> >> >> >>> }; >> >> >> >>> }; >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >> >>> cases where our code base has a pattern similar to this: >> >> >> >>> >> >> >> >>> let category = data.category; >> >> >> >>> >> >> >> >>> if (category === undefined) { >> >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >> >>> category = PaymentCategory.RENT; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> } >> >> >> >>> } >> >> >> >>> >> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >> >>> problem really create a simple result: >> >> >> >>> >> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >> >>> PaymentCategory.ROYALTIES; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >> >>> PaymentCategory.SERVICES; >> >> >> >>> default => PaymentCategory.OTHER; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >> >>> >> >> >> >>> const quarter = switch (foo) { >> >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >> >>> the existing language. In one of their examples they provide an example >> >> >> >>> of the Redux reducer >> >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >> >>> a switch expression implementation. >> >> >> >>> >> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >> >>> (action.type) { >> >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >> >>> action.filter }; >> >> >> >>> case ADD_TODO => { >> >> >> >>> ...state, todos: [ >> >> >> >>> ...state.todos, >> >> >> >>> { >> >> >> >>> text: action.text, >> >> >> >>> completed: false >> >> >> >>> } >> >> >> >>> ] >> >> >> >>> }; >> >> >> >>> case TOGGLE_TODO => { >> >> >> >>> ...state, >> >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >> >>> }; >> >> >> >>> default => state; >> >> >> >>> } >> >> >> >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> _______________________________________________ >> >> >> >>> 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 >> >> >> >>> >> >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: >> >> >> >>> >> >> >> >>> Jordan, >> >> >> >>> >> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >> >>> >> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >> >>> >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >> >>> >> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Hi, >> >> >> >>> >> >> >> >>> This would be covered by do expressions. You could just do: >> >> >> >>> >> >> >> >>> ```js >> >> >> >>> const category = do { >> >> >> >>> switch (...) { >> >> >> >>> ... >> >> >> >>> }; >> >> >> >>> }; >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >> >>> cases where our code base has a pattern similar to this: >> >> >> >>> >> >> >> >>> let category = data.category; >> >> >> >>> >> >> >> >>> if (category === undefined) { >> >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >> >>> category = PaymentCategory.RENT; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> } >> >> >> >>> } >> >> >> >>> >> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >> >>> problem really create a simple result: >> >> >> >>> >> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >> >>> PaymentCategory.ROYALTIES; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >> >>> PaymentCategory.SERVICES; >> >> >> >>> default => PaymentCategory.OTHER; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >> >>> >> >> >> >>> const quarter = switch (foo) { >> >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >> >>> the existing language. In one of their examples they provide an example >> >> >> >>> of the Redux reducer >> >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >> >>> a switch expression implementation. >> >> >> >>> >> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >> >>> (action.type) { >> >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >> >>> action.filter }; >> >> >> >>> case ADD_TODO => { >> >> >> >>> ...state, todos: [ >> >> >> >>> ...state.todos, >> >> >> >>> { >> >> >> >>> text: action.text, >> >> >> >>> completed: false >> >> >> >>> } >> >> >> >>> ] >> >> >> >>> }; >> >> >> >>> case TOGGLE_TODO => { >> >> >> >>> ...state, >> >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >> >>> }; >> >> >> >>> default => state; >> >> >> >>> } >> >> >> >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> _______________________________________________ >> >> >> >>> 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 >> >> >> >>> >> >> >> >>> _______________________________________________ >> >> >> >>> 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 >> >> >> > >> >> >> > _______________________________________________ >> >> >> > es-discuss mailing list >> >> >> > es-discuss at mozilla.org >> >> >> > https://mail.mozilla.org/listinfo/es-discuss >> >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> > >> > The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway). >> > >> > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? >> > >> > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> I'm looking at Three.js's code base, and I'm not seeing any method >> >> overriding or abstract methods used except at the API level for >> >> cloning and copying. Instead, you update properties on the supertype. >> >> As far as I can tell, the entirety of Three.js could almost be >> >> mechanically refactored in terms of components instead of inheritance, >> >> without substantially modifying the API apart from a few extra >> >> `.geometry`/etc. property accesses when calling supertype methods. >> >> It's data-driven and almost ECS. (It uses `.isObject3D`, >> >> `.isPerspectiveCamera`, and similar brand checks, but those don't >> >> *need* to be inherited to work.) >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> > >> >> > I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. >> >> > >> >> > If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. >> >> > >> >> > As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. >> >> > >> >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> >> >> Sometimes, this is actually *desired*, and most cases where I could've >> >> >> used this, inheritance was not involved *anywhere*. Also, in >> >> >> performance-sensitive contexts (like games, which *heavily* use >> >> >> `switch`/`case`), method dispatch is *far* slower than a simple >> >> >> `switch` statement, so that pattern doesn't apply everywhere. >> >> >> >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over >> >> >> this anyways - it covers more use cases and is all around more >> >> >> flexible, so I get more bang for the buck. >> >> >> >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> contact at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> >> > >> >> >> > Hi David! >> >> >> > >> >> >> > Your last example would, I think, be better served by classes and inheritance, than switch. >> >> >> > >> >> >> > Dogs are house animals which are animals >> >> >> > Cheetas are wild cats which are animals >> >> >> > >> >> >> > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> >> >> > >> >> >> > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> >> >> > >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> > >> >> >> > All thoughts on this are welcome. Do let me know >> >> >> > >> >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> >> >> >> >> >> >> Naveen, >> >> >> >> >> >> >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> switch (animal) { >> >> >> >> case Animal.DOG, Animal.CAT => { >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> } >> >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> } >> >> >> >> case Animal.ELEPHANT => "hay"; >> >> >> >> default => { throw new Error("Unsupported Animal"); }; >> >> >> >> } >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> function houseAnimal() { >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> } >> >> >> >> >> >> >> >> function wildCatFood() { >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> } >> >> >> >> >> >> >> >> >> >> >> >> const cases = { >> >> >> >> [Animal.DOG]: houseAnimal, >> >> >> >> [Animal.CAT]: houseAnimal, >> >> >> >> [Animal.LION]: wildCatFood, >> >> >> >> [Animal.TIGER]: wildCatFood, >> >> >> >> [Animal.CHEETA]: wildCatFood, >> >> >> >> } >> >> >> >> >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> >> >> >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> >> >> >> >> >> >> Isn't the best existing pattern an object literal? >> >> >> >> >> >> >> >> const >> >> >> >> cases = >> >> >> >> { >> >> >> >> foo: ()=>1, >> >> >> >> bar: ()=>3, >> >> >> >> baz: ()=>6 >> >> >> >> } >> >> >> >> , >> >> >> >> x = >> >> >> >> cases[v] ? >> >> >> >> cases[v]() : >> >> >> >> 99 >> >> >> >> ; >> >> >> >> >> >> >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> >> >> >> >> >> >> const >> >> >> >> x = >> >> >> >> { >> >> >> >> foo: ()=>1, >> >> >> >> bar: ()=>3, >> >> >> >> baz: ()=>6 >> >> >> >> }[v]?.() >> >> >> >> || >> >> >> >> 99 >> >> >> >> ; >> >> >> >> >> >> >> >> Do let me know your thoughts guys >> >> >> >> >> >> >> >> >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >> >> >> >>> >> >> >> >>> This is unmaintainable -- >> >> >> >>> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >>> >> >> >> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >> >> >> >>> >> >> >> >>> ```javascript >> >> >> >>> /*jslint*/ >> >> >> >>> "use strict"; >> >> >> >>> const v = "foo"; >> >> >> >>> const x = ( >> >> >> >>> v === "foo" >> >> >> >>> ? 1 >> >> >> >>> : v === "bar" >> >> >> >>> ? 3 >> >> >> >>> : v === "baz" >> >> >> >>> ? 6 >> >> >> >>> : 99 >> >> >> >>> ); >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >> >> >> >>> >> >> >> >>> ```javascript >> >> >> >>> $ node -e ' >> >> >> >>> /*jslint devel*/ >> >> >> >>> "use strict"; >> >> >> >>> function renderRecent(date) { >> >> >> >>> /* >> >> >> >>> * this function will render <date> to "xxx ago" >> >> >> >>> */ >> >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >> >> >> >>> return ( >> >> >> >>> !Number.isFinite(date) >> >> >> >>> ? "" >> >> >> >>> : date < 60 >> >> >> >>> ? date + " sec ago" >> >> >> >>> : date < 3600 >> >> >> >>> ? Math.round(date / 60) + " min ago" >> >> >> >>> : date < 86400 >> >> >> >>> ? Math.round(date / 3600) + " hr ago" >> >> >> >>> : date < 129600 >> >> >> >>> ? "1 day ago" >> >> >> >>> : Math.round(date / 86400) + " days ago" >> >> >> >>> ); >> >> >> >>> } >> >> >> >>> >> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> >> >> >>> ' >> >> >> >>> >> >> >> >>> 0 sec ago >> >> >> >>> 10 sec ago >> >> >> >>> 5 min ago >> >> >> >>> 18 min ago >> >> >> >>> 2 hr ago >> >> >> >>> 16 days ago >> >> >> >>> 365 days ago >> >> >> >>> >> >> >> >>> $ >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >> >> >> >>> >> >> >> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> >> >>> >> >> >> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >> >> >> >>> >> >> >> >>> Thanks >> >> >> >>> >> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >> >> >>> >> >> >> >>> You're not alone in wanting pattern matching to be expression-based: >> >> >> >>> >> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> >> >>> >> >> >> >>> ----- >> >> >> >>> >> >> >> >>> Isiah Meadows >> >> >> >>> contact at isiahmeadows.com >> >> >> >>> www.isiahmeadows.com >> >> >> >>> >> >> >> >>> ----- >> >> >> >>> >> >> >> >>> Isiah Meadows >> >> >> >>> contact at isiahmeadows.com >> >> >> >>> www.isiahmeadows.com >> >> >> >>> >> >> >> >>> >> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> Jordan, >> >> >> >>> >> >> >> >>> Thanks for taking time to read and provide thoughts. >> >> >> >>> >> >> >> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >> >> >>> >> >> >> >>> This is unmaintainable -- >> >> >> >>> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >>> >> >> >> >>> This is maintainable, but is less than ideal: >> >> >> >>> >> >> >> >>> let x; >> >> >> >>> >> >> >> >>> switch (v) { >> >> >> >>> case "foo": >> >> >> >>> x = 1; >> >> >> >>> break; >> >> >> >>> case "bar": >> >> >> >>> x = 3; >> >> >> >>> break; >> >> >> >>> case "baz": >> >> >> >>> x = 6; >> >> >> >>> break; >> >> >> >>> default: >> >> >> >>> x = 99; >> >> >> >>> break; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >> >> >>> >> >> >> >>> let x; >> >> >> >>> >> >> >> >>> case (v) { >> >> >> >>> when "foo" -> x = 1; >> >> >> >>> when "bar" -> x = 3; >> >> >> >>> when "baz" -> x = 6; >> >> >> >>> when v -> x = 99; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> >> >>> >> >> >> >>> const x = do { >> >> >> >>> if (v === "foo") { 1; } >> >> >> >>> else if (v === "bar") { 3; } >> >> >> >>> else if (v === "baz") { 6; } >> >> >> >>> else { 99; } >> >> >> >>> } >> >> >> >>> >> >> >> >>> Or as another do expression variant: >> >> >> >>> >> >> >> >>> const x = do { >> >> >> >>> switch (v) { >> >> >> >>> case "foo": 1; break; >> >> >> >>> case "bar": 3; break; >> >> >> >>> case "baz": 6; break; >> >> >> >>> default: 99; break; >> >> >> >>> } >> >> >> >>> } >> >> >> >>> >> >> >> >>> And as I'm thinking about switch expressions: >> >> >> >>> >> >> >> >>> const x = switch (v) { >> >> >> >>> case "foo" => 1; >> >> >> >>> case "bar" => 3; >> >> >> >>> case "baz" => 6; >> >> >> >>> default => 99; >> >> >> >>> } >> >> >> >>> >> >> >> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >> >> >>> >> >> >> >>> const nextState = switch (currentState) { >> >> >> >>> case ... => >> >> >> >>> } >> >> >> >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> >> >>> >> >> >> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >> >> >>> >> >> >> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >> >> >>> >> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> Jordan, >> >> >> >>> >> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >> >>> >> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >> >>> >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >> >>> >> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Hi, >> >> >> >>> >> >> >> >>> This would be covered by do expressions. You could just do: >> >> >> >>> >> >> >> >>> ```js >> >> >> >>> const category = do { >> >> >> >>> switch (...) { >> >> >> >>> ... >> >> >> >>> }; >> >> >> >>> }; >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >> >>> cases where our code base has a pattern similar to this: >> >> >> >>> >> >> >> >>> let category = data.category; >> >> >> >>> >> >> >> >>> if (category === undefined) { >> >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >> >>> category = PaymentCategory.RENT; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> } >> >> >> >>> } >> >> >> >>> >> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >> >>> problem really create a simple result: >> >> >> >>> >> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >> >>> PaymentCategory.ROYALTIES; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >> >>> PaymentCategory.SERVICES; >> >> >> >>> default => PaymentCategory.OTHER; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >> >>> >> >> >> >>> const quarter = switch (foo) { >> >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >> >>> } >> >> >> >>> >> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >> >>> the existing language. In one of their examples they provide an example >> >> >> >>> of the Redux reducer >> >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >> >>> a switch expression implementation. >> >> >> >>> >> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >> >>> (action.type) { >> >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >> >>> action.filter }; >> >> >> >>> case ADD_TODO => { >> >> >> >>> ...state, todos: [ >> >> >> >>> ...state.todos, >> >> >> >>> { >> >> >> >>> text: action.text, >> >> >> >>> completed: false >> >> >> >>> } >> >> >> >>> ] >> >> >> >>> }; >> >> >> >>> case TOGGLE_TODO => { >> >> >> >>> ...state, >> >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >> >>> }; >> >> >> >>> default => state; >> >> >> >>> } >> >> >> >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> _______________________________________________ >> >> >> >>> 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 >> >> >> >>> >> >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: >> >> >> >>> >> >> >> >>> Jordan, >> >> >> >>> >> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >> >>> >> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >> >>> >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >> >>> >> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >> >>> >> >> >> >>> --David >> >> >> >>> >> >> >> >>> >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >> >>> >> >> >> >>> Hi, >> >> >> >>> >> >> >> >>> This would be covered by do expressions. You could just do: >> >> >> >>> >> >> >> >>> ```js >> >> >> >>> const category = do { >> >> >> >>> switch (...) { >> >> >> >>> ... >> >> >> >>> }; >> >> >> >>> }; >> >> >> >>> ``` >> >> >> >>> >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >> >>> >> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >> >>> cases where our code base has a pattern similar to this: >> >> >> >>> >> >> >> >>> let category = data.category; >> >> >> >>> >> >> >> >>> if (category === undefined) { >> >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >> >>> category = PaymentCategory.RENT; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >>> break; >> >> >> >>> } >> >> >> >>> } >> >> >> >>> >> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >> >>> problem really create a simple result: >> >> >> >>> >> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >> >>> PaymentCategory.ROYALTIES;
It "simulates" then does it for real, hence slower than just doing it (there is no need to simulate if we have the paradigm we are are trying to benchmark ready for us to just use). I'd be curious to see a direct benchmark comparison, but don't have time to set it up right now.
It "simulates" then does it for real, hence slower than just doing it (there is no need to simulate if we have the paradigm we are are trying to benchmark ready for us to just use). I'd be curious to see a direct benchmark comparison, but don't have time to set it up right now. On Sat, 2 Mar 2019, 2:08 pm Isiah Meadows, <isiahmeadows at gmail.com> wrote: > IIUC the "object dispatch integer"/"object dispatch string" benchmarks > are the things you were referring to. Those simulate what the engine > would see with virtual dispatch and completely different type maps, > just without the source overhead of creating an entire class just for > a little benchmark. > > And also, engines *won't* be able to optimize them generally, because > there could be infinitely many type maps, and after about 200 or so > types, the `switch` statement ends up *much* slower. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > On Sat, Mar 2, 2019 at 3:43 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > > > > I don't think those benchmarks test exactly what we are talking about. > They have a dictionary/array look up followed by method dispatch, vs switch > case and execute. Removing the look up it would be: `x.doStuff()` vs > `switch(x.type)...`. Make sense? Don't have time to do it right now. > > > > Logically I think the JS engine can make them perform identically, so > even if benchmarks show something today, I would not be concerned for the > future and so would prefer to opt for the paradigm that offers the best > manageability, which I think is inheritance by a significant margin, in the > cases mentioned. Other types of cases could of course be a whole different > story. > > > > On Sat, 2 Mar 2019, 5:24 am Isiah Meadows, <isiahmeadows at gmail.com> > wrote: > >> > >> > It would be unthinkable for it to use pattern matching or explicit > code branchinI'm g instead of method inheritance for type disambiguation > during render > >> > >> But it frequently does internally. For example: > >> > >> - Calculating object projections: > >> > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1243-L1351 > >> - Rendering object *lists*: > >> > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1353-L1411 > >> - Setting the rendering mode and controlling basic rendering: > >> > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L802-L874 > >> > >> Obviously, it exposes a data-oriented, object oriented API. And it > >> does appear it's not *exclusively* conditionals: > >> > >> - It invokes an dynamic `render` method for "immediate render > >> objects": > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L636-L644 > >> - In `renderBufferDirect`, it does virtual method dispatch on `render` > >> based on one of two possible types, but it otherwise uses conditionals > >> for everything:\* > >> > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L707-L876 > >> - It uses a fair bit of functional programming in `compile`: > >> > https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1009-L1054 > >> > >> However, I'm finding exceptions in its core and renderers, and it > >> doesn't appear virtual dispatch is *that* broadly and pervasively > >> used, even though it uses methods a lot. > >> > >> \* This seems like overkill when the diff between the two renderers in > >> question [1] [2] consist of an extra method + 2 extra variables [3] > >> [4], a few changed method invocations [5] [6], and the rest just due > >> to a different name and a useless `var`. > >> > >> [1]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLBufferRenderer.js > >> [2]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js > >> [3]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L15-L22 > >> [4]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L61 > >> [5]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L26 > >> [6]: > https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L53 > >> > >> > I'm curious where you got the idea that method invocation is "far" > slower than explicit code branching? > >> > >> - In C++: https://stackoverflow.com/a/8866789 > >> - JS benchmark with 4 variants (typed method dispatch is polymorphic): > >> http://jsben.ch/fbJQH > >> - JS benchmark with 12 variants (typed method dispatch is > >> megamorphic): http://jsben.ch/aWNDN > >> > >> And in my experience, the speed difference in real-world > >> performance-critical code is not unlike that microbenchmark and is > >> sometimes even more drastic, especially if it's a linked list instead > >> of just a simple array lookup. > >> > >> I'd like to emphasize I'm specifically referring to the case where the > >> engine can't reliably assume a single method receiver, i.e. when it > >> *has* to fall back to dynamic dispatch. > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> > > >> > The entire renderers, cameras, meshes etc. hierarchy uses method > inheritance and many of those methods are called during scene rendering > (which is performance sensitive as it happens per frame). It would be > unthinkable for it to use pattern matching or explicit code branching > instead of method inheritance for type disambiguation during render, > because it would explode the code as well as making it error prone due to > initial cases potentially unintentionally swallowing up cases intended for > later code branches (or unintentionally repeating code branches if the > pattern-matching proposal doesn't have "else" behaviour, of which I'm not > sure, but it if does, it suffers from the first problem anyway). > >> > > >> > I'm curious where you got the idea that method invocation is "far" > slower than explicit code branching? > >> > > >> > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> > >> >> I'm looking at Three.js's code base, and I'm not seeing any method > >> >> overriding or abstract methods used except at the API level for > >> >> cloning and copying. Instead, you update properties on the supertype. > >> >> As far as I can tell, the entirety of Three.js could almost be > >> >> mechanically refactored in terms of components instead of > inheritance, > >> >> without substantially modifying the API apart from a few extra > >> >> `.geometry`/etc. property accesses when calling supertype methods. > >> >> It's data-driven and almost ECS. (It uses `.isObject3D`, > >> >> `.isPerspectiveCamera`, and similar brand checks, but those don't > >> >> *need* to be inherited to work.) > >> >> > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> contact at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla < > naveen.chwl at gmail.com> wrote: > >> >> > > >> >> > I'm not sure that pattern matching handles deep levels of > inheritance more elegantly than inheritance itself. > >> >> > > >> >> > If there is a conceptual type hierarchy, then the ability to call > "super", combine it with specialized functionality, etc. is a lot more > manageable using localized, separated logic where you don't feel forced to > read "other patterns" to understand whether your target functionality will > resolve correctly. And hence, a lower chance of bugs. > >> >> > > >> >> > As for performance, I'd have to see modern benchmarks. But it's > not necessarily clear that pattern matching will be particularly fast > either. I've done game programming with method overriding (Three.js uses it > too throughout) and there is no notable performance hit from doing so. So > I'm not clear where you have got this information from. > >> >> > > >> >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> >> > >> >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> >> > >> >> >> Sometimes, this is actually *desired*, and most cases where I > could've > >> >> >> used this, inheritance was not involved *anywhere*. Also, in > >> >> >> performance-sensitive contexts (like games, which *heavily* use > >> >> >> `switch`/`case`), method dispatch is *far* slower than a simple > >> >> >> `switch` statement, so that pattern doesn't apply everywhere. > >> >> >> > >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ > over > >> >> >> this anyways - it covers more use cases and is all around more > >> >> >> flexible, so I get more bang for the buck. > >> >> >> > >> >> >> ----- > >> >> >> > >> >> >> Isiah Meadows > >> >> >> contact at isiahmeadows.com > >> >> >> www.isiahmeadows.com > >> >> >> > >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla < > naveen.chwl at gmail.com> wrote: > >> >> >> > > >> >> >> > Hi David! > >> >> >> > > >> >> >> > Your last example would, I think, be better served by classes > and inheritance, than switch. > >> >> >> > > >> >> >> > Dogs are house animals which are animals > >> >> >> > Cheetas are wild cats which are animals > >> >> >> > > >> >> >> > Each could have overridden methods, entirely optionally, where > the method gets called and resolves appropriately. > >> >> >> > > >> >> >> > The input argument could be the class name, from which it is > trivial to instantiate a new instance and get required results. > >> >> >> > > >> >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> >> > > >> >> >> > All thoughts on this are welcome. Do let me know > >> >> >> > > >> >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> > wrote: > >> >> >> >> > >> >> >> >> Naveen, > >> >> >> >> > >> >> >> >> Thanks for your observation. The example that I gave might > have been too simplistic, here's a more complete example: > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> switch (animal) { > >> >> >> >> case Animal.DOG, Animal.CAT => { > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "dry food"; > >> >> >> >> } > >> >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "fresh meat"; > >> >> >> >> } > >> >> >> >> case Animal.ELEPHANT => "hay"; > >> >> >> >> default => { throw new Error("Unsupported Animal"); }; > >> >> >> >> } > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> While you give examples that would totally work. Things that > bother me about the approach are, when taken to something more complex than > a quick value for value switch you end up with something that looks like > this. > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> function houseAnimal() { > >> >> >> >> > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "dry food"; > >> >> >> >> } > >> >> >> >> > >> >> >> >> function wildCatFood() { > >> >> >> >> > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "fresh meat"; > >> >> >> >> } > >> >> >> >> > >> >> >> >> > >> >> >> >> const cases = { > >> >> >> >> [Animal.DOG]: houseAnimal, > >> >> >> >> [Animal.CAT]: houseAnimal, > >> >> >> >> [Animal.LION]: wildCatFood, > >> >> >> >> [Animal.TIGER]: wildCatFood, > >> >> >> >> [Animal.CHEETA]: wildCatFood, > >> >> >> >> } > >> >> >> >> > >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => > {throw new Error("Unsuppored Animal")})(); > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> As we all know once any language reaches a basic level of > functionality anything is possible. What I think is that JavaScript would > benefit by having a cleaner approach. > >> >> >> >> > >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: > >> >> >> >> > >> >> >> >> Isn't the best existing pattern an object literal? > >> >> >> >> > >> >> >> >> const > >> >> >> >> cases = > >> >> >> >> { > >> >> >> >> foo: ()=>1, > >> >> >> >> bar: ()=>3, > >> >> >> >> baz: ()=>6 > >> >> >> >> } > >> >> >> >> , > >> >> >> >> x = > >> >> >> >> cases[v] ? > >> >> >> >> cases[v]() : > >> >> >> >> 99 > >> >> >> >> ; > >> >> >> >> > >> >> >> >> What does any proposal have that is better than this? With > optional chaining feature: > >> >> >> >> > >> >> >> >> const > >> >> >> >> x = > >> >> >> >> { > >> >> >> >> foo: ()=>1, > >> >> >> >> bar: ()=>3, > >> >> >> >> baz: ()=>6 > >> >> >> >> }[v]?.() > >> >> >> >> || > >> >> >> >> 99 > >> >> >> >> ; > >> >> >> >> > >> >> >> >> Do let me know your thoughts guys > >> >> >> >> > >> >> >> >> > >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> > wrote: > >> >> >> >>> > >> >> >> >>> This is unmaintainable -- > >> >> >> >>> > >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' > ? 6 : 99; > >> >> >> >>> > >> >> >> >>> i feel proposed switch-expressions are no more > readable/maintainable than ternary-operators, if you follow jslint's > style-guide. i'll like to see more convincing evidence/use-case that they > are better: > >> >> >> >>> > >> >> >> >>> ```javascript > >> >> >> >>> /*jslint*/ > >> >> >> >>> "use strict"; > >> >> >> >>> const v = "foo"; > >> >> >> >>> const x = ( > >> >> >> >>> v === "foo" > >> >> >> >>> ? 1 > >> >> >> >>> : v === "bar" > >> >> >> >>> ? 3 > >> >> >> >>> : v === "baz" > >> >> >> >>> ? 6 > >> >> >> >>> : 99 > >> >> >> >>> ); > >> >> >> >>> ``` > >> >> >> >>> > >> >> >> >>> here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > >> >> >> >>> > >> >> >> >>> ```javascript > >> >> >> >>> $ node -e ' > >> >> >> >>> /*jslint devel*/ > >> >> >> >>> "use strict"; > >> >> >> >>> function renderRecent(date) { > >> >> >> >>> /* > >> >> >> >>> * this function will render <date> to "xxx ago" > >> >> >> >>> */ > >> >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) > * 0.0001) * 10; > >> >> >> >>> return ( > >> >> >> >>> !Number.isFinite(date) > >> >> >> >>> ? "" > >> >> >> >>> : date < 60 > >> >> >> >>> ? date + " sec ago" > >> >> >> >>> : date < 3600 > >> >> >> >>> ? Math.round(date / 60) + " min ago" > >> >> >> >>> : date < 86400 > >> >> >> >>> ? Math.round(date / 3600) + " hr ago" > >> >> >> >>> : date < 129600 > >> >> >> >>> ? "1 day ago" > >> >> >> >>> : Math.round(date / 86400) + " days ago" > >> >> >> >>> ); > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 > sec ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec > ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min > ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min > ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr > ago" > >> >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 > days ago" > >> >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 > days ago" > >> >> >> >>> ' > >> >> >> >>> > >> >> >> >>> 0 sec ago > >> >> >> >>> 10 sec ago > >> >> >> >>> 5 min ago > >> >> >> >>> 18 min ago > >> >> >> >>> 2 hr ago > >> >> >> >>> 16 days ago > >> >> >> >>> 365 days ago > >> >> >> >>> > >> >> >> >>> $ > >> >> >> >>> ``` > >> >> >> >>> > >> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> > wrote: > >> >> >> >>> > >> >> >> >>> Just for folks who might be interested, added a babel-plugin > to see what was involved in making this possible. > >> >> >> >>> > >> >> >> >>> Pull request available here -- > https://github.com/babel/babel/pull/9604 > >> >> >> >>> > >> >> >> >>> I'm sure I'm missing a bunch of details, but would be > interested in some help in making this a bit more real. > >> >> >> >>> > >> >> >> >>> Thanks > >> >> >> >>> > >> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: > >> >> >> >>> > >> >> >> >>> You're not alone in wanting pattern matching to be > expression-based: > >> >> >> >>> > >> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 > >> >> >> >>> > >> >> >> >>> ----- > >> >> >> >>> > >> >> >> >>> Isiah Meadows > >> >> >> >>> contact at isiahmeadows.com > >> >> >> >>> www.isiahmeadows.com > >> >> >> >>> > >> >> >> >>> ----- > >> >> >> >>> > >> >> >> >>> Isiah Meadows > >> >> >> >>> contact at isiahmeadows.com > >> >> >> >>> www.isiahmeadows.com > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> Jordan, > >> >> >> >>> > >> >> >> >>> Thanks for taking time to read and provide thoughts. > >> >> >> >>> > >> >> >> >>> I just back and re-read the pattern matching proposal and it > still fails on the basic requirement of being an Expression not a > Statement. The problem that I see and want to address is the need to have > something that removes the need to chain trinary expressions together to > have an Expression. > >> >> >> >>> > >> >> >> >>> This is unmaintainable -- > >> >> >> >>> > >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' > ? 6 : 99; > >> >> >> >>> > >> >> >> >>> This is maintainable, but is less than ideal: > >> >> >> >>> > >> >> >> >>> let x; > >> >> >> >>> > >> >> >> >>> switch (v) { > >> >> >> >>> case "foo": > >> >> >> >>> x = 1; > >> >> >> >>> break; > >> >> >> >>> case "bar": > >> >> >> >>> x = 3; > >> >> >> >>> break; > >> >> >> >>> case "baz": > >> >> >> >>> x = 6; > >> >> >> >>> break; > >> >> >> >>> default: > >> >> >> >>> x = 99; > >> >> >> >>> break; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Pattern matching does shorten the code, but you have a weird > default case and also still end up with a loose variable and since pattern > matching is a statement you still have a initially undefined variable. > >> >> >> >>> > >> >> >> >>> let x; > >> >> >> >>> > >> >> >> >>> case (v) { > >> >> >> >>> when "foo" -> x = 1; > >> >> >> >>> when "bar" -> x = 3; > >> >> >> >>> when "baz" -> x = 6; > >> >> >> >>> when v -> x = 99; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Let's try do expressions, I'll leave people's thoughts to > themselves. > >> >> >> >>> > >> >> >> >>> const x = do { > >> >> >> >>> if (v === "foo") { 1; } > >> >> >> >>> else if (v === "bar") { 3; } > >> >> >> >>> else if (v === "baz") { 6; } > >> >> >> >>> else { 99; } > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Or as another do expression variant: > >> >> >> >>> > >> >> >> >>> const x = do { > >> >> >> >>> switch (v) { > >> >> >> >>> case "foo": 1; break; > >> >> >> >>> case "bar": 3; break; > >> >> >> >>> case "baz": 6; break; > >> >> >> >>> default: 99; break; > >> >> >> >>> } > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> And as I'm thinking about switch expressions: > >> >> >> >>> > >> >> >> >>> const x = switch (v) { > >> >> >> >>> case "foo" => 1; > >> >> >> >>> case "bar" => 3; > >> >> >> >>> case "baz" => 6; > >> >> >> >>> default => 99; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> What I really like is that it preserves all of the normal > JavaScript syntax with the small change that a switch is allowed in an > expression provided that all of the cases evaluate to expressions hence the > use of the '=>' as an indicator. Fundamentally this is a very basic > concept where you have a state machine and need it switch based on the > current state and evaluate to the new state. > >> >> >> >>> > >> >> >> >>> const nextState = switch (currentState) { > >> >> >> >>> case ... => > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: > >> >> >> >>> > >> >> >> >>> Pattern Matching is still at stage 1; so there's not really > any permanent decisions that have been made - the repo theoretically should > contain rationales for decisions up to this point. > >> >> >> >>> > >> >> >> >>> I can speak for myself (as "not a champion" of that proposal, > just a fan) that any similarity to the reviled and terrible `switch` is > something I'll be pushing back against - I want a replacement that lacks > the footguns and pitfalls of `switch`, and that is easily teachable and > googleable as a different, distinct thing. > >> >> >> >>> > >> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> Jordan, > >> >> >> >>> > >> >> >> >>> One question that I have lingering from pattern matching is > why is the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >> >> >>> > >> >> >> >>> Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > >> >> >> >>> > >> >> >> >>> --David > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband < > ljharb at gmail.com> wrote: > >> >> >> >>> > >> >> >> >>> Additionally, > https://github.com/tc39/proposal-pattern-matching - switch statements are > something I hope we'll soon be able to relegate to the dustbin of history. > >> >> >> >>> > >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> I quite aware that it’s covered in do expressions. Personally > I find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >> >> >>> > >> >> >> >>> Hence why I wanted to put out there the idea of switch > expressions. > >> >> >> >>> > >> >> >> >>> --David > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> > wrote: > >> >> >> >>> > >> >> >> >>> Hi, > >> >> >> >>> > >> >> >> >>> This would be covered by do expressions. You could just do: > >> >> >> >>> > >> >> >> >>> ```js > >> >> >> >>> const category = do { > >> >> >> >>> switch (...) { > >> >> >> >>> ... > >> >> >> >>> }; > >> >> >> >>> }; > >> >> >> >>> ``` > >> >> >> >>> > >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> After looking at a bunch of code in our system noted that > there are many > >> >> >> >>> cases where our code base has a pattern similar to this: > >> >> >> >>> > >> >> >> >>> let category = data.category; > >> >> >> >>> > >> >> >> >>> if (category === undefined) { > >> >> >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >> >> >>> case TaxIncomeCode.RENTS_14: > >> >> >> >>> category = PaymentCategory.RENT; > >> >> >> >>> break; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >> >>> category = PaymentCategory.SERVICES; > >> >> >> >>> break; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >> >>> category = PaymentCategory.SERVICES; > >> >> >> >>> break; > >> >> >> >>> } > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> I also bumped into a block of go code that also implemented > similar > >> >> >> >>> patterns, which really demonstrated to me that there while > you could go > >> >> >> >>> crazy with triary nesting there should be a better way. > Looked at the > >> >> >> >>> pattern matching proposal and while could possibly help > looked like it > >> >> >> >>> was overkill for the typical use case that I'm seeing. The > most relevant > >> >> >> >>> example I noted was switch expressions from Java. When > applied to this > >> >> >> >>> problem really create a simple result: > >> >> >> >>> > >> >> >> >>> const category = data.category || switch > (setting.incomeCode) { > >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >> >> >>> PaymentCategory.ROYALTIES; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >> >> >> >>> PaymentCategory.SERVICES; > >> >> >> >>> default => PaymentCategory.OTHER; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Note; the instead of using the '->' as Java, continue to use > => and with > >> >> >> >>> the understanding that the right hand side is fundamentally > function. > >> >> >> >>> So similar things to this are natural, note this proposal > should remove > >> >> >> >>> "fall through" breaks and allow for multiple cases as such. > >> >> >> >>> > >> >> >> >>> const quarter = switch (foo) { > >> >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; > >> >> >> >>> case "Apr", "May", "Jun" => "Q2"; > >> >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; > >> >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >> >> >> >>> default => { throw new Error("Invalid Month") }; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Also compared this to the do expression proposal, it also > provides a > >> >> >> >>> substantial simplification, but in a way that is more > consistent with > >> >> >> >>> the existing language. In one of their examples they provide > an example > >> >> >> >>> of the Redux reducer > >> >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- > this would be > >> >> >> >>> a switch expression implementation. > >> >> >> >>> > >> >> >> >>> function todoApp(state = initialState, action) => switch > >> >> >> >>> (action.type) { > >> >> >> >>> case SET_VISIBILITY_FILTER => { ...state, > visibilityFilter: > >> >> >> >>> action.filter }; > >> >> >> >>> case ADD_TODO => { > >> >> >> >>> ...state, todos: [ > >> >> >> >>> ...state.todos, > >> >> >> >>> { > >> >> >> >>> text: action.text, > >> >> >> >>> completed: false > >> >> >> >>> } > >> >> >> >>> ] > >> >> >> >>> }; > >> >> >> >>> case TOGGLE_TODO => { > >> >> >> >>> ...state, > >> >> >> >>> todos: state.todos.map((todo, index) => (index === > >> >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : > todo) > >> >> >> >>> }; > >> >> >> >>> default => state; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> _______________________________________________ > >> >> >> >>> 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 > >> >> >> >>> > >> >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: > >> >> >> >>> > >> >> >> >>> Jordan, > >> >> >> >>> > >> >> >> >>> One question that I have lingering from pattern matching is > why is the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >> >> >>> > >> >> >> >>> Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > >> >> >> >>> > >> >> >> >>> --David > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband < > ljharb at gmail.com> wrote: > >> >> >> >>> > >> >> >> >>> Additionally, > https://github.com/tc39/proposal-pattern-matching - switch statements are > something I hope we'll soon be able to relegate to the dustbin of history. > >> >> >> >>> > >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> I quite aware that it’s covered in do expressions. Personally > I find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >> >> >>> > >> >> >> >>> Hence why I wanted to put out there the idea of switch > expressions. > >> >> >> >>> > >> >> >> >>> --David > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> > wrote: > >> >> >> >>> > >> >> >> >>> Hi, > >> >> >> >>> > >> >> >> >>> This would be covered by do expressions. You could just do: > >> >> >> >>> > >> >> >> >>> ```js > >> >> >> >>> const category = do { > >> >> >> >>> switch (...) { > >> >> >> >>> ... > >> >> >> >>> }; > >> >> >> >>> }; > >> >> >> >>> ``` > >> >> >> >>> > >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> After looking at a bunch of code in our system noted that > there are many > >> >> >> >>> cases where our code base has a pattern similar to this: > >> >> >> >>> > >> >> >> >>> let category = data.category; > >> >> >> >>> > >> >> >> >>> if (category === undefined) { > >> >> >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >> >> >>> case TaxIncomeCode.RENTS_14: > >> >> >> >>> category = PaymentCategory.RENT; > >> >> >> >>> break; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >> >>> category = PaymentCategory.SERVICES; > >> >> >> >>> break; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >> >>> category = PaymentCategory.SERVICES; > >> >> >> >>> break; > >> >> >> >>> } > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> I also bumped into a block of go code that also implemented > similar > >> >> >> >>> patterns, which really demonstrated to me that there while > you could go > >> >> >> >>> crazy with triary nesting there should be a better way. > Looked at the > >> >> >> >>> pattern matching proposal and while could possibly help > looked like it > >> >> >> >>> was overkill for the typical use case that I'm seeing. The > most relevant > >> >> >> >>> example I noted was switch expressions from Java. When > applied to this > >> >> >> >>> problem really create a simple result: > >> >> >> >>> > >> >> >> >>> const category = data.category || switch > (setting.incomeCode) { > >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; > >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => > >> >> >> >>> PaymentCategory.ROYALTIES; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => > >> >> >> >>> PaymentCategory.SERVICES; > >> >> >> >>> default => PaymentCategory.OTHER; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Note; the instead of using the '->' as Java, continue to use > => and with > >> >> >> >>> the understanding that the right hand side is fundamentally > function. > >> >> >> >>> So similar things to this are natural, note this proposal > should remove > >> >> >> >>> "fall through" breaks and allow for multiple cases as such. > >> >> >> >>> > >> >> >> >>> const quarter = switch (foo) { > >> >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; > >> >> >> >>> case "Apr", "May", "Jun" => "Q2"; > >> >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; > >> >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; > >> >> >> >>> default => { throw new Error("Invalid Month") }; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Also compared this to the do expression proposal, it also > provides a > >> >> >> >>> substantial simplification, but in a way that is more > consistent with > >> >> >> >>> the existing language. In one of their examples they provide > an example > >> >> >> >>> of the Redux reducer > >> >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- > this would be > >> >> >> >>> a switch expression implementation. > >> >> >> >>> > >> >> >> >>> function todoApp(state = initialState, action) => switch > >> >> >> >>> (action.type) { > >> >> >> >>> case SET_VISIBILITY_FILTER => { ...state, > visibilityFilter: > >> >> >> >>> action.filter }; > >> >> >> >>> case ADD_TODO => { > >> >> >> >>> ...state, todos: [ > >> >> >> >>> ...state.todos, > >> >> >> >>> { > >> >> >> >>> text: action.text, > >> >> >> >>> completed: false > >> >> >> >>> } > >> >> >> >>> ] > >> >> >> >>> }; > >> >> >> >>> case TOGGLE_TODO => { > >> >> >> >>> ...state, > >> >> >> >>> todos: state.todos.map((todo, index) => (index === > >> >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : > todo) > >> >> >> >>> }; > >> >> >> >>> default => state; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> _______________________________________________ > >> >> >> >>> 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 > >> >> >> >>> > >> >> >> >>> _______________________________________________ > >> >> >> >>> 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 > >> >> >> > > >> >> >> > _______________________________________________ > >> >> >> > es-discuss mailing list > >> >> >> > es-discuss at mozilla.org > >> >> >> > https://mail.mozilla.org/listinfo/es-discuss > >> > >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> > wrote: > >> > > >> > The entire renderers, cameras, meshes etc. hierarchy uses method > inheritance and many of those methods are called during scene rendering > (which is performance sensitive as it happens per frame). It would be > unthinkable for it to use pattern matching or explicit code branching > instead of method inheritance for type disambiguation during render, > because it would explode the code as well as making it error prone due to > initial cases potentially unintentionally swallowing up cases intended for > later code branches (or unintentionally repeating code branches if the > pattern-matching proposal doesn't have "else" behaviour, of which I'm not > sure, but it if does, it suffers from the first problem anyway). > >> > > >> > I'm curious where you got the idea that method invocation is "far" > slower than explicit code branching? > >> > > >> > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> > >> >> I'm looking at Three.js's code base, and I'm not seeing any method > >> >> overriding or abstract methods used except at the API level for > >> >> cloning and copying. Instead, you update properties on the supertype. > >> >> As far as I can tell, the entirety of Three.js could almost be > >> >> mechanically refactored in terms of components instead of > inheritance, > >> >> without substantially modifying the API apart from a few extra > >> >> `.geometry`/etc. property accesses when calling supertype methods. > >> >> It's data-driven and almost ECS. (It uses `.isObject3D`, > >> >> `.isPerspectiveCamera`, and similar brand checks, but those don't > >> >> *need* to be inherited to work.) > >> >> > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> contact at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla < > naveen.chwl at gmail.com> wrote: > >> >> > > >> >> > I'm not sure that pattern matching handles deep levels of > inheritance more elegantly than inheritance itself. > >> >> > > >> >> > If there is a conceptual type hierarchy, then the ability to call > "super", combine it with specialized functionality, etc. is a lot more > manageable using localized, separated logic where you don't feel forced to > read "other patterns" to understand whether your target functionality will > resolve correctly. And hence, a lower chance of bugs. > >> >> > > >> >> > As for performance, I'd have to see modern benchmarks. But it's > not necessarily clear that pattern matching will be particularly fast > either. I've done game programming with method overriding (Three.js uses it > too throughout) and there is no notable performance hit from doing so. So > I'm not clear where you have got this information from. > >> >> > > >> >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> >> > >> >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> >> > >> >> >> Sometimes, this is actually *desired*, and most cases where I > could've > >> >> >> used this, inheritance was not involved *anywhere*. Also, in > >> >> >> performance-sensitive contexts (like games, which *heavily* use > >> >> >> `switch`/`case`), method dispatch is *far* slower than a simple > >> >> >> `switch` statement, so that pattern doesn't apply everywhere. > >> >> >> > >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ > over > >> >> >> this anyways - it covers more use cases and is all around more > >> >> >> flexible, so I get more bang for the buck. > >> >> >> > >> >> >> ----- > >> >> >> > >> >> >> Isiah Meadows > >> >> >> contact at isiahmeadows.com > >> >> >> www.isiahmeadows.com > >> >> >> > >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla < > naveen.chwl at gmail.com> wrote: > >> >> >> > > >> >> >> > Hi David! > >> >> >> > > >> >> >> > Your last example would, I think, be better served by classes > and inheritance, than switch. > >> >> >> > > >> >> >> > Dogs are house animals which are animals > >> >> >> > Cheetas are wild cats which are animals > >> >> >> > > >> >> >> > Each could have overridden methods, entirely optionally, where > the method gets called and resolves appropriately. > >> >> >> > > >> >> >> > The input argument could be the class name, from which it is > trivial to instantiate a new instance and get required results. > >> >> >> > > >> >> >> > Using a "switch" here forces you to group classes of objects > together and then you don't get the 2nd, 3rd, 4th etc. levels of > specialization that you might later want. > >> >> >> > > >> >> >> > All thoughts on this are welcome. Do let me know > >> >> >> > > >> >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> > wrote: > >> >> >> >> > >> >> >> >> Naveen, > >> >> >> >> > >> >> >> >> Thanks for your observation. The example that I gave might > have been too simplistic, here's a more complete example: > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> switch (animal) { > >> >> >> >> case Animal.DOG, Animal.CAT => { > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "dry food"; > >> >> >> >> } > >> >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "fresh meat"; > >> >> >> >> } > >> >> >> >> case Animal.ELEPHANT => "hay"; > >> >> >> >> default => { throw new Error("Unsupported Animal"); }; > >> >> >> >> } > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> While you give examples that would totally work. Things that > bother me about the approach are, when taken to something more complex than > a quick value for value switch you end up with something that looks like > this. > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> function houseAnimal() { > >> >> >> >> > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "dry food"; > >> >> >> >> } > >> >> >> >> > >> >> >> >> function wildCatFood() { > >> >> >> >> > >> >> >> >> // larger block expression > >> >> >> >> // which spans multiple lines > >> >> >> >> > >> >> >> >> return "fresh meat"; > >> >> >> >> } > >> >> >> >> > >> >> >> >> > >> >> >> >> const cases = { > >> >> >> >> [Animal.DOG]: houseAnimal, > >> >> >> >> [Animal.CAT]: houseAnimal, > >> >> >> >> [Animal.LION]: wildCatFood, > >> >> >> >> [Animal.TIGER]: wildCatFood, > >> >> >> >> [Animal.CHEETA]: wildCatFood, > >> >> >> >> } > >> >> >> >> > >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => > {throw new Error("Unsuppored Animal")})(); > >> >> >> >> > >> >> >> >> ``` > >> >> >> >> > >> >> >> >> As we all know once any language reaches a basic level of > functionality anything is possible. What I think is that JavaScript would > benefit by having a cleaner approach. > >> >> >> >> > >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: > >> >> >> >> > >> >> >> >> Isn't the best existing pattern an object literal? > >> >> >> >> > >> >> >> >> const > >> >> >> >> cases = > >> >> >> >> { > >> >> >> >> foo: ()=>1, > >> >> >> >> bar: ()=>3, > >> >> >> >> baz: ()=>6 > >> >> >> >> } > >> >> >> >> , > >> >> >> >> x = > >> >> >> >> cases[v] ? > >> >> >> >> cases[v]() : > >> >> >> >> 99 > >> >> >> >> ; > >> >> >> >> > >> >> >> >> What does any proposal have that is better than this? With > optional chaining feature: > >> >> >> >> > >> >> >> >> const > >> >> >> >> x = > >> >> >> >> { > >> >> >> >> foo: ()=>1, > >> >> >> >> bar: ()=>3, > >> >> >> >> baz: ()=>6 > >> >> >> >> }[v]?.() > >> >> >> >> || > >> >> >> >> 99 > >> >> >> >> ; > >> >> >> >> > >> >> >> >> Do let me know your thoughts guys > >> >> >> >> > >> >> >> >> > >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> > wrote: > >> >> >> >>> > >> >> >> >>> This is unmaintainable -- > >> >> >> >>> > >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' > ? 6 : 99; > >> >> >> >>> > >> >> >> >>> i feel proposed switch-expressions are no more > readable/maintainable than ternary-operators, if you follow jslint's > style-guide. i'll like to see more convincing evidence/use-case that they > are better: > >> >> >> >>> > >> >> >> >>> ```javascript > >> >> >> >>> /*jslint*/ > >> >> >> >>> "use strict"; > >> >> >> >>> const v = "foo"; > >> >> >> >>> const x = ( > >> >> >> >>> v === "foo" > >> >> >> >>> ? 1 > >> >> >> >>> : v === "bar" > >> >> >> >>> ? 3 > >> >> >> >>> : v === "baz" > >> >> >> >>> ? 6 > >> >> >> >>> : 99 > >> >> >> >>> ); > >> >> >> >>> ``` > >> >> >> >>> > >> >> >> >>> here's another example from real-world production-code, where > switch-expressions probably wouldn't help: > >> >> >> >>> > >> >> >> >>> ```javascript > >> >> >> >>> $ node -e ' > >> >> >> >>> /*jslint devel*/ > >> >> >> >>> "use strict"; > >> >> >> >>> function renderRecent(date) { > >> >> >> >>> /* > >> >> >> >>> * this function will render <date> to "xxx ago" > >> >> >> >>> */ > >> >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) > * 0.0001) * 10; > >> >> >> >>> return ( > >> >> >> >>> !Number.isFinite(date) > >> >> >> >>> ? "" > >> >> >> >>> : date < 60 > >> >> >> >>> ? date + " sec ago" > >> >> >> >>> : date < 3600 > >> >> >> >>> ? Math.round(date / 60) + " min ago" > >> >> >> >>> : date < 86400 > >> >> >> >>> ? Math.round(date / 3600) + " hr ago" > >> >> >> >>> : date < 129600 > >> >> >> >>> ? "1 day ago" > >> >> >> >>> : Math.round(date / 86400) + " days ago" > >> >> >> >>> ); > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 > sec ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec > ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min > ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min > ago" > >> >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr > ago" > >> >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 > days ago" > >> >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 > days ago" > >> >> >> >>> ' > >> >> >> >>> > >> >> >> >>> 0 sec ago > >> >> >> >>> 10 sec ago > >> >> >> >>> 5 min ago > >> >> >> >>> 18 min ago > >> >> >> >>> 2 hr ago > >> >> >> >>> 16 days ago > >> >> >> >>> 365 days ago > >> >> >> >>> > >> >> >> >>> $ > >> >> >> >>> ``` > >> >> >> >>> > >> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> > wrote: > >> >> >> >>> > >> >> >> >>> Just for folks who might be interested, added a babel-plugin > to see what was involved in making this possible. > >> >> >> >>> > >> >> >> >>> Pull request available here -- > https://github.com/babel/babel/pull/9604 > >> >> >> >>> > >> >> >> >>> I'm sure I'm missing a bunch of details, but would be > interested in some help in making this a bit more real. > >> >> >> >>> > >> >> >> >>> Thanks > >> >> >> >>> > >> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: > >> >> >> >>> > >> >> >> >>> You're not alone in wanting pattern matching to be > expression-based: > >> >> >> >>> > >> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 > >> >> >> >>> > >> >> >> >>> ----- > >> >> >> >>> > >> >> >> >>> Isiah Meadows > >> >> >> >>> contact at isiahmeadows.com > >> >> >> >>> www.isiahmeadows.com > >> >> >> >>> > >> >> >> >>> ----- > >> >> >> >>> > >> >> >> >>> Isiah Meadows > >> >> >> >>> contact at isiahmeadows.com > >> >> >> >>> www.isiahmeadows.com > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> Jordan, > >> >> >> >>> > >> >> >> >>> Thanks for taking time to read and provide thoughts. > >> >> >> >>> > >> >> >> >>> I just back and re-read the pattern matching proposal and it > still fails on the basic requirement of being an Expression not a > Statement. The problem that I see and want to address is the need to have > something that removes the need to chain trinary expressions together to > have an Expression. > >> >> >> >>> > >> >> >> >>> This is unmaintainable -- > >> >> >> >>> > >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' > ? 6 : 99; > >> >> >> >>> > >> >> >> >>> This is maintainable, but is less than ideal: > >> >> >> >>> > >> >> >> >>> let x; > >> >> >> >>> > >> >> >> >>> switch (v) { > >> >> >> >>> case "foo": > >> >> >> >>> x = 1; > >> >> >> >>> break; > >> >> >> >>> case "bar": > >> >> >> >>> x = 3; > >> >> >> >>> break; > >> >> >> >>> case "baz": > >> >> >> >>> x = 6; > >> >> >> >>> break; > >> >> >> >>> default: > >> >> >> >>> x = 99; > >> >> >> >>> break; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Pattern matching does shorten the code, but you have a weird > default case and also still end up with a loose variable and since pattern > matching is a statement you still have a initially undefined variable. > >> >> >> >>> > >> >> >> >>> let x; > >> >> >> >>> > >> >> >> >>> case (v) { > >> >> >> >>> when "foo" -> x = 1; > >> >> >> >>> when "bar" -> x = 3; > >> >> >> >>> when "baz" -> x = 6; > >> >> >> >>> when v -> x = 99; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Let's try do expressions, I'll leave people's thoughts to > themselves. > >> >> >> >>> > >> >> >> >>> const x = do { > >> >> >> >>> if (v === "foo") { 1; } > >> >> >> >>> else if (v === "bar") { 3; } > >> >> >> >>> else if (v === "baz") { 6; } > >> >> >> >>> else { 99; } > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> Or as another do expression variant: > >> >> >> >>> > >> >> >> >>> const x = do { > >> >> >> >>> switch (v) { > >> >> >> >>> case "foo": 1; break; > >> >> >> >>> case "bar": 3; break; > >> >> >> >>> case "baz": 6; break; > >> >> >> >>> default: 99; break; > >> >> >> >>> } > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> And as I'm thinking about switch expressions: > >> >> >> >>> > >> >> >> >>> const x = switch (v) { > >> >> >> >>> case "foo" => 1; > >> >> >> >>> case "bar" => 3; > >> >> >> >>> case "baz" => 6; > >> >> >> >>> default => 99; > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> What I really like is that it preserves all of the normal > JavaScript syntax with the small change that a switch is allowed in an > expression provided that all of the cases evaluate to expressions hence the > use of the '=>' as an indicator. Fundamentally this is a very basic > concept where you have a state machine and need it switch based on the > current state and evaluate to the new state. > >> >> >> >>> > >> >> >> >>> const nextState = switch (currentState) { > >> >> >> >>> case ... => > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: > >> >> >> >>> > >> >> >> >>> Pattern Matching is still at stage 1; so there's not really > any permanent decisions that have been made - the repo theoretically should > contain rationales for decisions up to this point. > >> >> >> >>> > >> >> >> >>> I can speak for myself (as "not a champion" of that proposal, > just a fan) that any similarity to the reviled and terrible `switch` is > something I'll be pushing back against - I want a replacement that lacks > the footguns and pitfalls of `switch`, and that is easily teachable and > googleable as a different, distinct thing. > >> >> >> >>> > >> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> Jordan, > >> >> >> >>> > >> >> >> >>> One question that I have lingering from pattern matching is > why is the syntax so different? IMHO it is still a switch statement with a > variation of the match on the case rather than a whole new construct. > >> >> >> >>> > >> >> >> >>> Is there somewhere I can find a bit of discussion about the > history of the syntax decisions? > >> >> >> >>> > >> >> >> >>> --David > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband < > ljharb at gmail.com> wrote: > >> >> >> >>> > >> >> >> >>> Additionally, > https://github.com/tc39/proposal-pattern-matching - switch statements are > something I hope we'll soon be able to relegate to the dustbin of history. > >> >> >> >>> > >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> I quite aware that it’s covered in do expressions. Personally > I find do expressions non-JavaScript in style and it’s also not necessarily > going to make it into the language. > >> >> >> >>> > >> >> >> >>> Hence why I wanted to put out there the idea of switch > expressions. > >> >> >> >>> > >> >> >> >>> --David > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> > wrote: > >> >> >> >>> > >> >> >> >>> Hi, > >> >> >> >>> > >> >> >> >>> This would be covered by do expressions. You could just do: > >> >> >> >>> > >> >> >> >>> ```js > >> >> >> >>> const category = do { > >> >> >> >>> switch (...) { > >> >> >> >>> ... > >> >> >> >>> }; > >> >> >> >>> }; > >> >> >> >>> ``` > >> >> >> >>> > >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas < > david at koblas.com> wrote: > >> >> >> >>> > >> >> >> >>> After looking at a bunch of code in our system noted that > there are many > >> >> >> >>> cases where our code base has a pattern similar to this: > >> >> >> >>> > >> >> >> >>> let category = data.category; > >> >> >> >>> > >> >> >> >>> if (category === undefined) { > >> >> >> >>> // Even if Tax is not enabled, we have defaults for > incomeCode > >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { > >> >> >> >>> case TaxIncomeCode.RENTS_14: > >> >> >> >>> category = PaymentCategory.RENT; > >> >> >> >>> break; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >> >>> category = PaymentCategory.SERVICES; > >> >> >> >>> break; > >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: > >> >> >> >>> category = PaymentCategory.SERVICES; > >> >> >> >>> break; > >> >> >> >>> } > >> >> >> >>> } > >> >> >> >>> > >> >> >> >>> I also bumped into a block of go code that also implemented > similar > >> >> >> >>> patterns, which really demonstrated to me that there while > you could go > >> >> >> >>> crazy with triary nesting there should be a better way. > Looked at the > >> >> >> >>> pattern matching proposal and while could possibly help > looked like it > >> >> >> >>> was overkill for the typical use case that I'm seeing. The > most relevant > >> >> >> >>> example I noted was switch expressions from Java. When > applied to this > >> >> >> >>> problem really create a simple -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190302/a70d86ee/attachment-0001.html>
By "simulating", I meant doing something that was functionally
equivalent. If you wanted a direct equivalent, just replace all
instances of {keyN: true, print: function () { ... }}
with new class { constructor() { this.keyN = true } print() { ... } }
in the
benchmarks, and you've got something that's exactly equivalent. And
no, that won't be any faster, because in either case, with object
literals or with immediately-invoked classes, the engine is working
with a different set of type maps for each type, so the only variable
here is whether print
is own or prototype and whether the prototype
is Object.prototype
or a unique object. If anything, the class
variant would be slower because of the indirection.
Here's a couple fixed benchmarks with clearer test names:
- 4 types: jsben.ch/4VeWy
- 12/16 types: jsben.ch/wplTp
In both cases, method dispatch is about 10-20% slower than the
corresponding switch
/case
, and is only marginally (< 5%) faster
than dynamic string property dispatch. I've gotten these results
running both benchmark suites about 10 times each and even in the one
statistical outlier I had where everything ran slower, method dispatch
still was listed as slower than switch/case across the board and
roughly equal to dynamic string property dispatch.
One last thing: could you please quit arguing semantics?
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
By "simulating", I meant doing something that was functionally equivalent. If you wanted a direct equivalent, just replace all instances of `{keyN: true, print: function () { ... }}` with `new class { constructor() { this.keyN = true } print() { ... } }` in the benchmarks, and you've got something that's *exactly* equivalent. And no, that won't be any faster, because in either case, with object literals or with immediately-invoked classes, the engine is working with a different set of type maps for each type, so the only variable here is whether `print` is own or prototype and whether the prototype is `Object.prototype` or a unique object. If anything, the class variant would be slower because of the indirection. Here's a couple fixed benchmarks with clearer test names: - 4 types: http://jsben.ch/4VeWy - 12/16 types: http://jsben.ch/wplTp In both cases, method dispatch is about 10-20% slower than the corresponding `switch`/`case`, and is only marginally (< 5%) faster than dynamic string property dispatch. I've gotten these results running both benchmark suites about 10 times each and even in the one statistical outlier I had where everything ran slower, method dispatch still was listed as slower than switch/case across the board and roughly equal to dynamic string property dispatch. One last thing: could you *please* quit arguing semantics? ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Sat, Mar 2, 2019 at 4:57 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: > > It "simulates" then does it for real, hence slower than just doing it (there is no need to simulate if we have the paradigm we are are trying to benchmark ready for us to just use). I'd be curious to see a direct benchmark comparison, but don't have time to set it up right now. > > On Sat, 2 Mar 2019, 2:08 pm Isiah Meadows, <isiahmeadows at gmail.com> wrote: >> >> IIUC the "object dispatch integer"/"object dispatch string" benchmarks >> are the things you were referring to. Those simulate what the engine >> would see with virtual dispatch and completely different type maps, >> just without the source overhead of creating an entire class just for >> a little benchmark. >> >> And also, engines *won't* be able to optimize them generally, because >> there could be infinitely many type maps, and after about 200 or so >> types, the `switch` statement ends up *much* slower. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> On Sat, Mar 2, 2019 at 3:43 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> > >> > I don't think those benchmarks test exactly what we are talking about. They have a dictionary/array look up followed by method dispatch, vs switch case and execute. Removing the look up it would be: `x.doStuff()` vs `switch(x.type)...`. Make sense? Don't have time to do it right now. >> > >> > Logically I think the JS engine can make them perform identically, so even if benchmarks show something today, I would not be concerned for the future and so would prefer to opt for the paradigm that offers the best manageability, which I think is inheritance by a significant margin, in the cases mentioned. Other types of cases could of course be a whole different story. >> > >> > On Sat, 2 Mar 2019, 5:24 am Isiah Meadows, <isiahmeadows at gmail.com> wrote: >> >> >> >> > It would be unthinkable for it to use pattern matching or explicit code branchinI'm g instead of method inheritance for type disambiguation during render >> >> >> >> But it frequently does internally. For example: >> >> >> >> - Calculating object projections: >> >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1243-L1351 >> >> - Rendering object *lists*: >> >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1353-L1411 >> >> - Setting the rendering mode and controlling basic rendering: >> >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L802-L874 >> >> >> >> Obviously, it exposes a data-oriented, object oriented API. And it >> >> does appear it's not *exclusively* conditionals: >> >> >> >> - It invokes an dynamic `render` method for "immediate render >> >> objects": https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L636-L644 >> >> - In `renderBufferDirect`, it does virtual method dispatch on `render` >> >> based on one of two possible types, but it otherwise uses conditionals >> >> for everything:\* >> >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L707-L876 >> >> - It uses a fair bit of functional programming in `compile`: >> >> https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js#L1009-L1054 >> >> >> >> However, I'm finding exceptions in its core and renderers, and it >> >> doesn't appear virtual dispatch is *that* broadly and pervasively >> >> used, even though it uses methods a lot. >> >> >> >> \* This seems like overkill when the diff between the two renderers in >> >> question [1] [2] consist of an extra method + 2 extra variables [3] >> >> [4], a few changed method invocations [5] [6], and the rest just due >> >> to a different name and a useless `var`. >> >> >> >> [1]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLBufferRenderer.js >> >> [2]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js >> >> [3]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L15-L22 >> >> [4]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L61 >> >> [5]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L26 >> >> [6]: https://github.com/mrdoob/three.js/blob/dev/src/renderers/webgl/WebGLIndexedBufferRenderer.js#L53 >> >> >> >> > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? >> >> >> >> - In C++: https://stackoverflow.com/a/8866789 >> >> - JS benchmark with 4 variants (typed method dispatch is polymorphic): >> >> http://jsben.ch/fbJQH >> >> - JS benchmark with 12 variants (typed method dispatch is >> >> megamorphic): http://jsben.ch/aWNDN >> >> >> >> And in my experience, the speed difference in real-world >> >> performance-critical code is not unlike that microbenchmark and is >> >> sometimes even more drastic, especially if it's a linked list instead >> >> of just a simple array lookup. >> >> >> >> I'd like to emphasize I'm specifically referring to the case where the >> >> engine can't reliably assume a single method receiver, i.e. when it >> >> *has* to fall back to dynamic dispatch. >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> > >> >> > The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway). >> >> > >> >> > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? >> >> > >> >> > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> >> >> I'm looking at Three.js's code base, and I'm not seeing any method >> >> >> overriding or abstract methods used except at the API level for >> >> >> cloning and copying. Instead, you update properties on the supertype. >> >> >> As far as I can tell, the entirety of Three.js could almost be >> >> >> mechanically refactored in terms of components instead of inheritance, >> >> >> without substantially modifying the API apart from a few extra >> >> >> `.geometry`/etc. property accesses when calling supertype methods. >> >> >> It's data-driven and almost ECS. (It uses `.isObject3D`, >> >> >> `.isPerspectiveCamera`, and similar brand checks, but those don't >> >> >> *need* to be inherited to work.) >> >> >> >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> contact at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> >> > >> >> >> > I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. >> >> >> > >> >> >> > If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. >> >> >> > >> >> >> > As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. >> >> >> > >> >> >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> >> >> >> >> Sometimes, this is actually *desired*, and most cases where I could've >> >> >> >> used this, inheritance was not involved *anywhere*. Also, in >> >> >> >> performance-sensitive contexts (like games, which *heavily* use >> >> >> >> `switch`/`case`), method dispatch is *far* slower than a simple >> >> >> >> `switch` statement, so that pattern doesn't apply everywhere. >> >> >> >> >> >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over >> >> >> >> this anyways - it covers more use cases and is all around more >> >> >> >> flexible, so I get more bang for the buck. >> >> >> >> >> >> >> >> ----- >> >> >> >> >> >> >> >> Isiah Meadows >> >> >> >> contact at isiahmeadows.com >> >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> >> >> > >> >> >> >> > Hi David! >> >> >> >> > >> >> >> >> > Your last example would, I think, be better served by classes and inheritance, than switch. >> >> >> >> > >> >> >> >> > Dogs are house animals which are animals >> >> >> >> > Cheetas are wild cats which are animals >> >> >> >> > >> >> >> >> > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> >> >> >> > >> >> >> >> > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> >> >> >> > >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> > >> >> >> >> > All thoughts on this are welcome. Do let me know >> >> >> >> > >> >> >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> >> >> >> >> >> >> >> >> Naveen, >> >> >> >> >> >> >> >> >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> switch (animal) { >> >> >> >> >> case Animal.DOG, Animal.CAT => { >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> >> } >> >> >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> >> } >> >> >> >> >> case Animal.ELEPHANT => "hay"; >> >> >> >> >> default => { throw new Error("Unsupported Animal"); }; >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> function houseAnimal() { >> >> >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> function wildCatFood() { >> >> >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> const cases = { >> >> >> >> >> [Animal.DOG]: houseAnimal, >> >> >> >> >> [Animal.CAT]: houseAnimal, >> >> >> >> >> [Animal.LION]: wildCatFood, >> >> >> >> >> [Animal.TIGER]: wildCatFood, >> >> >> >> >> [Animal.CHEETA]: wildCatFood, >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> >> >> >> >> >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> >> >> >> >> >> >> >> >> Isn't the best existing pattern an object literal? >> >> >> >> >> >> >> >> >> >> const >> >> >> >> >> cases = >> >> >> >> >> { >> >> >> >> >> foo: ()=>1, >> >> >> >> >> bar: ()=>3, >> >> >> >> >> baz: ()=>6 >> >> >> >> >> } >> >> >> >> >> , >> >> >> >> >> x = >> >> >> >> >> cases[v] ? >> >> >> >> >> cases[v]() : >> >> >> >> >> 99 >> >> >> >> >> ; >> >> >> >> >> >> >> >> >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> >> >> >> >> >> >> >> >> const >> >> >> >> >> x = >> >> >> >> >> { >> >> >> >> >> foo: ()=>1, >> >> >> >> >> bar: ()=>3, >> >> >> >> >> baz: ()=>6 >> >> >> >> >> }[v]?.() >> >> >> >> >> || >> >> >> >> >> 99 >> >> >> >> >> ; >> >> >> >> >> >> >> >> >> >> Do let me know your thoughts guys >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> This is unmaintainable -- >> >> >> >> >>> >> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >> >>> >> >> >> >> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >> >> >> >> >>> >> >> >> >> >>> ```javascript >> >> >> >> >>> /*jslint*/ >> >> >> >> >>> "use strict"; >> >> >> >> >>> const v = "foo"; >> >> >> >> >>> const x = ( >> >> >> >> >>> v === "foo" >> >> >> >> >>> ? 1 >> >> >> >> >>> : v === "bar" >> >> >> >> >>> ? 3 >> >> >> >> >>> : v === "baz" >> >> >> >> >>> ? 6 >> >> >> >> >>> : 99 >> >> >> >> >>> ); >> >> >> >> >>> ``` >> >> >> >> >>> >> >> >> >> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >> >> >> >> >>> >> >> >> >> >>> ```javascript >> >> >> >> >>> $ node -e ' >> >> >> >> >>> /*jslint devel*/ >> >> >> >> >>> "use strict"; >> >> >> >> >>> function renderRecent(date) { >> >> >> >> >>> /* >> >> >> >> >>> * this function will render <date> to "xxx ago" >> >> >> >> >>> */ >> >> >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >> >> >> >> >>> return ( >> >> >> >> >>> !Number.isFinite(date) >> >> >> >> >>> ? "" >> >> >> >> >>> : date < 60 >> >> >> >> >>> ? date + " sec ago" >> >> >> >> >>> : date < 3600 >> >> >> >> >>> ? Math.round(date / 60) + " min ago" >> >> >> >> >>> : date < 86400 >> >> >> >> >>> ? Math.round(date / 3600) + " hr ago" >> >> >> >> >>> : date < 129600 >> >> >> >> >>> ? "1 day ago" >> >> >> >> >>> : Math.round(date / 86400) + " days ago" >> >> >> >> >>> ); >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> >> >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> >> >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> >> >> >> >>> ' >> >> >> >> >>> >> >> >> >> >>> 0 sec ago >> >> >> >> >>> 10 sec ago >> >> >> >> >>> 5 min ago >> >> >> >> >>> 18 min ago >> >> >> >> >>> 2 hr ago >> >> >> >> >>> 16 days ago >> >> >> >> >>> 365 days ago >> >> >> >> >>> >> >> >> >> >>> $ >> >> >> >> >>> ``` >> >> >> >> >>> >> >> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >> >> >> >> >>> >> >> >> >> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> >> >> >>> >> >> >> >> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >> >> >> >> >>> >> >> >> >> >>> Thanks >> >> >> >> >>> >> >> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >> >> >> >>> >> >> >> >> >>> You're not alone in wanting pattern matching to be expression-based: >> >> >> >> >>> >> >> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> >> >> >>> >> >> >> >> >>> ----- >> >> >> >> >>> >> >> >> >> >>> Isiah Meadows >> >> >> >> >>> contact at isiahmeadows.com >> >> >> >> >>> www.isiahmeadows.com >> >> >> >> >>> >> >> >> >> >>> ----- >> >> >> >> >>> >> >> >> >> >>> Isiah Meadows >> >> >> >> >>> contact at isiahmeadows.com >> >> >> >> >>> www.isiahmeadows.com >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Jordan, >> >> >> >> >>> >> >> >> >> >>> Thanks for taking time to read and provide thoughts. >> >> >> >> >>> >> >> >> >> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >> >> >> >>> >> >> >> >> >>> This is unmaintainable -- >> >> >> >> >>> >> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >> >>> >> >> >> >> >>> This is maintainable, but is less than ideal: >> >> >> >> >>> >> >> >> >> >>> let x; >> >> >> >> >>> >> >> >> >> >>> switch (v) { >> >> >> >> >>> case "foo": >> >> >> >> >>> x = 1; >> >> >> >> >>> break; >> >> >> >> >>> case "bar": >> >> >> >> >>> x = 3; >> >> >> >> >>> break; >> >> >> >> >>> case "baz": >> >> >> >> >>> x = 6; >> >> >> >> >>> break; >> >> >> >> >>> default: >> >> >> >> >>> x = 99; >> >> >> >> >>> break; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >> >> >> >>> >> >> >> >> >>> let x; >> >> >> >> >>> >> >> >> >> >>> case (v) { >> >> >> >> >>> when "foo" -> x = 1; >> >> >> >> >>> when "bar" -> x = 3; >> >> >> >> >>> when "baz" -> x = 6; >> >> >> >> >>> when v -> x = 99; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> >> >> >>> >> >> >> >> >>> const x = do { >> >> >> >> >>> if (v === "foo") { 1; } >> >> >> >> >>> else if (v === "bar") { 3; } >> >> >> >> >>> else if (v === "baz") { 6; } >> >> >> >> >>> else { 99; } >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Or as another do expression variant: >> >> >> >> >>> >> >> >> >> >>> const x = do { >> >> >> >> >>> switch (v) { >> >> >> >> >>> case "foo": 1; break; >> >> >> >> >>> case "bar": 3; break; >> >> >> >> >>> case "baz": 6; break; >> >> >> >> >>> default: 99; break; >> >> >> >> >>> } >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> And as I'm thinking about switch expressions: >> >> >> >> >>> >> >> >> >> >>> const x = switch (v) { >> >> >> >> >>> case "foo" => 1; >> >> >> >> >>> case "bar" => 3; >> >> >> >> >>> case "baz" => 6; >> >> >> >> >>> default => 99; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >> >> >> >>> >> >> >> >> >>> const nextState = switch (currentState) { >> >> >> >> >>> case ... => >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> >> >> >>> >> >> >> >> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >> >> >> >>> >> >> >> >> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >> >> >> >>> >> >> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Jordan, >> >> >> >> >>> >> >> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >> >> >>> >> >> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >> >> >>> >> >> >> >> >>> --David >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >> >> >>> >> >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >> >> >>> >> >> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >> >> >>> >> >> >> >> >>> --David >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Hi, >> >> >> >> >>> >> >> >> >> >>> This would be covered by do expressions. You could just do: >> >> >> >> >>> >> >> >> >> >>> ```js >> >> >> >> >>> const category = do { >> >> >> >> >>> switch (...) { >> >> >> >> >>> ... >> >> >> >> >>> }; >> >> >> >> >>> }; >> >> >> >> >>> ``` >> >> >> >> >>> >> >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >> >> >>> cases where our code base has a pattern similar to this: >> >> >> >> >>> >> >> >> >> >>> let category = data.category; >> >> >> >> >>> >> >> >> >> >>> if (category === undefined) { >> >> >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >> >> >>> category = PaymentCategory.RENT; >> >> >> >> >>> break; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >> >>> break; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >> >>> break; >> >> >> >> >>> } >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >> >> >>> problem really create a simple result: >> >> >> >> >>> >> >> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >> >> >>> PaymentCategory.ROYALTIES; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >> >> >>> PaymentCategory.SERVICES; >> >> >> >> >>> default => PaymentCategory.OTHER; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >> >> >>> >> >> >> >> >>> const quarter = switch (foo) { >> >> >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >> >> >>> the existing language. In one of their examples they provide an example >> >> >> >> >>> of the Redux reducer >> >> >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >> >> >>> a switch expression implementation. >> >> >> >> >>> >> >> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >> >> >>> (action.type) { >> >> >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >> >> >>> action.filter }; >> >> >> >> >>> case ADD_TODO => { >> >> >> >> >>> ...state, todos: [ >> >> >> >> >>> ...state.todos, >> >> >> >> >>> { >> >> >> >> >>> text: action.text, >> >> >> >> >>> completed: false >> >> >> >> >>> } >> >> >> >> >>> ] >> >> >> >> >>> }; >> >> >> >> >>> case TOGGLE_TODO => { >> >> >> >> >>> ...state, >> >> >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >> >> >>> }; >> >> >> >> >>> default => state; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> _______________________________________________ >> >> >> >> >>> 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 >> >> >> >> >>> >> >> >> >> >>> On 2/25/19 3:42 PM, David Koblas wrote: >> >> >> >> >>> >> >> >> >> >>> Jordan, >> >> >> >> >>> >> >> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >> >> >>> >> >> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >> >> >>> >> >> >> >> >>> --David >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >> >> >>> >> >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >> >> >>> >> >> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >> >> >>> >> >> >> >> >>> --David >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Hi, >> >> >> >> >>> >> >> >> >> >>> This would be covered by do expressions. You could just do: >> >> >> >> >>> >> >> >> >> >>> ```js >> >> >> >> >>> const category = do { >> >> >> >> >>> switch (...) { >> >> >> >> >>> ... >> >> >> >> >>> }; >> >> >> >> >>> }; >> >> >> >> >>> ``` >> >> >> >> >>> >> >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >> >> >>> cases where our code base has a pattern similar to this: >> >> >> >> >>> >> >> >> >> >>> let category = data.category; >> >> >> >> >>> >> >> >> >> >>> if (category === undefined) { >> >> >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >> >> >>> category = PaymentCategory.RENT; >> >> >> >> >>> break; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >> >>> break; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >> >>> break; >> >> >> >> >>> } >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >> >> >>> problem really create a simple result: >> >> >> >> >>> >> >> >> >> >>> const category = data.category || switch (setting.incomeCode) { >> >> >> >> >>> case TaxIncomeCode.RENTS_14 => PaymentCategory.RENT; >> >> >> >> >>> case TaxIncomeCode.ROYALTIES_COPYRIGHTS_12 => >> >> >> >> >>> PaymentCategory.ROYALTIES; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17 => >> >> >> >> >>> PaymentCategory.SERVICES; >> >> >> >> >>> default => PaymentCategory.OTHER; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Note; the instead of using the '->' as Java, continue to use => and with >> >> >> >> >>> the understanding that the right hand side is fundamentally function. >> >> >> >> >>> So similar things to this are natural, note this proposal should remove >> >> >> >> >>> "fall through" breaks and allow for multiple cases as such. >> >> >> >> >>> >> >> >> >> >>> const quarter = switch (foo) { >> >> >> >> >>> case "Jan", "Feb", "Mar" => "Q1"; >> >> >> >> >>> case "Apr", "May", "Jun" => "Q2"; >> >> >> >> >>> case "Jul", "Aug", "Sep" => "Q3"; >> >> >> >> >>> case "Oct", "Nov", "Dec" => { return "Q4" }; >> >> >> >> >>> default => { throw new Error("Invalid Month") }; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Also compared this to the do expression proposal, it also provides a >> >> >> >> >>> substantial simplification, but in a way that is more consistent with >> >> >> >> >>> the existing language. In one of their examples they provide an example >> >> >> >> >>> of the Redux reducer >> >> >> >> >>> https://redux.js.org/basics/reducers#splitting-reducers -- this would be >> >> >> >> >>> a switch expression implementation. >> >> >> >> >>> >> >> >> >> >>> function todoApp(state = initialState, action) => switch >> >> >> >> >>> (action.type) { >> >> >> >> >>> case SET_VISIBILITY_FILTER => { ...state, visibilityFilter: >> >> >> >> >>> action.filter }; >> >> >> >> >>> case ADD_TODO => { >> >> >> >> >>> ...state, todos: [ >> >> >> >> >>> ...state.todos, >> >> >> >> >>> { >> >> >> >> >>> text: action.text, >> >> >> >> >>> completed: false >> >> >> >> >>> } >> >> >> >> >>> ] >> >> >> >> >>> }; >> >> >> >> >>> case TOGGLE_TODO => { >> >> >> >> >>> ...state, >> >> >> >> >>> todos: state.todos.map((todo, index) => (index === >> >> >> >> >>> action.index) ? { ...todo, completed: !todo.completed } : todo) >> >> >> >> >>> }; >> >> >> >> >>> default => state; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> _______________________________________________ >> >> >> >> >>> 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 >> >> >> >> >>> >> >> >> >> >>> _______________________________________________ >> >> >> >> >>> 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 >> >> >> >> > >> >> >> >> > _______________________________________________ >> >> >> >> > es-discuss mailing list >> >> >> >> > es-discuss at mozilla.org >> >> >> >> > https://mail.mozilla.org/listinfo/es-discuss >> >> >> >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> > >> >> > The entire renderers, cameras, meshes etc. hierarchy uses method inheritance and many of those methods are called during scene rendering (which is performance sensitive as it happens per frame). It would be unthinkable for it to use pattern matching or explicit code branching instead of method inheritance for type disambiguation during render, because it would explode the code as well as making it error prone due to initial cases potentially unintentionally swallowing up cases intended for later code branches (or unintentionally repeating code branches if the pattern-matching proposal doesn't have "else" behaviour, of which I'm not sure, but it if does, it suffers from the first problem anyway). >> >> > >> >> > I'm curious where you got the idea that method invocation is "far" slower than explicit code branching? >> >> > >> >> > On Thu, 28 Feb 2019 at 18:49 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> >> >> I'm looking at Three.js's code base, and I'm not seeing any method >> >> >> overriding or abstract methods used except at the API level for >> >> >> cloning and copying. Instead, you update properties on the supertype. >> >> >> As far as I can tell, the entirety of Three.js could almost be >> >> >> mechanically refactored in terms of components instead of inheritance, >> >> >> without substantially modifying the API apart from a few extra >> >> >> `.geometry`/etc. property accesses when calling supertype methods. >> >> >> It's data-driven and almost ECS. (It uses `.isObject3D`, >> >> >> `.isPerspectiveCamera`, and similar brand checks, but those don't >> >> >> *need* to be inherited to work.) >> >> >> >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> contact at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> >> > >> >> >> > I'm not sure that pattern matching handles deep levels of inheritance more elegantly than inheritance itself. >> >> >> > >> >> >> > If there is a conceptual type hierarchy, then the ability to call "super", combine it with specialized functionality, etc. is a lot more manageable using localized, separated logic where you don't feel forced to read "other patterns" to understand whether your target functionality will resolve correctly. And hence, a lower chance of bugs. >> >> >> > >> >> >> > As for performance, I'd have to see modern benchmarks. But it's not necessarily clear that pattern matching will be particularly fast either. I've done game programming with method overriding (Three.js uses it too throughout) and there is no notable performance hit from doing so. So I'm not clear where you have got this information from. >> >> >> > >> >> >> > On Thu, 28 Feb 2019 at 17:06 Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> >> >> >> >> Sometimes, this is actually *desired*, and most cases where I could've >> >> >> >> used this, inheritance was not involved *anywhere*. Also, in >> >> >> >> performance-sensitive contexts (like games, which *heavily* use >> >> >> >> `switch`/`case`), method dispatch is *far* slower than a simple >> >> >> >> `switch` statement, so that pattern doesn't apply everywhere. >> >> >> >> >> >> >> >> BTW, I prefer https://github.com/tc39/proposal-pattern-matching/ over >> >> >> >> this anyways - it covers more use cases and is all around more >> >> >> >> flexible, so I get more bang for the buck. >> >> >> >> >> >> >> >> ----- >> >> >> >> >> >> >> >> Isiah Meadows >> >> >> >> contact at isiahmeadows.com >> >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <naveen.chwl at gmail.com> wrote: >> >> >> >> > >> >> >> >> > Hi David! >> >> >> >> > >> >> >> >> > Your last example would, I think, be better served by classes and inheritance, than switch. >> >> >> >> > >> >> >> >> > Dogs are house animals which are animals >> >> >> >> > Cheetas are wild cats which are animals >> >> >> >> > >> >> >> >> > Each could have overridden methods, entirely optionally, where the method gets called and resolves appropriately. >> >> >> >> > >> >> >> >> > The input argument could be the class name, from which it is trivial to instantiate a new instance and get required results. >> >> >> >> > >> >> >> >> > Using a "switch" here forces you to group classes of objects together and then you don't get the 2nd, 3rd, 4th etc. levels of specialization that you might later want. >> >> >> >> > >> >> >> >> > All thoughts on this are welcome. Do let me know >> >> >> >> > >> >> >> >> > On Thu, 28 Feb 2019 at 14:06 David Koblas <david at koblas.com> wrote: >> >> >> >> >> >> >> >> >> >> Naveen, >> >> >> >> >> >> >> >> >> >> Thanks for your observation. The example that I gave might have been too simplistic, here's a more complete example: >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> switch (animal) { >> >> >> >> >> case Animal.DOG, Animal.CAT => { >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> >> } >> >> >> >> >> case Animal.TIGER, Animal.LION, Animal.CHEETA => { >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> >> } >> >> >> >> >> case Animal.ELEPHANT => "hay"; >> >> >> >> >> default => { throw new Error("Unsupported Animal"); }; >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> While you give examples that would totally work. Things that bother me about the approach are, when taken to something more complex than a quick value for value switch you end up with something that looks like this. >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> function houseAnimal() { >> >> >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "dry food"; >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> function wildCatFood() { >> >> >> >> >> >> >> >> >> >> // larger block expression >> >> >> >> >> // which spans multiple lines >> >> >> >> >> >> >> >> >> >> return "fresh meat"; >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> const cases = { >> >> >> >> >> [Animal.DOG]: houseAnimal, >> >> >> >> >> [Animal.CAT]: houseAnimal, >> >> >> >> >> [Animal.LION]: wildCatFood, >> >> >> >> >> [Animal.TIGER]: wildCatFood, >> >> >> >> >> [Animal.CHEETA]: wildCatFood, >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> const food = cases[animal] ? cases[animal]() : (() => {throw new Error("Unsuppored Animal")})(); >> >> >> >> >> >> >> >> >> >> ``` >> >> >> >> >> >> >> >> >> >> As we all know once any language reaches a basic level of functionality anything is possible. What I think is that JavaScript would benefit by having a cleaner approach. >> >> >> >> >> >> >> >> >> >> On 2/28/19 4:37 AM, Naveen Chawla wrote: >> >> >> >> >> >> >> >> >> >> Isn't the best existing pattern an object literal? >> >> >> >> >> >> >> >> >> >> const >> >> >> >> >> cases = >> >> >> >> >> { >> >> >> >> >> foo: ()=>1, >> >> >> >> >> bar: ()=>3, >> >> >> >> >> baz: ()=>6 >> >> >> >> >> } >> >> >> >> >> , >> >> >> >> >> x = >> >> >> >> >> cases[v] ? >> >> >> >> >> cases[v]() : >> >> >> >> >> 99 >> >> >> >> >> ; >> >> >> >> >> >> >> >> >> >> What does any proposal have that is better than this? With optional chaining feature: >> >> >> >> >> >> >> >> >> >> const >> >> >> >> >> x = >> >> >> >> >> { >> >> >> >> >> foo: ()=>1, >> >> >> >> >> bar: ()=>3, >> >> >> >> >> baz: ()=>6 >> >> >> >> >> }[v]?.() >> >> >> >> >> || >> >> >> >> >> 99 >> >> >> >> >> ; >> >> >> >> >> >> >> >> >> >> Do let me know your thoughts guys >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> On Thu, 28 Feb 2019 at 06:04 kai zhu <kaizhu256 at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> This is unmaintainable -- >> >> >> >> >>> >> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >> >>> >> >> >> >> >>> i feel proposed switch-expressions are no more readable/maintainable than ternary-operators, if you follow jslint's style-guide. i'll like to see more convincing evidence/use-case that they are better: >> >> >> >> >>> >> >> >> >> >>> ```javascript >> >> >> >> >>> /*jslint*/ >> >> >> >> >>> "use strict"; >> >> >> >> >>> const v = "foo"; >> >> >> >> >>> const x = ( >> >> >> >> >>> v === "foo" >> >> >> >> >>> ? 1 >> >> >> >> >>> : v === "bar" >> >> >> >> >>> ? 3 >> >> >> >> >>> : v === "baz" >> >> >> >> >>> ? 6 >> >> >> >> >>> : 99 >> >> >> >> >>> ); >> >> >> >> >>> ``` >> >> >> >> >>> >> >> >> >> >>> here's another example from real-world production-code, where switch-expressions probably wouldn't help: >> >> >> >> >>> >> >> >> >> >>> ```javascript >> >> >> >> >>> $ node -e ' >> >> >> >> >>> /*jslint devel*/ >> >> >> >> >>> "use strict"; >> >> >> >> >>> function renderRecent(date) { >> >> >> >> >>> /* >> >> >> >> >>> * this function will render <date> to "xxx ago" >> >> >> >> >>> */ >> >> >> >> >>> date = Math.ceil((Date.now() - new Date(date).getTime()) * 0.0001) * 10; >> >> >> >> >>> return ( >> >> >> >> >>> !Number.isFinite(date) >> >> >> >> >>> ? "" >> >> >> >> >>> : date < 60 >> >> >> >> >>> ? date + " sec ago" >> >> >> >> >>> : date < 3600 >> >> >> >> >>> ? Math.round(date / 60) + " min ago" >> >> >> >> >>> : date < 86400 >> >> >> >> >>> ? Math.round(date / 3600) + " hr ago" >> >> >> >> >>> : date < 129600 >> >> >> >> >>> ? "1 day ago" >> >> >> >> >>> : Math.round(date / 86400) + " days ago" >> >> >> >> >>> ); >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> console.log(renderRecent(new Date().toISOString())); // "0 sec ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T05:32:00Z")); // "10 sec ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T05:27:30Z")); // "5 min ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T05:14:00Z")); // "18 min ago" >> >> >> >> >>> console.log(renderRecent("2019-02-28T03:27:00Z")); // "2 hr ago" >> >> >> >> >>> console.log(renderRecent("2019-02-12T05:27:00Z")); // "16 days ago" >> >> >> >> >>> console.log(renderRecent("2018-02-28T05:27:00Z")); // "365 days ago" >> >> >> >> >>> ' >> >> >> >> >>> >> >> >> >> >>> 0 sec ago >> >> >> >> >>> 10 sec ago >> >> >> >> >>> 5 min ago >> >> >> >> >>> 18 min ago >> >> >> >> >>> 2 hr ago >> >> >> >> >>> 16 days ago >> >> >> >> >>> 365 days ago >> >> >> >> >>> >> >> >> >> >>> $ >> >> >> >> >>> ``` >> >> >> >> >>> >> >> >> >> >>> On 27 Feb 2019, at 13:12, David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Just for folks who might be interested, added a babel-plugin to see what was involved in making this possible. >> >> >> >> >>> >> >> >> >> >>> Pull request available here -- https://github.com/babel/babel/pull/9604 >> >> >> >> >>> >> >> >> >> >>> I'm sure I'm missing a bunch of details, but would be interested in some help in making this a bit more real. >> >> >> >> >>> >> >> >> >> >>> Thanks >> >> >> >> >>> >> >> >> >> >>> On 2/26/19 2:40 PM, Isiah Meadows wrote: >> >> >> >> >>> >> >> >> >> >>> You're not alone in wanting pattern matching to be expression-based: >> >> >> >> >>> >> >> >> >> >>> https://github.com/tc39/proposal-pattern-matching/issues/116 >> >> >> >> >>> >> >> >> >> >>> ----- >> >> >> >> >>> >> >> >> >> >>> Isiah Meadows >> >> >> >> >>> contact at isiahmeadows.com >> >> >> >> >>> www.isiahmeadows.com >> >> >> >> >>> >> >> >> >> >>> ----- >> >> >> >> >>> >> >> >> >> >>> Isiah Meadows >> >> >> >> >>> contact at isiahmeadows.com >> >> >> >> >>> www.isiahmeadows.com >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Jordan, >> >> >> >> >>> >> >> >> >> >>> Thanks for taking time to read and provide thoughts. >> >> >> >> >>> >> >> >> >> >>> I just back and re-read the pattern matching proposal and it still fails on the basic requirement of being an Expression not a Statement. The problem that I see and want to address is the need to have something that removes the need to chain trinary expressions together to have an Expression. >> >> >> >> >>> >> >> >> >> >>> This is unmaintainable -- >> >> >> >> >>> >> >> >> >> >>> const x = v === 'foo' ? 1 : v === 'bar' ? 3 : v === 'baz' ? 6 : 99; >> >> >> >> >>> >> >> >> >> >>> This is maintainable, but is less than ideal: >> >> >> >> >>> >> >> >> >> >>> let x; >> >> >> >> >>> >> >> >> >> >>> switch (v) { >> >> >> >> >>> case "foo": >> >> >> >> >>> x = 1; >> >> >> >> >>> break; >> >> >> >> >>> case "bar": >> >> >> >> >>> x = 3; >> >> >> >> >>> break; >> >> >> >> >>> case "baz": >> >> >> >> >>> x = 6; >> >> >> >> >>> break; >> >> >> >> >>> default: >> >> >> >> >>> x = 99; >> >> >> >> >>> break; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Pattern matching does shorten the code, but you have a weird default case and also still end up with a loose variable and since pattern matching is a statement you still have a initially undefined variable. >> >> >> >> >>> >> >> >> >> >>> let x; >> >> >> >> >>> >> >> >> >> >>> case (v) { >> >> >> >> >>> when "foo" -> x = 1; >> >> >> >> >>> when "bar" -> x = 3; >> >> >> >> >>> when "baz" -> x = 6; >> >> >> >> >>> when v -> x = 99; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Let's try do expressions, I'll leave people's thoughts to themselves. >> >> >> >> >>> >> >> >> >> >>> const x = do { >> >> >> >> >>> if (v === "foo") { 1; } >> >> >> >> >>> else if (v === "bar") { 3; } >> >> >> >> >>> else if (v === "baz") { 6; } >> >> >> >> >>> else { 99; } >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> Or as another do expression variant: >> >> >> >> >>> >> >> >> >> >>> const x = do { >> >> >> >> >>> switch (v) { >> >> >> >> >>> case "foo": 1; break; >> >> >> >> >>> case "bar": 3; break; >> >> >> >> >>> case "baz": 6; break; >> >> >> >> >>> default: 99; break; >> >> >> >> >>> } >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> And as I'm thinking about switch expressions: >> >> >> >> >>> >> >> >> >> >>> const x = switch (v) { >> >> >> >> >>> case "foo" => 1; >> >> >> >> >>> case "bar" => 3; >> >> >> >> >>> case "baz" => 6; >> >> >> >> >>> default => 99; >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> What I really like is that it preserves all of the normal JavaScript syntax with the small change that a switch is allowed in an expression provided that all of the cases evaluate to expressions hence the use of the '=>' as an indicator. Fundamentally this is a very basic concept where you have a state machine and need it switch based on the current state and evaluate to the new state. >> >> >> >> >>> >> >> >> >> >>> const nextState = switch (currentState) { >> >> >> >> >>> case ... => >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On 2/25/19 4:00 PM, Jordan Harband wrote: >> >> >> >> >>> >> >> >> >> >>> Pattern Matching is still at stage 1; so there's not really any permanent decisions that have been made - the repo theoretically should contain rationales for decisions up to this point. >> >> >> >> >>> >> >> >> >> >>> I can speak for myself (as "not a champion" of that proposal, just a fan) that any similarity to the reviled and terrible `switch` is something I'll be pushing back against - I want a replacement that lacks the footguns and pitfalls of `switch`, and that is easily teachable and googleable as a different, distinct thing. >> >> >> >> >>> >> >> >> >> >>> On Mon, Feb 25, 2019 at 12:42 PM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Jordan, >> >> >> >> >>> >> >> >> >> >>> One question that I have lingering from pattern matching is why is the syntax so different? IMHO it is still a switch statement with a variation of the match on the case rather than a whole new construct. >> >> >> >> >>> >> >> >> >> >>> Is there somewhere I can find a bit of discussion about the history of the syntax decisions? >> >> >> >> >>> >> >> >> >> >>> --David >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Feb 25, 2019, at 12:33 PM, Jordan Harband <ljharb at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Additionally, https://github.com/tc39/proposal-pattern-matching - switch statements are something I hope we'll soon be able to relegate to the dustbin of history. >> >> >> >> >>> >> >> >> >> >>> On Mon, Feb 25, 2019 at 6:01 AM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> I quite aware that it’s covered in do expressions. Personally I find do expressions non-JavaScript in style and it’s also not necessarily going to make it into the language. >> >> >> >> >>> >> >> >> >> >>> Hence why I wanted to put out there the idea of switch expressions. >> >> >> >> >>> >> >> >> >> >>> --David >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Feb 25, 2019, at 5:28 AM, N. Oxer <blueshuk2 at gmail.com> wrote: >> >> >> >> >>> >> >> >> >> >>> Hi, >> >> >> >> >>> >> >> >> >> >>> This would be covered by do expressions. You could just do: >> >> >> >> >>> >> >> >> >> >>> ```js >> >> >> >> >>> const category = do { >> >> >> >> >>> switch (...) { >> >> >> >> >>> ... >> >> >> >> >>> }; >> >> >> >> >>> }; >> >> >> >> >>> ``` >> >> >> >> >>> >> >> >> >> >>> On Sun, Feb 24, 2019 at 10:42 AM David Koblas <david at koblas.com> wrote: >> >> >> >> >>> >> >> >> >> >>> After looking at a bunch of code in our system noted that there are many >> >> >> >> >>> cases where our code base has a pattern similar to this: >> >> >> >> >>> >> >> >> >> >>> let category = data.category; >> >> >> >> >>> >> >> >> >> >>> if (category === undefined) { >> >> >> >> >>> // Even if Tax is not enabled, we have defaults for incomeCode >> >> >> >> >>> switch (session.merchant.settings.tax.incomeCode) { >> >> >> >> >>> case TaxIncomeCode.RENTS_14: >> >> >> >> >>> category = PaymentCategory.RENT; >> >> >> >> >>> break; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >> >>> break; >> >> >> >> >>> case TaxIncomeCode.INDEPENDENT_PERSONAL_SERVICE_17: >> >> >> >> >>> category = PaymentCategory.SERVICES; >> >> >> >> >>> break; >> >> >> >> >>> } >> >> >> >> >>> } >> >> >> >> >>> >> >> >> >> >>> I also bumped into a block of go code that also implemented similar >> >> >> >> >>> patterns, which really demonstrated to me that there while you could go >> >> >> >> >>> crazy with triary nesting there should be a better way. Looked at the >> >> >> >> >>> pattern matching proposal and while could possibly help looked like it >> >> >> >> >>> was overkill for the typical use case that I'm seeing. The most relevant >> >> >> >> >>> example I noted was switch expressions from Java. When applied to this >> >> >> >> >>> problem really create a simple
After looking at a bunch of code in our system noted that there are many cases where our code base has a pattern similar to this:
I also bumped into a block of go code that also implemented similar patterns, which really demonstrated to me that there while you could go crazy with triary nesting there should be a better way. Looked at the pattern matching proposal and while could possibly help looked like it was overkill for the typical use case that I'm seeing. The most relevant example I noted was switch expressions from Java. When applied to this problem really create a simple result:
Note; the instead of using the '->' as Java, continue to use => and with
the understanding that the right hand side is fundamentally function. So similar things to this are natural, note this proposal should remove "fall through" breaks and allow for multiple cases as such.
Also compared this to the do expression proposal, it also provides a substantial simplification, but in a way that is more consistent with the existing language. In one of their examples they provide an example of the Redux reducer redux.js.org/basics/reducers#splitting-reducers -- this would be a switch expression implementation.