Composite operators

A composite operator is implemented in the Streams Processing Language (SPL) and encapsulates a subgraph of a data flow graph that can be parameterized to make it reusable in multiple stream applications.

Besides structuring and reuse advantages, composite operators help with visualizing both static and dynamic views of programs. Composite operators serve as a foundation for higher-level streams programming, for example, with ontology-based planning. The syntax for composite operators separates the head from the body to make it easy to see port connections, and splits the body into multiple clauses, to make it easier to read and enable folding editors. The head lists input and output ports, and the body specifies clauses for formal parameters, types, graph, and config. The syntax is:
compositeDef ::= compositeHead compositeBody

The head of a composite operator definition names the operator and lists its ports. For example:

composite M (output K, L; input G, H) { 
  graph stream<int32 x> K = Functor(G) { } 
        stream<int32 x> L = Functor(H) { } 
}

The composite operator M has two output ports, K and L, and two input ports, G and H. The syntax is:

compositeHead  ::= ‘public'?composite' ID (‘(' compositeInOut+; ‘)')?
compositeInOut ::= (‘input'|‘output') (streamType? ID)+,
streamType     ::= ‘stream' ‘<' tupleBody ‘>'

Public composite operators (with the public modifier) can be used from any namespace, whereas private composite operators (without public modifier) are only visible in the same namespace that defines them.

The body of a composite operator definition specifies what kind of parameters it accepts, and contains a stream graph. Besides these two clauses, (param and graph), the body can also contain a type clause with type definitions and a config clause with compiler hints and directives. All four clauses are optional. Here is the syntax:

compositeBody ::= ‘{'
                ( ‘param' compositeFormal+ )? # see Operator parameters
                ( ‘type' compositeTypeDef+ )? # see Type definitions
                ( ‘graph' opInvoke+ )?        # see Graph clause
                ( ‘config' configuration+ )?  # see Config clause}'
Tip: Use composite operators whenever you write a large program, because it becomes easier to understand when you break it down into smaller subgraphs. Also, make your composite operators reusable, and put them into libraries, so they can be invoked in different programs. Finally, you can use parameterizable composite operators to implement distributed computing patterns.