Numerical
The workshop's bearing-only example estimates planar position and heading from linear velocity, angular velocity, and a bearing measurement to a known landmark. Its persistent state consists of a three-component mean and a 3×3 covariance matrix. Prediction advances the mean with the motion model and propagates covariance through its Jacobians. Correction compares the predicted and observed bearings, wraps the innovation with atan2(sin(r), cos(r)), and applies the Kalman gain. The covariance uses the Joseph update. The following listing is also the source used by the Rust embedding examples; the live fixed-camera extension is presented separately below.
Why an EKF? It is a representative robotics algorithm that combines matrix operations, nonlinear functions, persistent state, and successive sensor measurements. Here it also serves as a stress test of the current architecture: the same program exercises compilation, Rust embedding, checked reactive updates, and CPU/GPU execution. Earlier Mech implementations demonstrated the underlying reactive approach in the alpha-era LIVE 2019 work and the v0.1 HYTRADBOI 2022 presentation.[9][10] This case study extends that work to the current implementation; benchmarks and validation of additional algorithms, applications, and robot systems are planned.
Initialization. The imports, motion inputs, measurement covariance, and initial state establish the filter. The control vector u carries forward velocity, angular velocity, and a measurement-availability flag. Matrix shapes are inferred from their values.
Extended Kalman Filter
+> math/*+> logic/all--EKF step @compute
Δt f32 :=0.1u [f32] :=[Time update. The motion model predicts the next pose, and its Jacobians propagate the state and process-noise covariance.
Measurement update. An available bearing corrects the prediction. When the selected landmark is outside camera range, visible is zero and the gain is zero, giving a prediction-only update. Wrapping the angular innovation avoids a discontinuity at a full revolution; the Joseph form updates the covariance.
Checked publication. Integrity predicates validate the candidate before the new mean and covariance replace the accepted state.
Download the complete bearing-only ekf.mec. The Rust embedding examples below use this source; the interactive fixed-camera extension has its own listing and download.
The complete source includes initialization, prediction, correction, integrity constraints, and publication. := defines a binding, ~ marks assignable persistent state, and = assigns it. Matrix multiplication uses **, a trailing apostrophe transposes a matrix, and * applies broadcast multiplication. Types and dimensions are explicit where needed and inferred through the equations. The +> line imports numerical functions and the Boolean all reduction.
Alongside matrices, Mech provides sets, tables, tuples, maps, and records. Built-in set operators include union, intersection, difference, membership, and subset tests. Tables support row filtering, column selection, and relational joins, including inner, outer, semi, and anti joins. The linked reference pages describe their syntax and operations.
Ordinary Mech matrix storage and operations use Rust's nalgebra library. The accelerated fixed-shape kernel instead lowers the typed graph into specialized instructions or shaders. Alternative Rust numerical libraries could support other execution strategies without requiring a different mathematical notation.
The three named integrity constraints check finite candidate values, positive covariance diagonals, and symmetry of the raw Joseph update within an absolute-plus-relative tolerance. Linear indices [1 5 9] select the diagonal of the 3×3 covariance, and all reduces the comparisons to a single Boolean result. The stored candidate is the symmetric part of that update. Positive diagonal entries alone do not establish a positive-definite covariance; these checks do not constitute a complete proof of filter validity.
An earlier compact presentation of the archived kernel passed state, rejection, rollback, and recovery parity tests on five CPU execution paths, including JIT and AOT. This bearing-only listing includes a numerical correction prompted by longer browser runs, described in the Reactive section. It intentionally changes covariance handling. The timing and source-size measurements below retain their original, separately identified sources; they do not measure either this correction or the fixed-camera extension.1
Extending the Filter to Fixed Cameras
The interactive program adapts the repository's fixed-camera localization example. Four cameras occupy known positions in the field and supply noisy range and bearing observations of the robot. Bearing is measured from each camera in world coordinates, rather than relative to the robot's heading. One turn predicts motion once, then incorporates every enabled camera whose range contains the robot, in camera-index order. If none can observe it, the filter publishes only the prediction.
Initialization. The filter receives commanded motion and range-and-bearing observations from four fixed cameras. Each observation includes an availability flag; the state and covariance persist between turns.
Fixed-camera Extended Kalman Filter
+> math/*+> logic/all--Four-camera EKF step @compute
control [f32]:3,1 :=[Time update. A midpoint motion model predicts the next pose, and its Jacobians propagate state and process-noise covariance.
Both positive and negative edge crossings wrap into the field.
μ̄position:=μ̄raw[1..=2]+field-extent*ceil(-μ̄raw[1..=2]/field-extent)μ̄:=[Measurement update. Available cameras correct the prediction in sequence. Each correction wraps the bearing innovation and updates covariance in Joseph form; an unavailable camera contributes no correction.
Fixed cameras measure range and world-referenced bearing to the robot.
--Their observation contains no robot heading measurement.
--Align the nearest position representation when a boundary was crossed.
--Camera ranges remain ordinary distances within the visible field.
Checked publication. Integrity predicates validate the candidate before its mean and covariance replace the accepted state.
Download the complete live camera-ekf.mec. These blocks share the ekf namespace and compile together as the numerical program running in the Output pane.
Run the Example
The EKF runs in the console’s Output pane. Open it alongside the article or use the existing full-screen view to inspect the robot, covariance ellipse, and timing measurements.
The browser demonstration makes successive state updates visible. A pose marker represents the estimated position and heading, while an angled ellipse represents the position covariance. The ellipse uses the covariance's principal axes rather than an arbitrary drawing scale. A radius-two covariance ellipse in two dimensions is not a 95% probability region; it encloses approximately 86.5% under the Gaussian model.
Start with Run or advance One turn at a time. Click a fixed camera to enable or disable it, and adjust camera range to change which cameras can observe the robot. The forward and angular velocities control its commanded motion, which wraps at the field edges. Separate noise multipliers change the simulated motion and camera measurements. The filter receives measurements and commanded motion, not simulated truth; its process and measurement covariance models stay fixed as the noise changes. The drawing and telemetry show the first filter, while larger batches execute additional independent filters. CPU and GPU compile the same camera kernel where WebGPU is available. Changing the backend or batch size starts a new episode rather than migrating a running session between devices.
Inject an invalid observation to exercise rejection in the last filter of the batch. The accepted state stays visible and the behavior enters Fault; Reset starts a new episode. The displayed source is read-only, and the source links provide complete downloadable examples. The verification control separately checks CPU/GPU numerical agreement, rejection, rollback, and recovery, and reports when WebGPU is unavailable.
Both browser routes parse the displayed camera-ekf.mec source and construct the same fixed-shape numerical program. The CPU route executes its lowered instructions in the Mech interpreter, whose Rust implementation is compiled to WebAssembly. The GPU route generates WGSL and a binding manifest from that program; the browser host submits it through WebGPU and observes completion and integrity status before accepting the result. A separate resident Mech program computes camera measurements, scene geometry, covariance ellipses, and trails. JavaScript binds the controls, schedules execution, and passes the resulting drawing tables to the shared scene renderer.
Drawing with Mech Tables
The scene uses the same table-based drawing interface as the repository's EKF example. Columns describe properties of each primitive, and expressions connect those properties to the accepted filter state. These rows draw the fixed cameras and their range circles; lines and line strips describe observation rays, headings, trails, and the covariance ellipse. The supporting calculations are in the downloadable scene program rather than repeated throughout the article.
id
string
| x
f64
| y
f64
| radius
f64
| fill
*
| stroke
*
| stroke-width
f64
| opacity
f64
|
|---|---|---|---|---|---|---|---|
| "camera-range-1" | camera-screen[1,1] | camera-screen[1,2] | @input/camera-range | "none" | 0x687780 | 0.35 | camera-range-opacity[1] |
| "camera-range-2" | camera-screen[2,1] | camera-screen[2,2] | @input/camera-range | "none" | 0x687780 | 0.35 | camera-range-opacity[2] |
| "camera-range-3" | camera-screen[3,1] | camera-screen[3,2] | @input/camera-range | "none" | 0x687780 | 0.35 | camera-range-opacity[3] |
| "camera-range-4" | camera-screen[4,1] | camera-screen[4,2] | @input/camera-range | "none" | 0x687780 | 0.35 | camera-range-opacity[4] |
| "camera-1" | camera-screen[1,1] | camera-screen[1,2] | 2.3 | camera-colors[1] | 0x91cabc | 0.3 | 1.0 |
| "camera-2" | camera-screen[2,1] | camera-screen[2,2] | 2.3 | camera-colors[2] | 0x91cabc | 0.3 | 1.0 |
| "camera-3" | camera-screen[3,1] | camera-screen[3,2] | 2.3 | camera-colors[3] | 0x91cabc | 0.3 | 1.0 |
| "camera-4" | camera-screen[4,1] | camera-screen[4,2] | 2.3 | camera-colors[4] | 0x91cabc | 0.3 | 1.0 |
| "camera-hit-1" | camera-screen[1,1] | camera-screen[1,2] | 2.5 | "none" | "none" | 0.0 | 1.0 |
| "camera-hit-2" | camera-screen[2,1] | camera-screen[2,2] | 2.5 | "none" | "none" | 0.0 | 1.0 |
| "camera-hit-3" | camera-screen[3,1] | camera-screen[3,2] | 2.5 | "none" | "none" | 0.0 | 1.0 |
| "camera-hit-4" | camera-screen[4,1] | camera-screen[4,2] | 2.5 | "none" | "none" | 0.0 | 1.0 |
Download the complete camera and scene program. The table above is extracted from this file, which supplies the live drawing.
Browser results describe the browser and device running this page. The live summary uses the latest accepted turns after warmup, rather than independent process trials. Turn time includes input binding, execution, validation, synchronization, and one filter's readback; drawing and compilation are excluded. WebAssembly CPU and WebGPU have different timing boundaries from the native programs in the archived charts. A browser may translate WebGPU to Metal, Vulkan, or another platform API, which does not make its timings interchangeable with direct-Metal measurements.
Application Source Size
The source audit compares backend-neutral Mech with textbook-style Rust and a separate SIMD Rust implementation. It counts programmer-chosen names as one character per occurrence, excludes comments and nonliteral whitespace, and retains operators, types, numbers, and library names. The table reports normalized source characters, not lines or bytes. General libraries, compilers, and runtime internals are outside this application-source boundary.[11]
Application source category | Mech | Rust textbook-style | Rust SIMD |
|---|---|---|---|
Setup, constants and declarations | 340 | 486 | 561 |
EKF update and candidate handling | 356 | 1,024 | 1,119 |
Validation and component extraction | 383 | 263 | 396 |
Matrix helpers | 0 | 251 | 310 |
SIMD wrappers and adapters | 0 | 0 | 1,243 |
Batch dispatch, workers and rollback | 0 | 331 | 1,614 |
Total normalized characters | 1,079 | 2,355 | 5,243 |
The selected SIMD Rust application is almost five times as long as Mech. Its SIMD and batch support accounts for 2,857 characters, more than half of its total. A Rust library could provide some of that machinery, and different implementations could be shorter. In this case, the Mech application reuses the compiler's backend support instead of expressing a second set of equations and execution scaffolding. The counts describe the archived audit, not the compact document displayed above.
Embeddable
A Rust caller supplies source, identifies live inputs and exported state, and selects an execution backend. These examples use the bearing-only ekf.mec listing, not the extended camera kernel. Source can come from a file with include_str! or from a string embedded directly in mech::mech!. The macro constructs a kernel builder and preserves the source text. Compilation is explicit in .compile(...); the macro itself does not compile Mech during Rust compilation.
fn main() -> Result<(), Box<dyn std::error::Error>> {
use mech::kernel::Backend;
let source = include_str!("ekf.mec");
let kernel = mech::mech!(source)
.input("bearing", [-0.55; 4])
.export("μ")
.compile(Backend::Jit)?;
let mut ekf = kernel.start()?;
ekf.turn([("bearing", [-0.54; 4])])?;
let state = ekf.state("μ")?;
println!("{} filter instances", kernel.instances());
for (instance, pose) in state.chunks_exact(3).enumerate() {
println!(
"filter {instance}: x={:.6}, y={:.6}, heading={:.6}",
pose[0], pose[1], pose[2]
);
}
Ok(())
}Download the complete main.rs.
Compilation prepares a reusable kernel. start() initializes an independent session, and turn(...) binds an update packet and executes synchronously. The four bearing values in this example select four independent filters. A caller can submit new measurements as they arrive and borrow the published state by its source name. A rejected numerical update returns an error without replacing the accepted state. Invalid measurements must be replaced before retrying because a numerical rejection does not discard the newly bound inputs.
This interface is useful when Rust already provides the application's event loop, sensor access, or ROS integration. It executes a fixed-shape numerical kernel with checked publication; it does not implicitly construct the full reactive coordinator or deliver external effects. The larger runtime described next is a separate integration choice. Rust-native function implementations can also be registered in Mech's function catalog, allowing an application to expose its own operations to Mech.
Preparing and Loading an AOT Library
An application with a known kernel can compile it before deployment. The AOT producer below saves the native library and the interface information needed to initialize and address its inputs and state.
use mech::kernel::Backend;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let bundle = std::env::args_os()
.nth(1)
.unwrap_or_else(|| "ekf.bundle".into());
let kernel = mech::mech!(include_str!("ekf.mec"))
.input("bearing", [-0.55; 4])
.export("μ")
.compile(Backend::AotSimd)?;
kernel.save_bundle(&bundle)?;
println!(
"Saved AOT bundle: {}",
std::path::Path::new(&bundle).display()
);
Ok(())
}Download the complete build.rs.
A separate process loads that bundle and submits updates through the same session interface.
use mech::kernel::Kernel;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// SAFETY: Load only a trusted bundle produced for this host by the matching
// Mech build. Loading a native library can execute its initialization code.
let kernel = unsafe { Kernel::load_bundle("ekf.bundle")? };
let mut ekf = kernel.start()?;
ekf.turn([("bearing", [-0.54; 4])])?;
for (instance, pose) in ekf.state("μ")?.chunks_exact(3).enumerate() {
println!(
"filter {instance}: x={:.6}, y={:.6}, heading={:.6}",
pose[0], pose[1], pose[2]
);
}
Ok(())
}Download the complete load.rs.
The bundle describes named inputs, exported state, initial values, and the native layout. It is a deployment artifact, not a checkpoint of an already running filter. Loading a compatible bundle does not parse Mech source, invoke Cranelift, or run a linker.2
JIT and AOT share the relevant Cranelift numerical lowering. Their distinction is when native code is prepared and whether it is saved for later loading. Scalar and four-lane SIMD AOT are both implemented. Worker scheduling is an additional execution choice: the matched chart includes single-worker SIMD AOT and eight-worker SIMD JIT, so their difference must not be attributed to compilation time alone.
Dynamic Library Size, Throughput, and Memory
An independent checked-only dynamic-library experiment used one host thread, 10,000 filters, 200 timed turns, and seven fresh processes per library. The same minimal ABI loader called each library. Throughput and whole-process peak resident memory (RSS) are reported as median ± MAD.
Checked dynamic library | File size (bytes) | Throughput (M filter-turns/s) | Peak process RSS (MiB) |
|---|---|---|---|
Mech scalar AOT | 33,544 | 14.671 ± 0.008 | 2.69 ± 0.00 |
Rust optimized scalar | 50,016 | 21.121 ± 0.005 | 2.69 ± 0.00 |
Mech four-lane SIMD AOT | 33,864 | 34.863 ± 0.010 | 2.69 ± 0.00 |
This is a code-generation and packaging diagnostic, not the matched SIMD language comparison. In particular, the Rust library is a specialized scalar implementation, while the faster Mech row uses explicit cross-filter SIMD. The experiment does not measure the new ergonomic Rust wrapper.
All three rows have the same median whole-process peak RSS, 2,818,048 bytes, with MAD zero. Their ranges overlap, providing no evidence of a memory-use advantage. A later inspection of rebuilt or retained libraries traced most of the file-size difference to Mach-O segment padding and different linker/deployment settings. The smaller Mech file therefore does not show that it contains fewer numerical instructions or avoids a large linked Rust runtime.[12]
Reactive
Robot software commonly implements feedback loops over continuous data streams. In Mech, Rust host drivers supply updates from direct device interfaces or channels such as ROS. The runtime evaluates the affected computation, validates candidate state, and either publishes it or reports a rejection while retaining the previous state. This makes the acceptance boundary explicit in a program that is expected to process many successive inputs.
The build lane produces a Program Artifact containing the typed dataflow graph. A .mecb file is its serialized representation, so encoding and decoding connect the two representations. A .mcfg configuration supplies deployment choices such as hosts, capability grants, runtime policy, and compute targets. Activation checks those requirements against the available host services before creating the resident computation. Missing services or permissions can cause activation to fail before live execution starts.
During each turn, input changes identify affected computation. The resident graph calculates candidate state and proposed effects. Integrity predicates run before publication. A successful turn makes its accepted state available and records the result; after-commit output can deliver telemetry or a scene. A failed turn reports the rejection without publishing the candidate or its external effects. The diagram separates those outcomes rather than treating validation as a warning attached to an already published result.
A constraint caught rounding drift. While developing the earlier bearing-only demo, we exercised six combinations of CPU/GPU execution and filter batch size. The 65,536-filter CPU run rejected turn 360 after mirrored covariance entries differed by about 0.0001006, exceeding the original fixed tolerance of 0.0001; GPU runs reached the same failure at different turns. Rejection retained the accepted state and identified the failing constraint and filter. The Joseph update preserves symmetry in exact arithmetic, but separate f32 evaluations had accumulated different roundoff. We changed the Mech source to check each raw pair against 0.0001 + 0.000001 * abs(a) + 0.000001 * abs(b), then store the symmetric part as Σraw * 0.5 + (Σraw') * 0.5. Checking before projection exposes large asymmetries; projecting prevents small differences from accumulating into the next turn. Both browser backends compile that one source, so the correction required no separate CPU or GPU implementation. The corrected source completed 1,000 turns in each of the six configurations. The diagnostic record and reproducible tests document these bounded checks. They predate the fixed-camera extension and do not revise the native benchmark charts.
The EKF is one numerical component within this model. Its mean and covariance can drive a telemetry view and a robot scene from the same accepted update. The high-throughput kernel experiments below exercise double-buffered state and publication boundaries, but they do not time the complete host admission, recording, and output-delivery lifecycle. A separate retained f64, range-and-bearing EKF experiment measured a resident turn at approximately 315 ns median with zero steady-state allocations on its Apple M3 test machine.3 That result concerns a different kernel, machine, and execution boundary and is not plotted beside the parallel f32 measurements.
Heterogeneous
The typed numerical graph supports several execution targets. The following native bearing-only experiments examine cross-language CPU implementations, Mech's backend choices, and GPU execution. The fixed-camera browser example provides a separate way to explore CPU and GPU execution on the reader's machine; its workload and timings are not substituted for these retained measurements.
How the Measurements Were Collected
The native charts use an Apple M1 with four performance and four efficiency CPU cores, an eight-core GPU, 8 GiB of memory, and macOS 15.6.1. Each throughput chart processes 500,000 independent f32 filters for 40 timed turns. Each displayed mode has ten retained measurements from process trials. The CPU campaign's Mech trials measure both modes in the same process, while other CPU commands select one mode per process. The matched five-backend campaign uses a fresh process for each backend/mode case. The collectors interleave cases in a shuffled order, and no samples are removed as outliers. Values are medians with unscaled median absolute deviation (MAD), which describes observed variation rather than a confidence interval.
Checked execution evaluates the candidate-state predicates. Unchecked execution omits those predicates while retaining the specified state-publication mechanism. This does not refer to Rust's unsafe keyword or to a general removal of all runtime checks. The cross-language CPU experiment, same-source backend experiment, and Metal comparison have distinct execution boundaries and source identities; equal throughput units do not make them one experiment.
The repository retains raw process output, source and executable hashes, toolchain versions, and reproduction scripts. Native Rust uses LLVM 22.1.0 in the recorded nightly build, while Mech's native code uses Cranelift 0.131.3. The other compiler and package versions, including Julia, Mojo, Futhark/ISPC, Numba, Taichi, and Halide, are recorded with the individual campaigns.[13] Compilation and device setup are outside these steady-state throughput measurements. Their cost matters for short-lived applications but requires a separate experiment.
CPU Implementations
The cross-language CPU chart includes Mech, Rust, Mojo, Julia, Futhark, and a Numba kernel launched from Python. The NumPy/Numba path performs its numerical work in compiled code; it is not an interpreted Python or eager NumPy baseline. The comparison fixes the machine, precision, filter population, turn count, and eight-worker budget while using implementations suited to each system.
The closest comparison is between Mech and Rust. Both use four-wide packed SIMD and execute a fused 40-turn block with block-atomic rollback. Checked medians are 151.3 ± 2.6 million filter-turns/s for Mech and 149.9 ± 2.5 for Rust. Their small median difference is within the observed variation. Unchecked medians are 184.1 ± 8.1 and 170.5 ± 1.7, respectively. These results compare the selected implementations and their code generation. They do not establish that Rust cannot match the Mech result with a different implementation or optimization.
The remaining rows provide context rather than an exact fault-interface equivalence. Runtime setup, failure observation, and compiler transformations differ. Rust's measured interval includes worker creation and checkpoint work; Futhark's entry includes initialization and checksum work. Performance specialists may improve these implementations. The retained sources make those choices inspectable rather than attributing the entire difference to a language name.
One Mech Kernel, Five Execution Backends
The second chart holds the Mech source and publication boundary fixed. It compares interpretation of lowered numerical instructions, scalar JIT, single-worker SIMD AOT, eight-worker SIMD JIT, and direct Metal execution. Seven bindings are live inputs. Each process performs five untimed turns followed by 40 timed turns in the same resident session, without resetting the state between them. Inputs stay resident and unchanged during measurement. Every turn publishes separately, including a worker or device synchronization where required.
Checked medians range from about 0.70 million filter-turns/s for the numerical instruction interpreter to 420.9 for Metal. SIMD AOT reaches 34.6 on one worker and SIMD JIT reaches 121.1 on eight. These rows differ in interpretation, specialization, parallelism, and device execution, so the chart uses a logarithmic axis. The source equations require no backend-specific annotations to select these measured paths.
Checking reduces SIMD AOT throughput by approximately 6.6% and eight-worker SIMD JIT throughput by approximately 14.5% in this campaign. The Metal checked and unchecked medians differ by less than either MAD, so this measurement does not resolve a checking cost there. Both modes retain per-turn publication. Before collection, component-wise numerical preflights compared all backends with the scalar reference, and each checked backend rejected an injected NaN without changing the published batch state.
Native Metal Implementations
The six-system Metal comparison uses resident component-major f32 state, ping-pong publication, and a completed GPU submission after every turn. Rust supplies a host for a hand-written Metal Shading Language kernel, so its label is Rust + MSL rather than Rust alone. Mech generates its shader from the numerical graph; Julia uses Metal.jl, while Mojo, Taichi, and Halide use their respective native Metal paths. Futhark is included in the CPU comparison, not this Metal comparison.
Rust + MSL measures 425.2 ± 3.1 million checked filter-turns/s, Julia 410.4 ± 4.7, Mech 409.8 ± 3.0, and Mojo 406.0 ± 5.2. These retained results place generated Mech execution near the hand-written Metal control for this workload. They come from the earlier cross-system campaign; the 420.9 Mech result in the five-backend chart belongs to a separate collection and must not be substituted into this one.
Matching the GPU layout and host protocol materially changed earlier comparisons. The Julia and Mojo paths now use packed resident state and compact fault status instead of observing a large host-side result each turn. Taichi adds a compact cumulative fault count to the same publication protocol. Halide uses a resident per-lane fault plane because its measured interface does not expose the same device-wide compact status mechanism. Its checked result therefore includes that observation cost. Launch schedules and transcendental lowering also differ.4
CPU and GPU Execution from One Application Source
Mech, Taichi, and Halide additionally select CPU and Metal execution from one application source in their respective systems. Backend options and schedules change while the equations and publication contract are retained. In this archived comparison, Mech has the highest CPU and checked-Metal medians. Unchecked Metal places Taichi and Mech within 0.08%, far below either MAD. The useful result is that all three demonstrate this source reuse, with different measured execution costs; it is not evidence that Mech is universally faster on CPU or GPU.
Status and Future Work
This work presents a snapshot of Mech v0.4-beta. Many features are implemented and working, but further validation and hardening are needed. Research-hardware deployments have been limited. Over the next year we plan to test on a wider range of robot platforms, collect deployment feedback, and extend host integrations, standard-library coverage, and language features. The development plan targets a fuller v0.4 release in 2027 and a v1.0 release candidate in 2028; these are planned milestones rather than completed validation claims.
The EKF results motivate that work by connecting compact numerical source with usable Rust embedding, explicit validation boundaries, and several execution strategies. Future evaluations should include other numerical kernels, changing live inputs, complete robot workloads, and deployment costs beyond steady-state throughput. The source, raw measurements, and reproduction instructions are public. Contributions, independently optimized comparison implementations, and bug reports are welcome through mech-lang/mech.
More about Mech
The poster concentrates on embedded numerical kernels, but Mech is a functional language with user-defined types, pattern matching, and first-class state machines. The following examples extend the case study with facilities introduced in the version 0.3 article.[14] They run in this document's Rust interpreter compiled to WebAssembly. The numerical GPU backend supports a more restricted set of operations.
State Machines and Named Events
The behavior around the filter distinguishes paused operation, active patrol, and a latched fault. Modes and events are named atoms with declared types, so the host interface carries their meaning directly. Dispatch patterns match those values; a wildcard ignores an argument that does not affect a transition.
Robot behavior
mode:=:paused|:patrol|:faultevent:=:pause|:run|:rejected|:resetExample invocation: run from paused.
#Robot(:paused, :run)Download the complete behavior.mec.
The browser host supplies the current mode and an event to this Mech state machine. Its result determines whether JavaScript schedules another measurement update or stops the simulation. Reset takes precedence over a fault, while a run event cannot resume a faulted episode. The last accepted EKF state stays visible until reset. This behavior policy is separate from the kernel's rollback mechanism and can be changed independently.
Functions and Pattern Matching
A user-defined function can package a numerical expression without changing its matrix notation. This example wraps an angle and calls the function on a heading; adding a scalar to the heading vector demonstrates broadcasting. Further calls can be entered in the REPL.
Heading normalization
+> math/*Download the complete functions.mec.
Pattern matching can also select behavior from structured observations. Here a match arm binds the distance and checks its guard before choosing correction or prediction. A different distance or an occluded observation exercises the other branch.
Sensor decision
reading:=("visible",2.4)decision:=reading?Download the complete matching.mec.
Literate Documents
This article is itself a Mechdown document: prose explains the code, and executable blocks supply results alongside it. Download the complete .mec document to inspect the prose and expanded examples together. The live camera EKF blocks share a namespace and compile as one numerical kernel. The bearing-only reference listing has a separate namespace, while the language examples execute in the document REPL.
The Output pane presents the running EKF application. Its camera simulation and drawing tables are written in Mech, while the browser host supplies UI events and measured filter state. The article shows selected scene rows alongside the numerical kernel; the source link provides the complete supporting program. Mechdown also supports hidden executable blocks, so a document can present selected calculations without displaying every helper.