> 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 [email protected] www.isiahmeadows.com On Thu, Feb 28, 2019 at 12:28 PM David Koblas <[email protected]> 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 > > [email protected] > > www.isiahmeadows.com > > > > On Thu, Feb 28, 2019 at 9:23 AM Naveen Chawla <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 > >>>> [email protected] > >>>> www.isiahmeadows.com > >>>> > >>>> ----- > >>>> > >>>> Isiah Meadows > >>>> [email protected] > >>>> www.isiahmeadows.com > >>>> > >>>> > >>>> On Tue, Feb 26, 2019 at 1:34 PM David Koblas <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 > >>>> [email protected] > >>>> https://mail.mozilla.org/listinfo/es-discuss > >>>> > >>>> _______________________________________________ > >>>> es-discuss mailing list > >>>> [email protected] > >>>> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 > >>>> [email protected] > >>>> https://mail.mozilla.org/listinfo/es-discuss > >>>> > >>>> _______________________________________________ > >>>> es-discuss mailing list > >>>> [email protected] > >>>> https://mail.mozilla.org/listinfo/es-discuss > >>>> > >>>> > >>>> _______________________________________________ > >>>> es-discuss mailing list > >>>> [email protected] > >>>> https://mail.mozilla.org/listinfo/es-discuss > >>>> > >>>> _______________________________________________ > >>>> es-discuss mailing list > >>>> [email protected] > >>>> https://mail.mozilla.org/listinfo/es-discuss > >>>> > >>>> _______________________________________________ > >>>> es-discuss mailing list > >>>> [email protected] > >>>> https://mail.mozilla.org/listinfo/es-discuss > >>>> > >>>> > >>>> _______________________________________________ > >>>> es-discuss mailing list > >>>> [email protected] > >>>> https://mail.mozilla.org/listinfo/es-discuss > >> _______________________________________________ > >> es-discuss mailing list > >> [email protected] > >> https://mail.mozilla.org/listinfo/es-discuss _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

