Proposal: switch expressions

Naveen Chawla naveen.chwl at gmail.com
Thu Feb 28 09:37:54 UTC 2019


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>


More information about the es-discuss mailing list