Proposal: switch expressions
David Koblas
david at koblas.com
Thu Feb 28 14:06:05 UTC 2019
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>
More information about the es-discuss
mailing list