I agree with this analysis, especially the comment about “the One True Source Of Expressiveness: composition”.
—Guy > On Jan 18, 2021, at 10:20 AM, Brian Goetz <brian.go...@oracle.com> wrote: > > I think the question here is mostly one of terminology. Let’s restate the > problem first. The canonical example is: > > if (p instanceof Point(var x, var y) && x == y) { /* diagonal */ } > > Here, the language is fully powerful enough for the pattern to > test-and-extract on Point-ness, and the conjoined clause refines with an > arbitrary predicate. This comes “for free” because expressions compose. > > The switch analogue is: > > switch (p) { > case Point(var x, var y): > // I want to do one thing for diagonal points, and something else > for other points > } > > This is where it gets messy, because switch gives you one chance to classify > the target, and you have to live with that. I think we all agree that making > cases powerful enough to include type / deconstruction patterns has a “glass > half empty” character to it; there will be plenty of “cases” where we can’t > express what we want using a pattern switch, at least not without some ugly > and error-prone contortions. > > There have been three ways proposed out of this mess: > > 1. Have a “continue” statement that lets a case body say “whoops, I fell > into the wrong case, please keep trying to match at the next case.” > 2. Have an explicit feature of the switch statement to refine the pattern > with a predicate, such as: “case P when(e)”. > 3. Find a way to turn boolean expressions into patterns, and just use > pattern-AND composition. > > The confusion over terminology stems from the fact that we could describe all > these as sorts of guards: 1 is an “imperative guard”, 2 is a “switch guard”, > and 3 is a “guard pattern.” Which underscores we want some kind of > guarding-feature no matter what. > > John has defended #1 as being a good “primitive”. And from a VM-design > perspective, I agree; it is the sort of “control flow assembly language” we > might want, and it covers 100% of the cases. Unfortunately, as a user tool, > it has fewer good characteristics; it is like fallthrough squared. > > #2 had seemed like a “tolerable evil.” From a language design perspective, > it is surely a “bag” of sorts; it is limited in purpose, and doesn’t compose > with other features. It’s basically a “patch”, but one that users might find > easy enough to reason about. It is a candidate for a “worse is better” > solution. > > #3 is a feature that should make a PL designer happy; it is strictly more > powerful than #2, is more broadly applicable (we can use it in other contexts > where patterns are allowed), and its power derives from the One True Source > Of Expressiveness: composition. > > Even adding constant patterns or relational patterns (>= 0) doesn’t really > remove the need for some sort of guarding. > > The question is how we want to get there. #3 amounts to “turn a guard into a > pattern”. We might call it a “guard pattern”, or a “boolean pattern”. But > the terminology has gotten fuzzy, I agree. > > For the record, I am now strongly in Camp #3: we want AND pattern combination > anyway, so let’s find some way to turn a boolean expression into a pattern > and call that a win. > >> On Jan 18, 2021, at 3:03 AM, Remi Forax <fo...@univ-mlv.fr> wrote: >> >> Hi everybody, >> following the discussion about && / and between guards and patterns, >> i don't think i've a clear understanding on what is a pattern and what is a >> guard. >> >> When we first discuss of guards, the separation was a kind of clean, a >> pattern match something and a guard provides further refinements. >> By example, with a made a syntax, >> case Point(var x, var y) && x > 0 && y > 0 >> Point(var x, var y) is the pattern and x > 0 and y > 0 are the guards. >> >> So a pattern asks if something match and a guard uses the binding to add >> restrictions. >> >> As Brian said, in C#, you can directly test inside a pattern, using a weird >> syntax chosen by C# like >0, >> so the same example can be rewritten >> case Point(>0, >0) >> and with the bindings, i suppose something like >> case Point(var x >0, var y >0) >> >> Here the line between a pattern and a guard starts to become blurry, because >> >0 is pattern. >> >> So if there a difference between a pattern and a guard ? >> >> regards, >> Rémi >> >> >> >> >