next up previous
Next: Standard Libraries Up: Programs Previous: Variable Memory


Program Composition

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 $2a/b$ to controller (the gain in the control law). Most importantly, it says the the variables u (the control input to the plant) and y are shared - that is, assignments in either the plant part or the controller part of system will be to the same memory location. Meanwhile, the variable x remains local to plant and invisible to other programs.

The needs y declaration in the controller program is used to specify that the variable $y$ ``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 $i$ ranges over the values in the list L and may appear free in expr. It is very important to note that in the present version of ccli, the list L is evaluated in the global scope and can not refer to the parameters param1, param2, ... in the parameter list of $q$. So you can not say program q(n) := compose i in range n : p(i); because $n$ is not defined in the global scope (in this example anyway). The range function is defined in list.ccl. It takes an integer n and returns { 0, ..., n-1 }. Here's a more complete example that you could extend to do a multi-agent simulation.
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 5
results in
Hello from agent 0
Hello from agent 1
Hello from agent 2
Hello from agent 3
Hello from agent 4


next up previous
Next: Standard Libraries Up: Programs Previous: Variable Memory
Eric Klavins 2003-12-03