forty years of libraries

Calling C libraries

You describe what a C function does. The compiler handles the mechanics. You never write a pointer.

The idea

C has forty years of battle-tested libraries: cryptography, compression, audio, databases, image processing. Vision can reach all of them. The mechanism is a face declaration — a description, in plain Vision, of what a C function does. No C code, no pointers, no length parameters, no out-parameters.

The declaration says: this function takes these things, gives back these things, fails when this happens. The compiler — which has already read the C header — maps the Vision description to the actual C signature and handles the conversion beneath the floor.

Single-call face

A function that takes input and returns output in one call uses a simple face declaration:

this program understands blake3

// Declare what the function does — in Vision terms, never C terms:
to hash some bytes is blake3_hash
    it takes some bytes
    it gives back some bytes

// Call it like any Vision verb:
to fingerprint, given the contents:
    the hash is hash the contents
    answer the hash

this program understands blake3 tells the compiler to find and bind the blake3 library. The declaration says: "there is a Vision verb called 'hash some bytes', and it corresponds to blake3_hash in C." From that point, you call it like any Vision verb.

Stateful: begin / feed / finish

Many C libraries use an init/update/finalize pattern — create a context, feed data to it repeatedly, then extract a result. Vision provides begin / feed / finish as the vocabulary for this pattern:

this program understands blake3

// A stateful C API with init/update/finalize:
to begin a blake3 hash giving a hasher is blake3_hasher_init
    it gives back a hasher

to feed some bytes to a hasher is blake3_hasher_update
    it takes a hasher and some bytes

to finish a hasher giving some bytes is blake3_hasher_finalize
    it takes a hasher
    it gives back some bytes

// Use it in plain Vision:
to fingerprint, given the contents:
    begin a blake3 hash giving the hasher
    feed the contents to the hasher
    finish the hasher giving the result
    answer the result

Three separate face declarations, one per C function. Then the call site reads like a sentence: begin a hash, feed data to it, finish it to get the result. The C context object (the hasher) is managed automatically — it lives as long as it is needed.

Failure from C

C functions signal failure through return codes, errno, or output parameters. The face declaration captures this with it fails when:

this program understands zlib

to compress some bytes is compress2
    it takes some bytes and a level
    it gives back some bytes
    it fails when the input is too large

to decompress some bytes is uncompress
    it takes some bytes
    it gives back some bytes
    it fails when the data is corrupt

At the call site, Vision generates the but if failure conditions from the declaration. You handle them with the standard failure vocabulary — stop, hand it on, give up.

The by hand: escape

Occasionally a C function is genuinely too unusual to describe with a face declaration — unusual calling conventions, macro-based APIs, functions that mutate their arguments in non-standard ways. For these cases only, Vision provides a by hand: block.

by hand: is a last resort, not a shortcut. It should not contain loops, control flow, or allocation. If you reach for it, stop and ask: can the C function be wrapped in a thin C shim that has a normal face?

A well-written Vision program that uses C libraries should have zero or near-zero by hand: blocks. The face declaration is the right tool for almost every real C function.