Programs of many files
However many files a program is written in, it is one world: one set of units, one verifier’s verdict, one service. Files are how people share the writing. Namespaces keep their names apart, and packages bring in code from another directory or another repository.
Several files, one program
Every command takes any mix of files and directories. vishy check a.vish b.vish joins the files in the order given; vishy check dir/ takes every .vish file in the directory, in name order. A flow in one file calls a unit in another, and a diagnostic names the file and the line it is in. The check that keeps this guide true runs one file at a time, so the programs below that span files are shown as runs from 30 Sept 2026, and the refusals that fit in one file are included as usual.
A namespace
namespace pricing;
enum Tier { basic, gold }
// the fee a tier pays on an amount: three percent, one for gold
fn fee(amount: money(2), tier: Tier) -> money(2) = if tier == Tier.gold then percent(amount, 1) else percent(amount, 3);
examples fee { (10000, Tier.basic) => 300; (10000, Tier.gold) => 100; }
row Customer { id: id, tier: Tier }
unit Tiers {
caps storage;
state customers: Customer[8];
contract set(customer: id, tier: Tier) {
case customer in customers => Changed: customers[customer].tier := tier;
else => Added: customers += [Customer { id: customer, tier }];
}
contract fee_for(customer: id, amount: money(2)) -> money(2) {
case customer in customers => Fee(fee(amount, customers[customer].tier));
else => Fee(fee(amount, Tier.basic));
}
}
flow set_tier(customer: id, tier: Tier) atomic { Tiers.set(customer, tier); }
flow quote(customer: id, amount: money(2)) atomic { Tiers.fee_for(customer, amount); }
scenario gold_pays_less {
set_tier(7, Tier.gold) => Added;
quote(7, 10000) => Fee(100);
quote(8, 10000) => Fee(300);
}
namespace pricing; at the top puts everything the file declares in the namespace pricing. Inside it, names are written bare, as in every program so far. A file’s namespace is the one its first line names, else the directory it was given in, so vishy check pricing/ shop/ makes two namespaces from two directories; a file given directly with no such line is in the root namespace, where every earlier chapter’s program lived.
scenario gold_pays_less
pricing__set_tier(7, gold) => Added
pricing__quote(7, 10000) => Fee(100)
pricing__quote(8, 10000) => Fee(300)
pricing::Tiers = pricing__Tiers { customers: [pricing__Customer { id: 7, tier: gold }] }
3 step(s), all as expected
verify: 2 examples, 3 scenario steps and 4000 sampled checks passed
The trace shows how the compiler spells a name inside the joined program: the namespace, two underscores, the name. That is why no name of yours may contain two underscores:
book/refusals/files-underscores.vish:1:5: error: `Price::Old`: names may not contain `__` (reserved)
row Price__Old { id: id, cents: money(2) }
^^^^^^^^^^
A namespace exports its rows, enums, events, functions, units, flows and effects. It does not export state or contracts, which belong to their unit, so there is no pub to write: the first removal already says that nothing outside a unit reads its state.
use
Outside its namespace a name has a full name, pricing::Tier, and the compiler prints it that way. To write it in another namespace, import it. A second file:
namespace shop;
use pricing::Tiers as Pricing;
use pricing::*;
row Order { id: id, total: money(2), fee: money(2) }
unit Orders {
state orders: Order[8];
contract place(order: id, total: money(2), fee: money(2)) => Placed: orders += [Order { id: order, total, fee }];
}
flow place(order: id, customer: id, total: money(2)) atomic { let f = Pricing.fee_for(customer, total); Orders.place(order, total, f); }
scenario gold_order { set_tier(7, Tier.gold) => Added; place(1, 7, 10000) => Placed; }
use pricing::Tiers as Pricing; brings in one name under an alias, use pricing::*; brings in every name the namespace exports, and use pricing::fee; would bring in one name as it is. The flow place in shop asks a unit of pricing for a fee and hands it to its own unit. Checked and verified together:
$ vishy check pricing.vish shop.vish
ok: 2 row(s), 0 event(s), 1 fn(s), 2 unit(s), 3 contract(s), 3 flow(s)
$ vishy verify pricing.vish shop.vish 7 1000
verify: 2 examples, 5 scenario steps and 6000 sampled checks passed
Three refusals keep the names honest. A namespace nobody gave the compiler is unknown; here is the shop’s import of pricing checked without the pricing file:
namespace shop;
use pricing::fee;
use pricing::Tier;
row Order { id: id, total: money(2), charged: money(2) }
unit Orders {
state orders: Order[8];
contract place(order: id, total: money(2), tier: Tier) => Placed: orders += [Order { id: order, total, charged: total + fee(total, tier) }];
}
book/refusals/files-unknown.vish:2:5: error: unknown namespace `pricing` (namespaces come from `namespace x;` or the directories given to vishy)
use pricing::fee;
^^^^^^^
A namespace does not import itself; its own names are already bare:
book/refusals/files-self.vish:2:1: error: a namespace does not import itself
use pricing::Tier;
^^^^^^^^^^^^^^^^^^
And a name already visible is not imported over. With shop declaring an enum Tier of its own and also writing use pricing::Tier;, the two-file check answered:
three/shop.vish:2:1: error: `Tier` is already visible here as `shop::Tier`; give the import an alias
use pricing::Tier;
^^^^^^^^^^^^^^^^^^
use pricing::Tier as PriceTier; checked. One name means one thing in a file, the same rule the chapter on outcomes gave for outcome names.
An interface and its parts
An interface with its parts is one program of several files, joined the same way: vishy check interface.vish parts/*.vish on the team-rooms interface and its two parts answered ok: 2 row(s), 0 event(s), 0 fn(s), 2 unit(s), 6 contract(s), 4 flow(s). The chapter on interfaces and parts says how they merge; the storage chapter shows the development door picking up a part saved later.
Packages
A directory with a package.vishy file is a package: its files are one namespace, named after the package unless the manifest names another with namespace = "…", and its dependencies come with it.
name = "shop"
version = "0.1.0"
dep pricing = path "../pricing"
dep billing = path "../billing"
dep pricing = path "../pricing" is another package on disk. dep rates = git "https://example.com/rates.git" tag "v2.0.1" is one in a repository, cloned at its tag into $VISHY_HOME/deps (by default ~/.vishy/deps). Dependencies are resolved all the way down, and a program has one version of each name. With shop needing pricing 1.0.0 and billing needing pricing 1.1.0, vishy check shop answered:
`pricing` is needed at 1.0.0 and at 1.1.0
There is no second copy to hide behind. The chapter on versions shows how vishy diff decides what the next version number of a package must be.
Try
In the shop file, delete the line use pricing::*; and check the two files again. The alias for Tiers still works, but the scenario’s first step is refused with unknown flow or view `set_tier` : a flow of another namespace is visible only when something imports it, and the check says which name was missing where.