metron-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Casey Stella <ceste...@gmail.com>
Subject Re: Stellar support for switch/case style conditionals
Date Tue, 24 Oct 2017 15:39:32 GMT
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 <kylerichardson2@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 <cestella@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 <
> > kylerichardson2@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 <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