The reverse direction is the harder credibility test. Most languages that call C cannot be called from C without ceremony — a runtime to initialize, a foreign-function veneer, reference-counting rules to obey at every crossing. The example CCallsMica is a plain C program, compiled by cc, whose link line names three archives — your Mica library, Mica’s standard library, and the Dragon runtime floor beneath it — and that is the entire integration:

cc -std=c23 -o ccallsmica main.c ../build/Utilities/Utilities.a /usr/lib/mica-stdlib-arm64.a /usr/lib/libmicadragon-rt-arm64.a -lm

No Mica source in the build. No init function to call. The C program’s main is in charge from the first instruction.

The naming rule

A Mica export’s linkage name is the build target’s name, two underscores, and the exported name. The Utilities library builds a target called Utilities, so its exp function Fibonacci links as:

extern int64_t Utilities__Fibonacci(int32_t n);
extern double  Utilities__VectorMagnitude(double x, double y);
extern float   Utilities__Sigmoid(float x);

The header utilities_ffi.h in the example is exactly such declarations plus a few typedefs — written by hand once, deliberately, so the example shows the raw truth of the boundary with nothing generated in between. The types map as the C ABI already says they must: int64 is int64_t, float32 is float, bool is bool, a packed Mica record is a __attribute__((packed)) struct, and a Mica string arrives as a two-field descriptor — pointer and rune count:

typedef struct MicaStringView
{
    const mica_char_t *Data;
    uint64_t Length;
} MicaStringView;

The run

Ordinal contract surface
  pair const check          = 1
  wrapper pair state/level  = busy / 4

Packed aggregate contract surface
  payload                  = 5, 16, 4.25
  lane                     = (21,30) (22,40)
  lane total               = 70

Text contract surface
  row                      = zero |  | two
  wrapper                  = north | west / center / east | south

Summary
  - Called one Mica library directly from C without an adapter runtime
  - Demonstrated value, pointer, and const-pointer calls across the shared ABI
  - Covered classic scalars, ordinals, packed data, arrays, records, and descriptor-based strings

Note what the sections mirror: they are the same value shapes the Mica-calls-C example sends the other way, printing the same numbers. One ABI story, told from both ends, agreeing — that symmetry is the boundary’s real test, and both examples are built by CI on every push so it cannot silently rot.

Deeper than a demo

This direction is not a courtesy feature; it is load-bearing. The Dragon SDK’s C consumers drive the compiler’s whole backend through exactly this boundary, and the PL/0 compiler that certifies the SDK exists twice — once in C, once in Mica — precisely because a C program linking Mica archives is a first-class citizen. When your build system, your test harness, or your twenty-year-old application is C, Mica joins it, not the other way around.

And since 6.12.6, the sandwich composes: a Mica program can hand a C library a callback road back into its own exports — C calling Mica inside Mica calling C — which the debugging tutorial walks with a debugger attached.

What this does not do

  • Non-exported names do not exist. The archive publishes exp-marked declarations and nothing else — the same rule the Mica side lives by, enforced by the symbol table itself.
  • The header is on your honor. Like every extern declaration in C, a wrong prototype is your foot and your trigger. Keep the declarations in one header beside the archive, and change them only when the library’s contract changes.
  • The stdlib archive joins the link. Mica code needs its runtime and standard library; they arrive as one more .a on the line — no shared runtime daemon, no environment, just an archive.

Try it

git clone https://gitlab.com/mica-lang/mica-container.git
make -C mica-container/examples/CCallsMica run

Add extern int64_t Utilities__Fibonacci(int32_t); to any C project you own, link the two archives, and call it. That — genuinely — is the whole procedure.

Next

Mica calls Linux — the boundary at its most worn: system calls, and the errno convention the compiler lifts so your program never sees a -1.