Relational DataFlows

 

Already DEAD

This page is already a dead end as we have found better more untuitive ways to deal with conditional logic. This has now made it's way into the 6.0 spec.

https://community.jboss.org/wiki/Drools60

Introduction

When reading this document please cast aside preconcieved ideas of what a rule engine is or how it works. The document is conceptual in nature and at this stage I would like conceptual input on how to progress the ideas, and not spend 95% of the time argueing over which symbols or keywords to use, or whether it's needed (see http://www.bikeshed.com). Expect many rough sketches, incomplete syntax etc, it is conceptual brain storming and not an implementation spec.

 

This document is to explore the power of the LHS "view" when applied to handling signal processing type problems, such as those handled in erlang. It will also compliment further research into using the LHS "view" for Linq type scenarios and our functional programming research. So please consider the view as an atomic data flow, and not as part of a complicated inference system. We want to conceptually take the concept as far a we can, to find all the edge cases before we worry about realities of implementation and use and cosmetics (symbols and keywords). It is expected that the technical language should be supported via a visual modeller for complex use cases that visualise the data flows as graphs.

 

Pattern filters on Variables

The following are currently both valid patterns:

Person( name == "mark" )

org.domain.Person( name == "mark" )

 

query xxx( Person $p )

    Person( name == "mark ) from $p

end

 

1) allow pattern filters to work on variable, using type inference to keep it compact.

 

$p( name == "mark" ) 

----

query xxx( Person $p ) 
    $p( name == "mark ) 
end 

 

2) Allow > and < for direction flow.

Person() < $p 
$p > Person() 

 

This also involves precedence issues, to with parenthesis will be important. But this is no different to F#'s forward and backward pipe operators, |> and <|, http://theburningmonk.com/2011/09/fsharp-pipe-forward-and-pipe-backward/

 

In the above example:

Person() < $p 

is equivalent to the existing syntax

Person() from $p

 

2.1) > is implicit

A() B() 

Is same as:

A() > B() 

Which is also the same as:

B() < A()

 

Parenthesis may be needed to ensure correct precedence and flow, in much the same way as it is necessary in F#'s pipe forward and pipe backwards operators.

 

Taking the following example:

A() B() C() D()

 

If we want wanted to use C() < B(), we would need parenthesis to ensure the original flow:

A() > ( C() < B() ) > D()

 

In the above A flows into the group, which has B as the head. B flows into D. The results of the group flow into D. When combined with the flow failure <! and !> operator it allows for tree like strucutures for signal processing. It is important to ensure that there is regularity and consistency with regards to flow directiona and precedence.

 

The above example of:

A() > ( C() < B() ) > D()

In terms of flow is similar too that already introduced by the implicit behaviour of 'from'

$r : Room( $people : people)
$owner: Person() from $people
$pet : Pet( owner == $owner )

 

The results of Room flow forward first into the 'from' expression of "people". That expression then flows backwards into Person(), the results of which then flow forward again into Pets.

It may be that, as in the case of 'from' that precedence can be inferred without the use of parenthesis for some use cases. For example below it is obviously that A must first flow into $people. I replaced B with $people to help with visualisation approximation of the above example.

A() 
C() < $people
D()

 

That implicit hard coded approach to flow, via 'from, is now generalised and made explicit via < and > operators and parenthesis for precedence.

 

If we have a more functional style with "<" and ">" how do we differentiate between the behaviour of execution a code block (eval), a patter and variable assignment. Currently we use ":" to indicate variable assignment. While we can also use "<" for assignment we need to keep : so that we can also do :=.

{code block} < Person()
Pattern() < Person()
var < Person()
var : Person()
var := Person()

 

However due to the need to

boolean code blocks

={code block} 
!{code block)

 

If the {....} block returns a value, then it's add to the tuple list - like 'from' is now. We may need a way to execute a code block, without either a boolean test or adding to the tuple

Person() < {someExpression()}

The above is the same as Person() from someExpression()

 

Inline Function Literals

3) introduce { } block structures as function literals to execute code, basically an eval node that always propagates, and supports multiline executions. Typically it'll just call a literal function { lit_func }. Ideally ; is optional.

$p : Person( name == "mark" ) { func_lit($p) } 

 

As ">" is implicit it's not necessary in this example. But it could be added:

$p : Person( name == "mark" ) >  { func_lit($p) } 

 

Failure Handling and Direction Flow

3) Allow !> and <! to direct join failures

{ fail_lit_f } <! Person() > { pass_lit_f } 
{ pass_lit_f } < Person() !> { fail_lit_f } 

 

Filters

4) We've discussed the concepts of "filters" taking over the role of 'over' and being more flexible. To differentiate a filter from an expr, I think we need a | symbol.

Alert() | window:time(30s) 
Alert() | distinct | window:length(100) 

 

Btw I feel we have a mismatch using square brackets for temperal operators "after [0s, 10s]", but standard parentheses for windows.

 

I feel this will still work with > and !>

Alert() | window:time(30s) > Location() 

 

 

4.1) Filters are pluggable, although due to the complexity of exposing "LeftTuple" we'll always classify it as a "subject to change" interface.

 

4.2) filters can work on individual patterns as above, or actually on network segments

( A() && B() ) | distinct 

 

4.3) Filters could be used to sort tuple sets, although this is probably only useful for queries, as the Agenda will resort those again. The following example would sort first by a city's name, and then by a person's age.

 

$c : City()
$p : Person() | filter:sort( $c.name, $p.age )

Cut (break)

5) 'cut' keyword, that works like prolog. This provides interesting work via 'or', where we'll make 'cut' work across logical branches. In essence 'cut' stops any further join attempts.

(or A() > {f1} > cut 
    B() > {f2} > cut 
    C() > {f3} > cut )

 

Because '>' can be implicit, we may be able to write this as below, but () would be needed I think...

(or (A() {f1} cut) 
    (B() {f2} cut) 
    (C() {f3} cut) ) 

 

Case

6) we need to introduce  'case' statement:

case( <expr>; 1...n cases ) 

 

case( Object() < entry-point "sensor"; 
      A() > {f1}; 
      B() > {f2}; 
      C() > {f3}; 

 

In effect the tuple returned by expr is then rematched against each case segment. Which is short for:

$o : Object() from entry-point "sensor" 
(or ((A() < $o) > {f1}) 
    ((B() < $o) > {f1}) 
    ((C() < $o) > {f1}) 

The above examples all use 'case' for alternative flow directions, we can also sue it for alternative value associatinos for the same flow branch. However there is still currently a descripentecy compard to above. The above uses 'case' statement has a head pattern, that provides the type for each case statement to match against. The below use case has no head statement. For now there is an empty ; to indicate there is no head pattern. Also notice I use direct variable assigment via "dx < 2" instead of "dx : Number() from 2". This uses type inference to ommit the Number() pattern, and we could use ":" or "<" to indicat ethat "2" flows into the vairable dx.

case(; PointWin(player.id == PlayerId.PlayerOne )
          dx < 2 );

          PointWin(player.id == PlayerId.PlayerTwo )
          dx < -2 );

          not PointWin( )
          dx < -2; )

 

The above can already be done via 'or', but again fair less readable. Notice I use semi colon for separation and 'and' is implicit, for compact reading and reduction of parenthesis.

 

( PointWin(player.id == PlayerId.PlayerOne ) and dx : Number() from 2 ) or 
( PointWin(player.id == PlayerId.PlayerTwo ) and dx : Number() from -2 ) or 
( not PointWin( ) and dx : Number() from -2 )     

 

Function literals and Rising/Falling edges

Something else to think about is the concepts of rising and  failing edges (match and unmatch) and how to execute on those.  Accumulates have the concept of 'reverse'. But if we are to allow literal functions blocks in the code, I suspect we should provide some mechanism for this.

 

Will execute on onMatch. Remember > is the pipe from the previous data source (pattern)

> { println( "hello mark" ); } 

 

Do we introduce an external "on" marker or hve a nested block:

> onMatch{ println( "hello mark" ) } // node will be ignored for retracts 
> onUnmatch{ println( "goodbye mark" ) } // node will be ignored for asserts

 

What about was true, still is true modifies?

>onRematch{ println("hello again") }

 

Another alternative is a separator, | is used else where,  so we'd need a different symbol

> { println( "hello mark" ) | println( "hello again" ); println( "goodbye mark" ) };

 

Alternatives the block can attach a call back (currently my favourite, as future ideas can just be sugar for this:

> { println( "hello mark"); onUnmarch = { println( "hello again" ); onUnmarch = { println( "goodbye mark" ); } }

 

what concept do we want for match unmatch

match/unmatch

rise/fall

true/false