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 [email protected] www.isiahmeadows.com On Sat, Mar 2, 2019 at 4:57 PM Naveen Chawla <[email protected]> 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, <[email protected]> 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 >> [email protected] >> www.isiahmeadows.com >> >> On Sat, Mar 2, 2019 at 3:43 AM Naveen Chawla <[email protected]> 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, <[email protected]> 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 >> >> [email protected] >> >> www.isiahmeadows.com >> >> >> >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <[email protected]> >> >> 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 <[email protected]> >> >> > 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 >> >> >> [email protected] >> >> >> www.isiahmeadows.com >> >> >> >> >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <[email protected]> >> >> >> 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 <[email protected]> >> >> >> > 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 >> >> >> >> On Fri, Mar 1, 2019 at 6:25 AM Naveen Chawla <[email protected]> >> >> 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 <[email protected]> >> >> > 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 >> >> >> [email protected] >> >> >> www.isiahmeadows.com >> >> >> >> >> >> On Thu, Feb 28, 2019 at 12:40 PM Naveen Chawla <[email protected]> >> >> >> 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 <[email protected]> >> >> >> > 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 _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

