You can compose programs together with the + operator on programs or the more general compose operator. This has the effect of unioning programs together. You can also specify that some of the variables used in the programs you compose are to be considered the same (or shared), while the rest of the variables are local to the programs. Here is an example.
include standard.ccl program plant(a,b,x0,delta) := { needs u; x := x0; y := x; true : { x := x + delta * ( a * x + b * u ), y := x, print ( " x = ", x, "\n" ) }; }; program controller ( k ) := { needs y; u := 0.0; true : { u := - k * y }; }; program system ( x0, a, b ) := plant ( a, b, x0, 0.05 ) + controller ( 2 * a / b ) sharing u, y; program main() := system ( 3.141, 5.5, 1.1 );Here, a simple one-dimensional linear system is declared in plant, which takes some parameters. Then the program controller is defined. By themselves, they don't do much (the value of x in the plant just explodes). But combined, they do. The program system is composed of instances of the plant and controller programs. It takes the initial value of x and the parameters a and b and passes them to plant and passes
The needs y declaration in the controller program is used to specify that the variable
``needs'' to be initialized by some other program, in this case, by plant. A similar
situation holds for the needs u declaration in the plant program. Any variable declared
as ``needed'' should be included in a ``shared'' part of a composition for it to be visible to the
program that initializes it.
Programs may initialize the same shared variables if they do not use the needs delcarator. The order of composition is important! This is because ccli will execute the initializers in order of appearance. Thus, if program p initializes x to 1 and program q initializes x to 2, then program p()+q() sharing x initializes x to 2.
ccli infers the types of all variables appearing in a program and makes sure that if a variable is shared between two programs, it has the same type in each.
Warning: Be wary of accessing 'x for any variable that is shared between multiple programs. The variable x may be assigned multiple times elsewhere before execution returns to the current program, with possibly unintended results. That is, 'x does not refer to the value of x at the previous time step. It is only the variable's previous value. As a general rule, only use the prev operator on local variables.
To compose more than two programs, you could do, for example,
program main() := p(1) + p(2) sharing x + p(3) sharing x + p(4) sharing x;but you could accomplish the same thing with
program main() := compose i in {1,2,3,4} : p(i) sharing x;which is a bit more concise. The general form for the compose operator is
program q ( param1, param_2, ... ) := compose v in L : p ( expr ) sharing var1, var2, ...The variable
include list.ccl include standard.ccl program agent ( i ) := { print ( "Hello from agent ", i, "\n" ); // etc. }; program quit() := { true : { exit() } }; n := atoi ( ARGV[2] ); program main() := compose i in range n : agent(i) + quit();If this code is in a file is called agent.ccl, then executing
ccli agent.ccl 5results in
Hello from agent 0 Hello from agent 1 Hello from agent 2 Hello from agent 3 Hello from agent 4