Spanda’s type system supports general-purpose programming and autonomous-systems domains: physical units, spatial math, sensors, AI, agents, safety, digital twins, and distributed robotics.
| Type | Example |
|---|---|
Int |
let n: Int = 42; |
Float |
let x: Float = 0.5; |
Bool |
let ok: Bool = true; |
String |
let s: String = "rover"; |
Char |
let c: Char; |
Bytes |
let buf: Bytes; |
Null |
let empty: Null; |
Type annotations are optional when an initializer is present; the checker infers types from expressions.
let count: Int = 3;
let label: String = "spanda";
Collections and distributed types use angle-bracket generics:
let goals: Array<Goal>;
let map: Map<String, Int>;
let stream: Topic<LidarScan>;
let svc: Service<Command, Feedback>;
let nav: Action<Command, Feedback, Path>;
The compiler reports arity errors when generic parameters are missing or extra, e.g. Array
requires exactly one type argument.
Spanda supports type parameters on module functions and structs:
module std.collections;
export fn identity<T>(value: T) -> T {
return value;
}
struct Box<T> {
value: T;
}
Stable surface:
| Capability | Status |
|---|---|
Module / export fn type params |
Stable (inference from call args) |
struct Name<T> + Name<Int> { … } literals |
Stable |
Empty <>, duplicate params |
Rejected with clear parse errors |
Not in this Stable subset (still rejected / unsupported):
| Capability | Status |
|---|---|
T: Bound, where clauses |
Rejected — planned separately |
| Trait / enum / agent type params | Not supported |
| Full Hindley–Milner inference | Not supported — annotate when ambiguous |
See feature-status.md.
export trait PathPlanner {
fn plan(goal: Pose) -> Path;
}
import navigation.traits;
robot R {
agent Nav { … }
impl PathPlanner for Nav {
fn plan(goal: Pose) -> Path { … }
}
}
Traits and impl Trait for Agent resolve after imports are expanded. Prefer export trait in a
module so importers can impl it; private traits stay module-local. dyn Trait objects are
supported for typed trait-object values.
Prefer typed enum forms where available; bare idents and string literals remain accepted. The type checker rejects unknown values for:
| Seam | Accepted values |
|---|---|
ai_model { provider: … } |
AiProvider.mock / .openai / .anthropic / .onnx (also bare/"…") |
serialize / deserialize format |
SerializeFormat.json / .yaml / .binary (also bare/"…") |
| Codegen target (CLI / config) | CodegenTarget.native / .wasm / .esp32 |
Non-literal expressions (variables) remain runtime-checked. CLI spanda codegen --target accepts
only native, wasm, or esp32 (same closed set as CodegenTarget).
Unit-aware types prevent mixing incompatible dimensions:
let speed: Velocity = 1.5 m/s;
let distance: Distance = 2.0 m;
let timeout: Duration = 500 ms;
Invalid operations are rejected at compile time:
// ERROR: speed + distance — incompatible physical categories
let bad = speed + distance;
Supported unit types include Distance, Velocity, Acceleration, Angle, AngularVelocity,
Mass, Force, Power, Voltage, Current, Temperature, and Pressure.
| Category | Canonical | Also accepted |
|---|---|---|
| Distance | m |
mm, cm, km, ft, in |
| Duration | s |
ms, us, min, h |
| Velocity | m/s |
km/h, mph |
| Acceleration | m/s² |
g (standard gravity) |
| Angle | rad |
deg |
| Angular velocity | rad/s |
deg/s |
| Mass | kg |
gram, lb |
| Force | N |
kN |
| Power | W |
kW, MW |
| Voltage | V |
mV, kV |
| Current | A |
mA |
| Temperature | celsius |
fahrenheit, kelvin |
| Pressure | Pa |
kPa, bar, mbar, psi |
| Frequency | Hz |
kHz, MHz |
| Category | Type name | Canonical | Also accepted |
|---|---|---|---|
| Humidity | Humidity |
rh |
%RH |
| Illuminance | Illuminance |
lux |
lx |
| Luminance | Luminance |
cd/m² |
nit |
| Gas concentration | Concentration |
ppm |
ppb |
| Sound level | SoundLevel |
dB |
dBA |
| Magnetic field | MagneticField |
uT |
gauss |
| Rotational speed | RotationalSpeed |
rpm |
— |
| Torque | Torque |
N·m |
Nm |
| Energy | Energy |
J |
Wh, kWh |
| UV index | UvIndex |
uvi |
— |
| Acidity | Ph |
pH |
— |
| Conductivity | Conductivity |
uS/cm |
mS/cm, S/m |
| Particulate matter | ParticulateMatter |
ug/m3 |
µg/m³ |
| Turbidity | Turbidity |
NTU |
FNU |
| Salinity | Salinity |
ppt |
psu |
| Radiation | Radiation |
uSv/h |
mSv/h |
| Soil moisture | SoilMoisture |
%VWC |
vwc |
let humidity: Humidity = 65 %RH;
let ambient: Illuminance = 320 lux;
let co2: Concentration = 800 ppm;
let noise: SoundLevel = 42 dBA;
let uv: UvIndex = 6.5 uvi;
let acidity: Ph = 7.2 pH;
let ec: Conductivity = 850 uS/cm;
let pm25: ParticulateMatter = 12 ug/m3;
let turbidity: Turbidity = 4.5 NTU;
let salt: Salinity = 35 ppt;
let dose: Radiation = 0.12 uSv/h;
let soil: SoilMoisture = 42 %VWC;
Compatible units may be mixed in comparisons and addition (e.g. 500 ms + 0.5 s, 100 cm + 1 m).
Incompatible dimensions are rejected at compile time.
let timeout: Duration = 500 ms;
let started_at: Timestamp;
Namespace: import std.time;
Point2D, Point3D, Vector2D, Vector3D, Quaternion, Pose, Transform, Trajectory,
Path, Waypoint, MotionCommand, ControlSignal, PIDConfig.
Namespace: import std.spatial;
CameraFrame, Image, DepthImage, PointCloud, LidarScan, GpsFix, ImuData, AudioFrame.
Namespace: import std.sensors;
LLM, VisionModel, EmbeddingModel, Prompt, Completion, Embedding, Token, Context,
Memory, Plan, ReasoningTrace, Goal.
Namespace: import std.ai;
ai_model blocks accept config keys used by hardware verification:
ai_model Vision: VisionModel {
memory_required: 2 GB;
gpu_required: true;
}
| Config key | Verification |
|---|---|
memory_required |
Compared to target profile memory |
gpu_required |
Target must have GPU / gpu_tops |
Agent, Goal, Task, Skill, Capability, Intent, ActionProposal, SafeAction.
ActionProposal is untrusted output from AI planners. It must never reach actuators directly.
let proposal: ActionProposal = planner.reason(prompt: "go");
let action: SafeAction = safety.validate(proposal);
wheels.execute(action); // OK
wheels.execute(proposal); // COMPILE ERROR
wheels.drive(linear: proposal.linear, angular: proposal.angular); // COMPILE ERROR
The type checker rejects ActionProposal passed to actuator.execute(), and rejects
ActionProposal.linear / .angular (opaque UntrustedLinear / UntrustedAngular) as arguments to
drive() / follow(). Only SafeAction from safety.validate() may reach execute(). Literal
non-AI drive(linear: …, angular: …) remains valid and is envelope-clamped at runtime.
Compile time: AI output is typed as ActionProposal. Actuator execute() accepts only
SafeAction from safety.validate(ActionProposal). ActionProposal motion components cannot feed
DifferentialDrive.drive / follow (including via let bindings). Non-AI literal drive /
follow(path:) remain available as low-level APIs. Runtime (interpreter run/sim):
safety { max_speed = … } clamps linear velocity on drive, execute, and follow(path:) cruise
speed (default 0.5 m/s, reduced by max_speed / zone caps at the call pose and again each
simulator tick while following; also inside safety.validate); optional max_angular = … rad/s
clamps turn rate on drive / execute; stop_if, zones, and emergency stop still gate motion via
before_motion. Follow path: each simulator tick re-runs validate_action_proposal on the
follow cruise (same gate as safety.validate) and stops the trajectory on reject. Not claimed:
hard real-time deadlines are intent/monitoring on the interpreter path, not OS-level guarantees.
Command, Conversation, Speech, Gesture, Emotion, Feedback.
Namespace: import std.hri;
Risk, Hazard, SafetyConstraint, EmergencyStop, SafeAction.
Namespace: import std.safety;
Twin, SimulationState, Telemetry, Replay, Fault, Scenario.
Namespace: import std.twin;
Fault types for simulate_compatibility (verification, not runtime twin API): CameraFailure,
LidarFailure, ImuFailure, BatteryDegradation, NetworkOutage.
Declared in programs, not runtime values:
| Construct | Role |
|---|---|
hardware Profile { } |
Platform capability declaration |
deploy Robot to Target |
Deployment binding |
requires_hardware { } |
Minimum platform requirements |
requires_network { } |
Connectivity requirements |
budget { } |
Per-task resource limits |
mission { duration } |
Mission length for power estimation |
Verification output types (Rust/JSON): CompatibilityReport, CompatItem, CompatibilityMatrix.
See hardware-compatibility.md.
Topic<T>, Message<T>, Service<Request, Response>, Action<Request, Feedback, Result>,
Endpoint.
KnowledgeGraph, Belief, Observation, WorldModel, Policy, Reward, StateEstimate.
| Module | Domain |
|---|---|
std.time |
Time and duration |
std.units |
Physical units |
std.spatial |
Pose, path, transforms |
std.ai |
Models and reasoning |
std.robotics |
Agents, motion, capabilities |
std.sensors |
Perception data |
std.safety |
Constraints and safe actions |
std.twin |
Simulation and replay |
std.hri |
Human–robot interaction |
Import with import std.units; then annotate types normally (Distance, Velocity, …).
See examples/types/ for annotated programs covering each category.
spanda check examples/types/units.sd
spanda run examples/types/safety.sd