Skip to content

Protocols and Bindings

Protocols are the top-level executable units in Culsma.

Protocols

A protocol has a name and a statement body.

text
protocol FlowCytometryProtocol {
    let target = tube(label = "Target", capacity = 100uL);
}

Culsma also supports parameters and explicit returns:

text
protocol Prepare(sample, volume = 10uL) returns (output) {
    let output = tube(label = "Output");
    return output;
}

The general shape is:

text
protocol Name(optional_params) returns (optional_names) {
    statements...
}

Protocol names are case-sensitive identifiers.

Parameters

Protocol parameters are declared in the protocol header:

text
protocol Prepare(sample, volume = 10uL) {
    ...
}

Parameters may have defaults. When another protocol calls a parameterized protocol, named arguments bind to parameter names.

text
Module.Prepare(sample = source, volume = 5uL);

Missing, unknown, duplicate, or redeclared call arguments are plan-stage diagnostics because call-frame expansion happens while building the plan.

Parameters can also drive plan-static control surfaces. For example, a protocol parameter may set a repeat count, a schedule boundary, an environment duration, or a static if condition:

text
protocol DiluteBatch(cycles = 3, hold_time = 10min, run_cleanup = true) {
    let target = tube(label = "Target", capacity = 100uL);
    let feed = tube(label = "Feed", capacity = 100uL);

    repeat cycle in schedule(start = 1, end = cycles, step = 1) {
        target << [feed:1uL];
    }

    if run_cleanup {
        with env(thermal = 4C, duration = hold_time) {
            hold(target);
        }
    }
}

Local Bindings

let creates a new protocol-local binding.

text
let source = tube(label = "Source", capacity = 100uL);

Later statements in the same protocol can refer to source.

text
let target = tube(label = "Target", capacity = 100uL);
target << [source:5uL];

let is local to the protocol body. It does not define a global material namespace and does not automatically leak into called protocols.

Assignment

Assignment updates an existing local binding:

text
x = 10uL;

Assignment is intentionally narrow. It updates protocol-local values; it does not mutate container material state. Material movement is written with <<.

Assignment allows:

  • booleans
  • integers
  • text
  • quantities
  • restricted result-member paths such as data_ref.result.field

It does not allow assignment to container refs, group refs, general indexed targets, or arbitrary object fields.

Returns

A protocol can return an expression:

text
return output;

It can also use named return bindings:

text
return output = output_tube;

Returning a container returns a container reference payload. It does not collapse the result to a string name or a derived report row.

Returning a group returns a container_group_ref payload. Explicit groups and plate selector groups preserve member order:

text
protocol SelectedWells returns (wells) {
    let plate96 = plate(label = "QPCR96", format = "96well", carrier_id = "PlateA");
    return wells = plate96[A1:A2];
}

Cross-Protocol Calls

The clearer form for calling another protocol is:

text
Module.ProtocolName(arg = value);

The referenced protocol is expanded into concrete plan steps. Reference errors and cycles are reported by plan diagnostics.

Source Includes and Imports

Culsma source files may use top-level loading declarations:

text
include "path/to/file.culs";
import StdlibModule;

include loads source files. import resolves library modules. The example protocol examples stay single-file; multi-file workflows can be introduced separately.

Released under the Apache-2.0 license.