vision
Install

Concept guide

Kinds, enums, and unions

Kinds give a value named fields. Enums choose one name from a fixed set. Tagged unions choose one shape, with the data that shape needs. Callable types let a verb itself become a checked value.

One program, four kinds of shape

This delivery router uses all four ideas together. Read it once for the outline: a delivery has fields, its priority is an enum, its destination is a tagged union, and publish accepts a formatting verb as data.

kinds-enums-unions.viscompiler checked
a priority is one of routine, urgent

kind delivery:
    title: text
    priority: priority
    attempts: whole

a destination is one of:
    a localqueue, with a lane
    a webhook, with an endpoint and a retrylimit

a presenter is a way to present, given a delivery called item, returns text

to compact(item: delivery), returns text:
    answer "{item.priority}: {item.title} ({item.attempts} attempts)"

to publish(item: delivery, render: presenter):
    say render(item)

to route(place: destination), returns text:
    when place is a localqueue:
        answer "queue {place.lane}"
    when place is a webhook:
        answer "webhook {place.endpoint}, retry {place.retrylimit}"

to begin:
    the shipment is a delivery:
        title is "release build"
        priority is urgent
        attempts is 2

    publish(shipment, compact)

    the fallback is a localqueue
    fallback.lane is "overnight"
    say route(fallback)

    the primary is a webhook
    primary.endpoint is "/deploy"
    primary.retrylimit is 3
    say route(primary)
a priority is one of routine, urgent

kind delivery:
    title: text
    priority: priority
    attempts: whole

a destination is one of:
    a localqueue, with a lane
    a webhook, with an endpoint and a retrylimit

a presenter is a way to present, given a delivery called item, returns text

to compact(item: delivery), returns text:
    answer "{item.priority}: {item.title} ({item.attempts} attempts)"

to publish(item: delivery, render: presenter):
    say render(item)

to route(place: destination), returns text:
    when place is a localqueue:
        answer "queue {place.lane}"
    when place is a webhook:
        answer "webhook {place.endpoint}, retry {place.retrylimit}"

to begin:
    the shipment is a delivery:
        title is "release build"
        priority is urgent
        attempts is 2

    publish(shipment, compact)

    the fallback is a localqueue
    fallback.lane is "overnight"
    say route(fallback)

    the primary is a webhook
    primary.endpoint is "/deploy"
    primary.retrylimit is 3
    say route(primary)
The build gate compiles and runs this complete program, then checks all three output lines exactly.

Kinds describe what belongs together

kind delivery: declares one described thing with three fields. Each field has a name and a type. The declaration creates the shape; the indented block under the shipment is a delivery:creates a value and fills its fields by name.

Read a field with the dot form, such as item.title, or with the prose form, such as the title of the item. The formatter uses dots because several reads sit inside one piece of text. Both forms refer to the same declared field.

Enums name a closed set of choices

a priority is one of routine, urgent declares two values of the same enum type. A delivery cannot hold an arbitrary word in its priority field. It holds one of those members, and the member renders by name when placed in text. That is why the first output line begins with urgent.

Enum comparisons use ordinary when conditions. The compiler does not apply tagged-union exhaustiveness checking to an enum comparison chain. Use an else: when the remaining members share one behavior, or name the members you mean to handle.

Tagged unions carry the data for one case

A colon after is one of: opens a tagged union. Here, a destination is either a localqueue with a lane, or a webhook with an endpoint and a retry limit. Each value carries one active tag and only that variant's payload.

The program constructs each variant by name, then fills its payload fields. Current Vision infers those payload types from their writes, solane and endpoint become text whileretrylimit becomes a whole number.

A parameter typed as destination could hold either variant.route narrows it with when place is a localqueue: before readingplace.lane. Reading a payload before that check is a compile error: the compiler cannot prove the field is present. A chain of union tag checks must cover every declared variant, or end in else:.

Callable types put a shape around a verb

presenter is the type of a verb that takes one delivery and returns text. The parameter names in the callable declaration explain the positions; their declared types are what the compiler checks.

publish(shipment, compact) passes compact as a value. Inside publish, render(item) calls whichever matching verb arrived. The bound verb must match the callable's parameter types, return type, world-touch marker, mutation shape, and failure set. This tutorial uses the plain, infallible case; the precise fallible and mutating forms belong in the language reference.

Add one destination and one presentation

Add a deadletter, with a reason todestination. The compiler will point out thatroute no longer covers every variant. Add the matching branch, construct a dead-letter destination in begin, and make its line read dead letter: expired.

Then write a second presenter that returns onlyitem.title, and pass it to publish. The call site changes; publish does not. If you want to revisit the construction block first, see Reading a Vision program. The later reference will cover field inference, shared enum names, and every callable shape in detail.