vision
Install

Concept guide

Safe by design: danger, failure, and memory

Vision puts risky work in the source where you can see it. World touches carry a marker, failures stay beside the operation, and returned data has a clear owner. Concurrency and C interop keep their boundaries visible too.

Mark contact with the world

Vision checks >> in both directions. File access, networking, clocks, randomness, and other tracked world touches need the marker. A calm operation rejects it. The rule follows calls as well, so a verb that reaches the world through another verb must carry >> on its declaration and call sites.

!! is narrower. It marks a step that cannot be undone, such as removing a file, renaming over one, stopping a process, or sending a signal. Recoverable world touches reject !!, which keeps the mark useful instead of letting it become decoration.

scratch.viscompiler checked
>> to begin:
    >> !! remove the file at "scratch.txt"
        | missing: stop
        | unwritable: stop
>> to begin:
    >> !! remove the file at "scratch.txt"
        | missing: stop
        | unwritable: stop
Removing a file touches the filesystem and cannot be undone, so the line carries both markers. A missing file is handled beside the operation.

How Vision thinks introduces the world-touch boundary from the calm side. This page follows it into the places where work can fail or cross a runtime boundary.

Keep failure beside the work

Checked Vision operations cannot quietly drop a failure. The file read below covers missing and unreadable; converting the bytes to text separately covers badtext. Each clause says what the program should do before execution can continue.

poem.viscompiler checked
>> to begin:
    >> read the file at "poem.txt" returning the poem
        | missing: say "no poem yet" and stop
        | unreadable: say "the poem cannot be read" and stop
    the poem decoded as text returning the readable
        | badtext: say "the poem is not valid text" and stop
    say the readable
>> to begin:
    >> read the file at "poem.txt" returning the poem
        | missing: say "no poem yet" and stop
        | unreadable: say "the poem cannot be read" and stop
    the poem decoded as text returning the readable
        | badtext: say "the poem is not valid text" and stop
    say the readable
This complete program handles every failure reported by the read and decode operations.

This guide uses stop for the shortest path. A deeper caller can also receive a failure handed on by a helper verb. The Effects and failure reference will cover every failure tag and resolution form.

Keep secrets distinct from text

A field declared secret is not ordinary text. Vision keeps that type through assignments and blocks direct output of the protected value. Reveal and wipe are deliberate operations, so code does not leak a passphrase through an everyday say by accident.

strongbox.viscompiler checked
kind strongbox:
    passphrase is a secret

to begin:
    the box is a strongbox
    box.passphrase is "hunter2"
    say "the box is sealed"
kind strongbox:
    passphrase is a secret

to begin:
    the box is a strongbox
    box.passphrase is "hunter2"
    say "the box is sealed"
The program can report that the box is sealed without printing its secret field.

Return values to a clear owner

Most Vision code says nothing about memory. The compiler records heap-owning values, frees local data when its scope ends, and cleans up caller-owned return values when they are ignored, replaced, or leave the caller's scope. There is no garbage collector in that path.

The compiler treats a heap-owning result as belonging to its caller. If a verb returns an owner it already made, ownership transfers. If it returns borrowed data—a text parameter, a field, or an item read from a list—the compiler clones that value at the return boundary. The caller receives an independent owner either way.

safe-ownership.viscompiler checked
to echo(source: text), returns text:
    answer source

to begin:
    the original is "caller-owned"
    the returned is echo(original)
    say "original: {the original}"
    say "returned: {the returned}"
to echo(source: text), returns text:
    answer source

to begin:
    the original is "caller-owned"
    the returned is echo(original)
    say "original: {the original}"
    say "returned: {the returned}"
echo returns a borrowed text parameter. The compiler clones it at the boundary, and the build checks both output lines exactly.

An unusual escape can still outlive the region that made it, such as an owned value stored into a longer-lived field. When the compiler cannot prove that lifetime, it requires a visible memory: block; it does not guess or switch to hidden reference counting. The precise escape rules belong in the Host and C interop reference.

Join concurrent work before moving on

all at once: opens a bounded concurrent region. Each ask a strand called block runs in its own worker, and the closing edge is a join: code after the block does not run until every strand has finished. In the current compiler, the block carries >> and is classified as a world touch.

A channel carries typed values between those workers. It closes automatically after its last sending strand exits, and for each item received from drains the remaining values before ending. There is no manual close step. Sending and receiving are in-process operations and stay unmarked; the visible >> belongs to the enclosing all at once: region.

safe-concurrency.viscompiler checked
>> to begin:
    the work is a channel of whole number, with room for 2
    >> all at once:
        ask a strand called producer:
            send 2 to the work
            send 5 to the work
            send 7 to the work
        ask a strand called consumer, returning the total:
            the tally is 0
            for each item received from the work:
                the tally is the tally plus the item
            answer the tally
    say "joined total: {the total}"
>> to begin:
    the work is a channel of whole number, with room for 2
    >> all at once:
        ask a strand called producer:
            send 2 to the work
            send 5 to the work
            send 7 to the work
        ask a strand called consumer, returning the total:
            the tally is 0
            for each item received from the work:
                the tally is the tally plus the item
            answer the tally
    say "joined total: {the total}"
One strand sends three whole numbers. A second drains the channel and answers 14; the parent reads that result only after the join.

Strand bodies are isolated. They may use values created inside the strand and channels captured from the outer scope, but they cannot read other outer local values. A result-bearing strand must answer on every path, and its result must be a whole, decimal, yes-or-no, byte, or text. Nested concurrency inside a strand is not supported in the current surface. Those constraints keep worker state explicit.

Know which boundary you are crossing

by hand: is the direct escape hatch. Its indented body is raw C, preserved as a sealed section and emitted into the surrounding C function. Vision does not infer the C code's effects, failures, or lifetime behavior. Once a program enters that section, those checks are the author's responsibility.

safe-interop.viscompiler checked
to read host counter:
    the counter is 0
    by hand:
        counter = 42;
    answer counter

to begin:
    say "host counter: {read host counter}"
to read host counter:
    the counter is 0
    by hand:
        counter = 42;
    answer counter

to begin:
    say "host counter: {read host counter}"
The sealed C section writes one local generated-C variable. The exact native output is checked during the website build.

The other boundary forms do different jobs. this program understands zlib asks the compiler to inspect an installed C library's declarations, then emits the header and build link directive for that library. this program exposes greet to the host marks a Vision verb for the generated C ABI in library mode. A host that receives heap-owned text from an exposed verb owns that result and must release it.

this program links (settings) does not bind C at all. It brings declarations from a peer settings.vis file into the program. this program links (settings as config) keeps them under a config. namespace. The Host and C interop reference will cover supported ABI shapes, library discovery, and linked-file name rules without turning this guide into an edge-case table.

Try one safe change and one unsafe one

In the concurrency example, send one more number and predict the new total before running it. Then try to read an outer local value from the producer strand. The compiler rejects the second change because that local is not captured into the worker.

Finally, change echo so it returns a new interpolation instead of its parameter. The output stays the same, but the ownership path changes: the new text transfers directly instead of being cloned from a borrow. The next concept guide, Generics and monomorphization, will show how Vision specializes reusable verbs without giving up these checks.