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:53:01 GMT
Gotcha. Yes, that makes perfect sense.

-Kyle

On Tue, Oct 24, 2017 at 11:39 AM, Casey Stella <cestella@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 <
> 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