You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@metron.apache.org by Casey Stella <ce...@gmail.com> on 2017/10/17 16:05:38 UTC

Stellar support for switch/case style conditionals

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/metron/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

Re: Stellar support for switch/case style conditionals

Posted by Kyle Richardson <ky...@gmail.com>.
Gotcha. Yes, that makes perfect sense.

-Kyle

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

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
@Test
@SuppressWarnings("unchecked")
public void testShortCircut() {

 Assert.assertEquals("ok",  run("match{ foo > 100 : THROW('oops'), foo
> 200 : THROW('oh no'), default : 'ok' }",
        new HashMap() {{
          put("foo", 50);
        }}));
}




On October 24, 2017 at 11:59:31, Otto Fowler (ottobackwards@gmail.com)
wrote:

I will also add explicit tests for proving non-execution of non-matching
clauses ;)


On October 24, 2017 at 11:52:25, Otto Fowler (ottobackwards@gmail.com)
wrote:

I did implement short circuiting.

I am going to submit, with the additional outstanding issue that using the
MAP() function in the evaluation
is broken.  I may need some help with that.  I fear that it may be do to my
syntax, which may work for other
cases, but is still not fundamentally correct, and thus exposed through the
MAP().



On October 24, 2017 at 11:39:46, 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 <ce...@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 <ot...@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
> > > > >> >
> > > > >>
> > > > >>
> > > > >
> > > >
> > >
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
I will also add explicit tests for proving non-execution of non-matching
clauses ;)


On October 24, 2017 at 11:52:25, Otto Fowler (ottobackwards@gmail.com)
wrote:

I did implement short circuiting.

I am going to submit, with the additional outstanding issue that using the
MAP() function in the evaluation
is broken.  I may need some help with that.  I fear that it may be do to my
syntax, which may work for other
cases, but is still not fundamentally correct, and thus exposed through the
MAP().



On October 24, 2017 at 11:39:46, 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 <ce...@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 <ot...@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
> > > > >> >
> > > > >>
> > > > >>
> > > > >
> > > >
> > >
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
I did implement short circuiting.

I am going to submit, with the additional outstanding issue that using the
MAP() function in the evaluation
is broken.  I may need some help with that.  I fear that it may be do to my
syntax, which may work for other
cases, but is still not fundamentally correct, and thus exposed through the
MAP().



On October 24, 2017 at 11:39:46, 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 <ce...@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 <ot...@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
> > > > >> >
> > > > >>
> > > > >>
> > > > >
> > > >
> > >
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Casey Stella <ce...@gmail.com>.
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 <ce...@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 <ot...@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
> > > > >> >
> > > > >>
> > > > >>
> > > > >
> > > >
> > >
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Kyle Richardson <ky...@gmail.com>.
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 <ce...@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 <ot...@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 <ot...@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 <ce...@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
> > > >> >
> > > >>
> > > >>
> > > >
> > >
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
Sorry my examples where so lambda heavy, I did that support first, because
LambdaExpression is ‘special’, as you know Casey.



On October 24, 2017 at 11:30:33, 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 <ot...@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 <ot...@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 <ot...@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 <ce...@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
> > >> >
> > >>
> > >>
> > >
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Casey Stella <ce...@gmail.com>.
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 <ot...@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 <ot...@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 <ot...@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 <ce...@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
> > >> >
> > >>
> > >>
> > >
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Kyle Richardson <ky...@gmail.com>.
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 <ot...@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 <ot...@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 <ot...@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 <ce...@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
> >> >
> >>
> >>
> >
>

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
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 <ot...@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 <ot...@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 <ce...@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
>> >
>>
>>
>

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
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 <ot...@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 <ot...@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 <ce...@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
>> >
>>
>>
>

Re: Stellar support for switch/case style conditionals

Posted by Casey Stella <ce...@gmail.com>.
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 <ot...@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 <ot...@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 <ce...@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
>> >
>>
>>
>

Re: Stellar support for switch/case style conditionals

Posted by Casey Stella <ce...@gmail.com>.
There's no string concat candy, so that'd probably be default:
FORMAT('critical-%s%s', x, y)

On Tue, Oct 17, 2017 at 2:28 PM, Otto Fowler <ot...@gmail.com>
wrote:

> match(longer_variable -> x, other_variable -> y) { x < 10 : 'info', x <=
> 20 : 'warn’, x < y : ‘oh boy’,  default: ‘critical- ‘ + TO_STRING(x) +
> TO_STRING(y) }
>
>
> On October 17, 2017 at 14:26:00, Otto Fowler (ottobackwards@gmail.com)
> wrote:
>
> match(longer_variable -> x) { x < 10 : 'info', x <= 20 : 'warn', default:
> 'critical' }
>
>

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
match(longer_variable -> x, other_variable -> y) { x < 10 : 'info', x <= 20
: 'warn’, x < y : ‘oh boy’,  default: ‘critical- ‘ + TO_STRING(x) +
TO_STRING(y) }


On October 17, 2017 at 14:26:00, Otto Fowler (ottobackwards@gmail.com)
wrote:

match(longer_variable -> x) { x < 10 : 'info', x <= 20 : 'warn', default:
'critical' }

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
match(longer_variable -> x) { x < 10 : 'info', x <= 20 : 'warn',
default: ‘critical’ + TO_STRING(x) }


On October 17, 2017 at 14:24:09, Otto Fowler (ottobackwards@gmail.com)
wrote:

match(longer_variable -> x) { x < 10 : 'info', x <= 20 : 'warn', default:
'critical' }

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
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 <ot...@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 <ce...@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/
> metron/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
> >
>
>

Re: Stellar support for switch/case style conditionals

Posted by Casey Stella <ce...@gmail.com>.
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 <ot...@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 <ce...@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/
> metron/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
> >
>
>

Re: Stellar support for switch/case style conditionals

Posted by Otto Fowler <ot...@gmail.com>.
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 <ce...@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/metron/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
>

Re: Stellar support for switch/case style conditionals

Posted by Casey Stella <ce...@gmail.com>.
Ugh, I forgot to preface this with DISCUSS:  Sorry!

On Tue, Oct 17, 2017 at 12:05 PM, Casey Stella <ce...@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/metron/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
>