Gotcha. Yes, that makes perfect sense. -Kyle
On Tue, Oct 24, 2017 at 11:39 AM, Casey Stella <ceste...@gmail.com> wrote: > Right. My point is that you never want the values of the match statement > to be evaluated prior to evaluating the match (otherwise you cannot short > circuit them). Consider the following: > > match {foo == 0 : CHEAP_FUNC(), foo != 0 : EXPENSIVE_FUNC() } > > you don't want CHEAP_FUNC() AND EXPENSIVE_FUNC() to be run every time it's > evaluated and have the values returned depending on foo's value. Instead, > you'd want to evaluate foo and choose which of EXPENSIVE_FUNC() or > CHEAP_FUNC() to evaluate (essentially treating the values as implicit > lambdas). This is how if/then/else's work now. For chained conditionals, > you aren't going to evaluate the else if the if condition is true. I just > want to make sure this fits that paradigm. > > Casey > > > On Tue, Oct 24, 2017 at 11:34 AM, Kyle Richardson < > kylerichards...@gmail.com > > wrote: > > > I guess I read it as supporting both. As a user, I certainly would prefer > > to use the less explicit syntax ( e.g. match { foo == 0: true, bar == 1: > > false, default: false } ). > > > > -Kyle > > > > On Tue, Oct 24, 2017 at 11:30 AM, Casey Stella <ceste...@gmail.com> > wrote: > > > > > So, I do like it. My only issue is the explicit lambda syntax in the > > > values there (e.g. foo == 0 : () -> true) I'm afraid that when we > > migrate > > > to a less explicit lambda syntax ( foo == 0 : true ), we will cause > > people > > > to have to transition twice in a row. > > > > > > Also, it goes without saying, but the syntax must be short-circuiting > > IMO. > > > > > > Casey > > > > > > On Tue, Oct 24, 2017 at 11:26 AM, Kyle Richardson < > > > kylerichards...@gmail.com > > > > wrote: > > > > > > > I like the way you have this laid out. Very useful to see it in test > > > cases. > > > > I'm +1 for this syntax addition. > > > > > > > > -Kyle > > > > > > > > On Mon, Oct 23, 2017 at 4:16 PM, Otto Fowler < > ottobackwa...@gmail.com> > > > > wrote: > > > > > > > > > What I would like to do for the first PR is introduce match with > the > > > > > following syntax > > > > > > > > > > > > > > > match{ logical expression : transformation expression, ….. , > > > default : > > > > > transformation expression} > > > > > > > > > > Such that the following work for example: > > > > > > > > > > @Test > > > > > public void testMatch() { > > > > > Assert.assertTrue(runPredicate("match { 1 >= 0 : ()-> true }", > new > > > > > HashMap(){{ > > > > > put("foo", 0); > > > > > }})); > > > > > Assert.assertTrue(runPredicate("match { foo == 0 : ()-> true, > > > > > default : ()-> false }", new HashMap(){{ > > > > > put("foo", 0); > > > > > }})); > > > > > Assert.assertFalse(runPredicate("match { foo == 0 : ()-> true, > > > > > default : ()-> false }", new HashMap(){{ > > > > > put("foo", 1); > > > > > }})); > > > > > > > > > > Assert.assertTrue(runPredicate("match { foo == 0 : ()-> false, > foo > > > > > == 1 : ()-> true, default : ()-> false }", new HashMap(){{ > > > > > put("foo", 1); > > > > > }})); > > > > > > > > > > Assert.assertTrue(runPredicate("match { foo == 0 : ()-> bFalse, > > foo > > > > > == 1 : ()-> bTrue, default : ()-> bFalse }", new HashMap(){{ > > > > > put("foo", 1); > > > > > put("bFalse", new Boolean(false)); > > > > > put("bTrue", new Boolean(true)); > > > > > }})); > > > > > > > > > > Assert.assertTrue(runPredicate("match { foo == 0 : ()-> bFalse, > > foo > > > > > == 1 : ()-> bTrue, default : ()-> bFalse }", new HashMap(){{ > > > > > put("foo", 1); > > > > > put("bFalse", new Boolean(false)); > > > > > put("bTrue", new Boolean(true)); > > > > > }})); > > > > > > > > > > Assert.assertTrue(runPredicate("match { foo == 0 : bFalse, foo > == > > 1 > > > > > : bTrue, default : false }", new HashMap(){{ > > > > > put("foo", 1); > > > > > put("bFalse", new Boolean(false)); > > > > > put("bTrue", new Boolean(true)); > > > > > }})); > > > > > > > > > > Assert.assertTrue(runPredicate("match { foo == 0 OR bar == > 'yes' > > : > > > > > ()-> true, default : ()-> false }", new HashMap(){{ > > > > > put("foo", 1); > > > > > put("bar", "yes"); > > > > > }})); > > > > > > > > > > Assert.assertEquals("warning", run("match{ threat.triage.level < > 10 > > > > > : 'info', threat.triage.level < 20 : 'warning', default : > 'critical' > > > > > }", new HashMap(){{ > > > > > put("threat.triage.level", 15); > > > > > }})); > > > > > } > > > > > > > > > > > > > > > So, the transformation expression will include support for zero arg > > > > lambda > > > > > syntax. The work to support the AS aliasing statement and lambda > > > support > > > > > for parameters may page in assignment and some other things, > > > > > and I would like to make that a follow on with some discussion > after > > > > > review. > > > > > > > > > > Thoughts? > > > > > > > > > > > > > > > > > > > > On October 17, 2017 at 14:31:07, Otto Fowler ( > > ottobackwa...@gmail.com) > > > > > wrote: > > > > > > > > > > OK > > > > > > > > > > > > > > > On October 17, 2017 at 14:28:02, Casey Stella (ceste...@gmail.com) > > > > wrote: > > > > > > > > > > Yeah, default would be a keyword. We could also do match(variable1 > > as > > > x, > > > > > variable2 as y) if you want to alias your fields *or* you could do > > > match > > > > { > > > > > ... } if you dont' want to alias your variables. > > > > > > > > > > e.g. if you had a field threat.triage.level either of these would > > work: > > > > > > > > > > match(threat.triage.level -> x) { x < 10 : 'info', x < 20 : > > 'warning', > > > > > default : 'critical' } > > > > > OR > > > > > match { threat.triage.level < 10 : 'info', threat.triage.level < > 20 : > > > > > 'warning, default : 'critical' } > > > > > > > > > > > > > > > > > > > > On Tue, Oct 17, 2017 at 2:24 PM, Otto Fowler < > > ottobackwa...@gmail.com> > > > > > wrote: > > > > > > > > > > > No that is it. > > > > > > > > > > > > So default would be a keyword? > > > > > > > > > > > > and a lambda that uses x can be used on the right side of the : > > > > > > > > > > > > > > > > > > > > > > > > On October 17, 2017 at 14:21:01, Casey Stella ( > ceste...@gmail.com) > > > > > wrote: > > > > > > > > > > > > So, just to map this onto the example, you mean: > > > > > > match(longer_variable -> x) { x < 10 : 'info', x <= 20 : 'warn', > > > > default: > > > > > > 'critical' } ? I took the liberty of adding a default keyword > > there > > > > the > > > > > > evaluation of the conditionals are considered lambda functions > > also. > > > > > > > > > > > > Did I catch the spirit of the suggestion or did I miss anything? > > > > > > > > > > > > On Tue, Oct 17, 2017 at 2:18 PM, Otto Fowler < > > > ottobackwa...@gmail.com> > > > > > > wrote: > > > > > > > > > > > >> How about this: > > > > > >> > > > > > >> match(VAR_TO_VAL_ASSIGNMENT+) { BOOLEAN_STATEMENT(VALS) : > > > > LAMBDA(VALS), > > > > > >> BOOLEAN_STATEMENT(VALS) : LAMBDA(VALS) , LAMBDA(VALS)} > > > > > >> > > > > > >> * match = new keyword > > > > > >> * match takes variable number of assignments, where the val > > assigned > > > > to > > > > > >> is available in the evaluation and the lambdas > > > > > >> * match {} contains comma separated list of a statement that > > > evaluates > > > > > to > > > > > >> a boolean and a lambda > > > > > >> * LAMBDA is executed on match, and it’s value is returned > > > > > >> * no matches returns null or return of optional final statement, > > > which > > > > > is > > > > > >> a LAMBDA without a BOOLEAN_STATEMENT > > > > > >> > > > > > >> > > > > > >> On October 17, 2017 at 12:06:05, Casey Stella ( > ceste...@gmail.com > > ) > > > > > wrote: > > > > > >> > > > > > >> Ugh, I forgot to preface this with DISCUSS: Sorry! > > > > > >> > > > > > >> On Tue, Oct 17, 2017 at 12:05 PM, Casey Stella < > > ceste...@gmail.com> > > > > > >> wrote: > > > > > >> > > > > > >> > Hi All, > > > > > >> > > > > > > >> > It's high time that Stellar supports some form of conditional > > that > > > > is > > > > > >> > beyond if/then/else. Right now, the way to do fall-through > > > > > conditionals > > > > > >> is: > > > > > >> > > > > > > >> > if x < 10 then 'info' else if x >= 10 && x <= 20 then 'warn' > > else > > > > > >> > 'critical' > > > > > >> > > > > > > >> > That becomes non-scalable very quickly. I wanted to > facilitate a > > > > > >> > discussion with the community on the syntax. I'll give a few > > > options > > > > > and > > > > > >> > you guys/gals can come up with your own suggestions too, but I > > > > wanted > > > > > to > > > > > >> > frame teh conversation. > > > > > >> > > > > > > >> > *MAP-BASED SWITCH* > > > > > >> > > > > > > >> > With the advent of METRON-1254 (https://github.com/apache/met > > > > > >> ron/pull/801), > > > > > >> > we could enable (from a language perspective in Stellar) > > > multi-part > > > > > >> > conditionals or switch/case style statements. To wit: > > > > > >> > > > > > > >> > MAP_GET(true, { x < 10 : 'info', x >= 10 && x <= 20 : 'warn', > x > > > > > > > 20 : > > > > > >> > 'critical' }) > > > > > >> > > > > > > >> > Or, with a convenience function: > > > > > >> > > > > > > >> > CASE( { x < 10 : 'info', x >= 10 && x <= 20 : 'warn', x > 20 : > > > > > >> 'critical' > > > > > >> > } ) > > > > > >> > > > > > > >> > The issue with this is that the last true condition wins > because > > > > we're > > > > > >> > using a map. > > > > > >> > > > > > > >> > *LIST-BASED SWITCH* > > > > > >> > > > > > > >> > We could correct this by adding a list of pairs construction > to > > > > > stellar: > > > > > >> > > > > > > >> > CASE( [ x < 10 : 'info', x <= 20 : 'warn'], 'critical') > > > > > >> > > > > > > >> > This would enable us to allow the first true condition to win, > > so > > > > the > > > > > >> > second condition can be simpler and we could pass a default > > return > > > > > >> value as > > > > > >> > the final argument. > > > > > >> > The downside to this, is that it requires a language > enhancement > > > > (the > > > > > >> list > > > > > >> > of pairs construction you see there). > > > > > >> > > > > > > >> > *LAMBDA FUNCTION-BASED SWITCH* > > > > > >> > > > > > > >> > Some of the problems with the previous statements are that > every > > > > > >> > conditional has to be evaluated and there is no opportunity to > > > short > > > > > >> > circuit. They're all evaluated at parse-time rather than > > execution > > > > > time. > > > > > >> > We could, instead, construct a lambda function approach to > this > > > and > > > > > >> support > > > > > >> > short-circuiting in even complex conditionals: > > > > > >> > > > > > > >> > CASE( real_variable_name, [ x -> x < 10 ? 'info', x -> x <= > 20 ? > > > > > 'warn' > > > > > >> ], > > > > > >> > 'critical') > > > > > >> > or > > > > > >> > CASE( real_variable_name, [ x -> if x < 10 then 'info', x -> > if > > x > > > <= > > > > > 20 > > > > > >> > then 'warn' ], 'critical') > > > > > >> > > > > > > >> > This would require lessening ?: (if/then/else) syntax to > support > > > to > > > > > >> enable > > > > > >> > just if without else conditions. This also has the benefit of > > > > allowing > > > > > >> > simplifying the expression due to lambda function variable > > > renaming > > > > > >> > (real_variable_name can be much more complex (or even an > > > expression) > > > > > >> than > > > > > >> > 'x'. > > > > > >> > > > > > > >> > Creative other approaches to this are appreciated! > > > > > >> > > > > > > >> > Thanks, > > > > > >> > > > > > > >> > Casey > > > > > >> > > > > > > >> > > > > > >> > > > > > > > > > > > > > > > > > > > > >