metron-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Kyle Richardson <kylerichards...@gmail.com>
Subject Re: Stellar support for switch/case style conditionals
Date Tue, 24 Oct 2017 15:26:53 GMT
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 <ottobackwards@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 (ottobackwards@gmail.com)
> wrote:
>
> OK
>
>
> On October 17, 2017 at 14:28:02, Casey Stella (cestella@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 <ottobackwards@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 (cestella@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 <ottobackwards@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 (cestella@gmail.com)
> wrote:
> >>
> >> Ugh, I forgot to preface this with DISCUSS: Sorry!
> >>
> >> On Tue, Oct 17, 2017 at 12:05 PM, Casey Stella <cestella@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
> >> >
> >>
> >>
> >
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message