Re: [External] : Re: Guards redux
> On 12 Mar 2021, at 11:02, Remi Forax wrote: > > - Mail original - >> De: "Gavin Bierman" >> À: "John Rose" >> Cc: "amber-spec-experts" >> Envoyé: Vendredi 12 Mars 2021 11:11:46 >> Objet: Re: Guards redux > >> Thanks everyone for the discussion. I have updated the JEP: >> >> http://openjdk.java.net/jeps/8213076 >> >> It proposes *guarded patterns* of the form `p&&e`, as well as parenthesized >> patterns, written `(p)`. > > > Hi Gavin, > nice, i find this version more appealing. Yay! > In the first example that shows && as a guard, the example uses parenthesis > which is my opinion should be removed >case Triangle t && (t.calculateArea() > 100) -> >System.out.println("Large Triangle"); > > if i read the grammar correctly, those parenthesis around the boolean > expression are unnecessary >case Triangle t && t.calculateArea() > 100 -> They are unnecessary, but this example comes before the grammar, so I didn’t want anyone to get confused. > > it will be more inline with the example above in the text that is using 'when' >case Triangle t when t.calculateArea() > 100 -> > > > Otherwise, in the document, the words "->-style" and ":-style" are hard to > parse, i think array-style and colon-style are better terms. I’ll take a look. > In the section "Dominance of pattern label", > It's not clear to me if there is a dominance in between expressions, > by example, can we have the following cases in that order ? > > case Foo(var a, var b) && a == 3: > case Foo(var a, var b) && a == 3 && b == 4: > > for me the answer is yes, the dominance is computed on patterns only, not on > expressions. I agree. I don’t think trying to calculate dominance between expressions is viable. Thanks Rémi! Gavin
Re: [External] : Re: Guards redux
On Mar 10, 2021, at 11:10 AM, Guy Steele wrote: > > But in principle it does matter for patterns, because while patterns arguably > do not involve _evaluation_, they most certainly involve _execution_ of > possibly user-written code. If side effects can occur, the distinction > arguably matters, and this is worth recognizing as we debate the design. I don’t think user-written patterns can reliably exclude side effects, and I think in some cases they will be useful (to accumulate some sort of “log” on the side of matching activity, perhaps to support backtracking). So let’s say that one way or another we have to specify ordering of side effects patterns. (Eww: What about class loading side effects for resolved class names? Do we have to package type names in thunks for the pattern runtime? Please, no.) Given nested patterns P(Q,R), if P fails then there’s no way to execute Q. If Q fails there’s no benefit to executing R, although that would be possible to more closely emulate unconditional evaluation of function parameters. Is there any reason to do this? I think not. And then P(Q & R) also has no reason to execute R if Q fails. As I said to Remi, importing unconditional evaluation rules from expressions into patterns makes no sense. As Brian said, pattern-& is as similar to type-& to expression-&.
Re: [External] : Re: Guards redux
> On Mar 10, 2021, at 12:04 PM, Brian Goetz wrote: > > > >> You nail the guard to a pattern, which is equivalent until we have nested >> patterns (and "or"/"and" patterns). > > We have nested patterns already in the JEPs on the table. Where's the > problem? > >> I see a lot of advantages of using && to link a guard to a pattern, >> - the symbol is heavy so there is a clear visual separation >> - without any supplementary parenthesis, && after the type pattern in an >> instanceofis the && between expression, it's almost like you can not have a >> guard with an instanceof, in practice, few instanceof will have a guard. > > I agree few instanceof will have a guard, but users are still free to express > it that way if they like, and there's nothing wrong with that. > >> I still think that using a guard inside a nested pattern is ugly but it can >> be just that, ugly. Someone may want a short-circuit in a deeply nested >> patterns . > > Yes. This is not unlike other compositions; for any compositional tool, you > can overuse it. (You can arbitrarily compose boolean expressions (or > arbitrarily chain method invocations), but sometimes this is taking it too > far.) > >> As i said to Gavin, i'm not at ease with using the symbol '&' in between >> patterns. > > I think that this is mostly a "who moved my cheese" reaction; you're used to > thinking that & is just for bitwise operations. But, that's not actually > true; we already use & and | on types -- intersection type casts, additional > generic type bounds, multi-catch. This appeals to a notion that & and | are > boolean-like combinators on types (even if not exposed in all places they > might make sense), but this is a different kind of combination than on > integers. And yet a different kind of combination on patterns. (In an > alternate universe, we might have different symbols for adding ints vs floats > vs string concatenation, but + works well enough that overloading the symbols > is OK -- because using + in this way appeals to the underlying algebraic > monoid structure these types share.) We all know that `+` is not your best poster child for this argument. (“foo” + 1) + 2 produces “foo12” “foo” + (1 + 2) produces “foo3” which is not my idea of good monoid behavior. But there is a lot of wiggle room in your use of the word “appeals”. :-) > The reason that & and | make sense on patterns, and on types, is that, like > the more familiar versions on bits, they describe a _boolean algebra_. > Boolean algebras have familiar properties such as De Morgan's Laws. These > work for types (when interpreted as value sets) as well as bits, and they > work for patterns too. > > I think where you're getting hung up is that when patterns produce bindings, > and other patterns consume those bindings, we have a dataflow dependence > which would appear to undermine certain other expected properties of a > boolean algebra, such as commutativity. But, if we view those dataflow > dependencies as a separate constraint -- as we *already do* for ints (e.g., > `(x=3)&(x|4)`, is invalid when `x` is an DU int, but valid when `x` is DA), > this seeming contradiction vanishes, and is seen to be merely a post-hoc > well-formedness constraint. If the WF constraint is satisfied, the expected > properties of boolean algebras (associativity, commutativity, absorption, > etc) are satisfied too. And yet, even if we stipulate all that, it is still the case that in expressions, Java uses `&&` to indicate short-circuiting and `&` to indicate no reliance on short-circuiting, and this is long-standing, familiar use. For the other applications cited (intersection type casts, additional generic type bounds, multi-catch) this distinction does not matter. But in principle it does matter for patterns, because while patterns arguably do not involve _evaluation_, they most certainly involve _execution_ of possibly user-written code. If side effects can occur, the distinction arguably matters, and this is worth recognizing as we debate the design. Nevertheless, as I just wrote in an earlier email, I think that the desire to maintain this distinction should be outweighed by other considerations (visual analogy to type intersection, for which `&` is used but not `&&`; need for distinct symbols for pattern conjunction action and guard attachment in order to solve parsing problems; desire not to invent an arbitrary new symbol such as `&:` or `&&&`).
Re: [External] : Re: Guards redux
You nail the guard to a pattern, which is equivalent until we have nested patterns (and "or"/"and" patterns). We have nested patterns already in the JEPs on the table. Where's the problem? I see a lot of advantages of using && to link a guard to a pattern, - the symbol is heavy so there is a clear visual separation - without any supplementary parenthesis, && after the type pattern in an instanceofis the && between expression, it's almost like you can not have a guard with an instanceof, in practice, few instanceof will have a guard. I agree few instanceof will have a guard, but users are still free to express it that way if they like, and there's nothing wrong with that. I still think that using a guard inside a nested pattern is ugly but it can be just that, ugly. Someone may want a short-circuit in a deeply nested patterns . Yes. This is not unlike other compositions; for any compositional tool, you can overuse it. (You can arbitrarily compose boolean expressions (or arbitrarily chain method invocations), but sometimes this is taking it too far.) As i said to Gavin, i'm not at ease with using the symbol '&' in between patterns. I think that this is mostly a "who moved my cheese" reaction; you're used to thinking that & is just for bitwise operations. But, that's not actually true; we already use & and | on types -- intersection type casts, additional generic type bounds, multi-catch. This appeals to a notion that & and | are boolean-like combinators on types (even if not exposed in all places they might make sense), but this is a different kind of combination than on integers. And yet a different kind of combination on patterns. (In an alternate universe, we might have different symbols for adding ints vs floats vs string concatenation, but + works well enough that overloading the symbols is OK -- because using + in this way appeals to the underlying algebraic monoid structure these types share.) The reason that & and | make sense on patterns, and on types, is that, like the more familiar versions on bits, they describe a _boolean algebra_. Boolean algebras have familiar properties such as De Morgan's Laws. These work for types (when interpreted as value sets) as well as bits, and they work for patterns too. I think where you're getting hung up is that when patterns produce bindings, and other patterns consume those bindings, we have a dataflow dependence which would appear to undermine certain other expected properties of a boolean algebra, such as commutativity. But, if we view those dataflow dependencies as a separate constraint -- as we *already do* for ints (e.g., `(x=3)&(x|4)`, is invalid when `x` is an DU int, but valid when `x` is DA), this seeming contradiction vanishes, and is seen to be merely a post-hoc well-formedness constraint. If the WF constraint is satisfied, the expected properties of boolean algebras (associativity, commutativity, absorption, etc) are satisfied too.
Re: [External] : Re: Guards
On 10 Mar 2021, at 00:55, Guy Steele mailto:guy.ste...@oracle.com>> wrote: On Mar 9, 2021, at 1:47 PM, Brian Goetz mailto:brian.go...@oracle.com>> wrote: Apart from what have said about letting grobble to fully access to the bindings Except that argument doesn't make sense. Accessing the bindings is not a special behavior of grobble, but a natural consequence of flow scoping. If I have P && g (or P & grobble(g)), then the scoping rules will tell us that the true set of P is present in g, and we're done. Nothing special here. Well, just to be fair, rules for flow scoping within expressions g && h already exist, but rules for P && g and P & Q do not yet exist. It’s easy to see that they probably should exist, and that this can be done in a manner entirely analogous to the way flow scoping is already done in expressions, but it has been an implicit assumption in some of the past discussion that is worth making explicit. “Consider it done!” :-) The draft JEP does try to be reasonably informative on this matter: A conditional-and pattern p & q introduces the union of the pattern variables introduced by the patterns p and q. The scope of any pattern variable declaration in p includes the pattern q. (This allows for a pattern such as String s & true(s.length() > 1) -- a value matches this pattern if it can be cast to a String and that string has a length of two or more characters.) Gavin
Re: [External] : Re: Guards
> On Mar 9, 2021, at 1:47 PM, Brian Goetz wrote: > > >> Apart from what have said about letting grobble to fully access to the >> bindings > > Except that argument doesn't make sense. Accessing the bindings is not a > special behavior of grobble, but a natural consequence of flow scoping. If I > have P && g (or P & grobble(g)), then the scoping rules will tell us that the > true set of P is present in g, and we're done. Nothing special here. Well, just to be fair, rules for flow scoping within expressions g && h already exist, but rules for P && g and P & Q do not yet exist. It’s easy to see that they probably should exist, and that this can be done in a manner entirely analogous to the way flow scoping is already done in expressions, but it has been an implicit assumption in some of the past discussion that is worth making explicit. “Consider it done!” :-) —Guy
Re: [External] : Re: Guards
On Mar 9, 2021, at 2:57 PM, fo...@univ-mlv.fr wrote: > > > > De: "John Rose" > À: "Remi Forax" > Cc: "Brian Goetz" , "amber-spec-experts" > > Envoyé: Mardi 9 Mars 2021 23:05:28 > Objet: Re: [External] : Re: Guards > On Mar 9, 2021, at 1:45 PM, fo...@univ-mlv.fr <mailto:fo...@univ-mlv.fr> > wrote: > > I see the pattern + parameters has a partial application, i.e. the parameters > other than the target are constants, > the keys of the map are constant, the java.util.regex.Pattern (or the string) > of a metch is a constant, etc > > So yes, a pattern have input parameters other than the target but should they > have access to the bindings, i think we are loosing a lot with that model. > > If I read you correctly, you want patterns to be > as statically scrutable as possible, so that translation > strategies can boil together as much of the pattern > as possible before first execution, typically using > indy or condy to do “advanced” configuration > and optimization of statement logic. > > Not for the translation strategy, just to be able to rapidly read all the > patterns without having to precisely follow the control flow. > I would like to have the horizontal reading to be as simple as possible so > you can grasp the whole pattern matching vertically. > Another way to see it is to see Pattern as declarations more than expressions. OK. So you are saying you are OK with northward dependencies on non-constant data (names of locals, etc.) but not westward. (BTW, we all agree that southward and westward dependencies are harder to cope with. Note, however, that Java declaration syntax has a prevailing eastward dependency, from the initializer. Just something to keep in mind for later.) When you say “horizontal reading”, I note that one *does* read westward dependencies before reading content to the east, but I think you want the whole line to be readable at once. Well, that’s fine, but it’s not clear that is a hard constraint on what we are designing. A coder can code that way. Even with guards, the “&&G” part can and sometimes should be put on its own line, south of the pattern. I can derive a soft requirement out of your point, and that is that “it would be nice” if “westward” dependencies in patterns are hard to hide. The “&&” operator fulfills this requirement for guards, since “&&” is relatively bold in appearance (easy to spot) and is not used for patterns. This is also the reason I (very tentatively) suggested that a unary version of the “&&” syntax might also be useful for marking in-arguments inside of patterns. If that suggestion doesn’t fly, maybe we can come up with some other marker for pattern in-args in general, or maybe just for “westward in-args in the same pattern”, that serves the purpose of making them stand out. (And without committing the newbie error of Always Making the NEW Feature **Stand OUT**, with h/t to Neal Gafter.) Happily, we can solve the general problem of in-arg marking separately from this design iteration. — John
Re: [External] : Re: Guards
> De: "John Rose" > À: "Remi Forax" > Cc: "Brian Goetz" , "amber-spec-experts" > > Envoyé: Mardi 9 Mars 2021 23:05:28 > Objet: Re: [External] : Re: Guards > On Mar 9, 2021, at 1:45 PM, [ mailto:fo...@univ-mlv.fr | fo...@univ-mlv.fr ] > wrote: >> I see the pattern + parameters has a partial application, i.e. the parameters >> other than the target are constants, >> the keys of the map are constant, the java.util.regex.Pattern (or the >> string) of >> a metch is a constant, etc >> So yes, a pattern have input parameters other than the target but should they >> have access to the bindings, i think we are loosing a lot with that model. > If I read you correctly, you want patterns to be > as statically scrutable as possible, so that translation > strategies can boil together as much of the pattern > as possible before first execution, typically using > indy or condy to do “advanced” configuration > and optimization of statement logic. Not for the translation strategy, just to be able to rapidly read all the patterns without having to precisely follow the control flow. I would like to have the horizontal reading to be as simple as possible so you can grasp the whole pattern matching vertically. Another way to see it is to see Pattern as declarations more than expressions. > Moreover, I think you want patterns to *reject* > non-constant operands, so that such configurations > are reliably done before first execution. > I whole-heartedly agree with the first goal, > but I think the second would be a bridge too > far, because various kinds of refactorings > can shift expressions in and out of the > “constant” category, and that’s useful. yes, it's not "constant" for the language. It's morally constant, the same way the lambdas in a stream has to be side effect free. [...] > — John Rémi
Re: [External] : Re: Guards
On Mar 9, 2021, at 1:45 PM, fo...@univ-mlv.fr wrote: > > I see the pattern + parameters has a partial application, i.e. the parameters > other than the target are constants, > the keys of the map are constant, the java.util.regex.Pattern (or the string) > of a metch is a constant, etc > > So yes, a pattern have input parameters other than the target but should they > have access to the bindings, i think we are loosing a lot with that model. If I read you correctly, you want patterns to be as statically scrutable as possible, so that translation strategies can boil together as much of the pattern as possible before first execution, typically using indy or condy to do “advanced” configuration and optimization of statement logic. Moreover, I think you want patterns to *reject* non-constant operands, so that such configurations are reliably done before first execution. I whole-heartedly agree with the first goal, but I think the second would be a bridge too far, because various kinds of refactorings can shift expressions in and out of the “constant” category, and that’s useful. I think we can trust users to use constants in places where they are advantageous. You may well ask, “but what is the language support for reliably folding constants at javac translation time?” And the answer is “nothing yet”. But I think that is a job for Brian’s constant-folding mechanisms, building on top of his ConstantDesc work. Until that point, we can live with non-constant expressions. I would say, before that point, it would be foolish to add indy support for constant in-args, just for and only for patterns. — John
Re: [External] : Re: Guards
There are two kinds of input args, one is the implicit target, the others are other arguments. It's not obvious to me that the parameters other than the target have to have access to the bindings. You keep saying "access to the bindings" as if that is a special thing. Bindings are ordinary local variables. Code has "access" to the bindings when the bindings are in scope. There's nothing magic here.
Re: [External] : Re: Guards
- Mail original - > De: "John Rose" > À: "Brian Goetz" > Cc: "Remi Forax" , "amber-spec-experts" > > Envoyé: Mardi 9 Mars 2021 19:56:42 > Objet: Re: [External] : Re: Guards > On Mar 9, 2021, at 10:47 AM, Brian Goetz wrote: >> >> >>> Apart from what have said about letting grobble to fully access to the >>> bindings >> >> Except that argument doesn't make sense. Accessing the bindings is not a >> special behavior of grobble, but a natural consequence of flow scoping. If I >> have P && g (or P & grobble(g)), then the scoping rules will tell us that the >> true set of P is present in g, and we're done. Nothing special here. > > Piling on: > > If patterns ever have in-args (in addition to out-args, > and they will!), then flow-scoping, regularly applied, > will allow those in-args to access previously bound > out-args to the left, within the same compound pattern, > whether in an instanceof pattern or a case label pattern. I agree that patterns have input args. There are two kinds of input args, one is the implicit target, the others are other arguments. It's not obvious to me that the parameters other than the target have to have access to the bindings. I see the pattern + parameters has a partial application, i.e. the parameters other than the target are constants, the keys of the map are constant, the java.util.regex.Pattern (or the string) of a metch is a constant, etc So yes, a pattern have input parameters other than the target but should they have access to the bindings, i think we are loosing a lot with that model. > > And, guards (whether built-in && as I claim they > should be) or a privileged use of in-args on a standard > method, will naturally have access to those same > leftward out-arg bindings. > > I think it’s a great model. The reason we are agonizing > here is kind of artificial, because in-args in patterns > are a 99.99% probable future, of which guards are > the first manifestation. > > — John Rémi
Re: [External] : Re: Guards
On Mar 9, 2021, at 10:47 AM, Brian Goetz wrote: > > >> Apart from what have said about letting grobble to fully access to the >> bindings > > Except that argument doesn't make sense. Accessing the bindings is not a > special behavior of grobble, but a natural consequence of flow scoping. If I > have P && g (or P & grobble(g)), then the scoping rules will tell us that the > true set of P is present in g, and we're done. Nothing special here. Piling on: If patterns ever have in-args (in addition to out-args, and they will!), then flow-scoping, regularly applied, will allow those in-args to access previously bound out-args to the left, within the same compound pattern, whether in an instanceof pattern or a case label pattern. And, guards (whether built-in && as I claim they should be) or a privileged use of in-args on a standard method, will naturally have access to those same leftward out-arg bindings. I think it’s a great model. The reason we are agonizing here is kind of artificial, because in-args in patterns are a 99.99% probable future, of which guards are the first manifestation. — John
Re: [External] : Re: Guards
Apart from what have said about letting grobble to fully access to the bindings Except that argument doesn't make sense. Accessing the bindings is not a special behavior of grobble, but a natural consequence of flow scoping. If I have P && g (or P & grobble(g)), then the scoping rules will tell us that the true set of P is present in g, and we're done. Nothing special here.