Proposal: switch expressions
David Koblas
david at koblas.com
Wed Feb 27 19:12:50 UTC 2019
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
More information about the es-discuss
mailing list