Capy-Bearer Documentation

10. Units and components

Each Capy source file is an independent unit. A unit can expose functions and type metadata to other units. It can also render named components.

Export a function

#exports lists the functions and types that another unit can inspect. A callable (dval) dval function can also be called through a module handle:

#exports echo

function echo(input : dval) dval {
    -> input
}

function RENDER(request : dval) {
    var result := echo({message: "ready"})
    print(result.message, "\n")
}

Output

ready

A module-callable function has the exact signature (dval) dval. Private helper functions do not appear in #exports.

Call another unit

A separate service unit can expose a data function:

#exports version

function version(input : dval) dval {
    -> {name: "Capy"}
}

Load and call it from a neighboring page:

function RENDER(request : dval) {
    var service := unit_load("/doc/examples/call_target.capy")
    var version := service.default_input()
    print(string(version), "\n")
}

unit_load returns a request-local module handle. Module member calls are dynamic. service.default_input() calls the exported function named default_input on that loaded unit. Module calls copy input and result dval values as BRRB. unit_call(path, name, input) performs the same operation without keeping a handle.

Relative paths resolve from the calling unit. Use an absolute site path when the target must not depend on the caller directory.

Module handles

A module handle is a verified request-local capability. It can pass through typed locals, parameters, and results during that request. It cannot enter a dval, aggregate, closure, condition, or serialized boundary.

Static type imports

Use #import "path" as name when the compiler must see exported Capy types from another unit. For example, #import "/doc/examples/call_target.capy" as example makes example.VersionInfo valid in type positions.

A static import reads type metadata at compile time. It does not create a runtime module handle. Use unit_load() when the target unit must be selected at runtime.

Do not retain a module handle for another request. A later request can load a newer valid source generation.

Components

A component writes reusable response content through output functions such as print. A named component uses a handler name:

function COMPONENT:CARD(request : dval) {
    var props := request.props
    print(<><article><?= string(props.title, {fallback: "Untitled"}) ?></article></>)
}

Render it into the response:

function RENDER(request : dval) {
    component_render("/doc/examples/card.capy:CARD", {title: "News"})
}

Use component() when the caller needs the component output as a string. Bearer copies props into the component request context and restores the earlier props after the call.

Boundary rules

Bearer copies dval values at module, component, task, export, codec, request, and serialization boundaries. A target cannot mutate the caller's value through a boundary. The dynamic-values guide defines the identity rule.

Missing units, missing exports, and incompatible exported signatures are errors.

Choose a boundary

Use a component when the result is markup for the current page. Use an exported function when the result is data. Use a direct helper function only inside one source file.

A module loaded with unit_load() lives only for the current request. Store data in a session, a file, a database, or a task result when it must outlive the request.

Keep component props narrow. Pass only the values the component needs. This makes examples, tests, and future replacement easier.