Summary#
A namespace groups a file's types under a common name, so two parts of an app can each have an Order
without colliding. Write it once, at the top of the file, terminated by a semicolon:
namespace Shop.Catalog;It is optional. A file with no namespace declaration puts its types in the global namespace — which is
exactly what a single-file app wants, and what every example in this reference assumes.
Signature#
namespace Shop; // this file's types are Shop.*
entity Order { … } // → Shop.OrderDescription#
What it does to a name#
A type declared in a namespace gets that namespace as a prefix. namespace Shop; followed by entity Order
declares Shop.Order — that is the type's real, full name, the one you use to refer to it from elsewhere.
Inside the file that declares it, you just write Order.
Two forms — file-scoped and braced#
Osy# supports both C# forms:
namespace Shop; // file-scoped — opens the whole file (preferred)
namespace Shop { entity Order { … } } // braced — opens just its block
namespace Billing { entity Order { … } } // several braced namespaces may share one file- File-scoped (
namespace X;) opens the whole file: it must come before every other declaration, and a file may declare at most one. Prefer it for a file that is all one namespace. - Braced (
namespace X { … }) opens just its block, so a file may hold several and they may nest (namespace Shop { namespace Catalog { … } }puts a type inShop.Catalog). This is what lets C# code paste in verbatim.
A file uses one form or the other, never both — mixing a file-scoped namespace X; with a braced
namespace Y { … } in the same file is an error (as in C#).
How a name resolves#
When you write a bare name, it is looked for in this order — the first match wins, and no later step can make it ambiguous:
- The namespace you're in, then each enclosing one, working outward. Inside
namespace Shop.Catalog;that meansShop.Catalog, thenShop, then the global namespace. - The namespaces you imported with a
usingdeclaration — a package's, or one your own app declares. Ausingis PER FILE, and it imports that namespace's own types, never a child's:using Shop;does not bring inShop.Orders.Order(writeusing Shop.Orders;). If two imports offer the same name, that is an error that names both — qualify the reference to say which you mean. - The
Osyrincore, the platform's own namespace, always available without importing anything.
Because step 1 comes first, a type you declared always wins over one you imported. To reach the imported one anyway, write its full name.
The same order holds for every kind of name — a type, a component, a control and a function alike. A component of your own named like one in the UI kit is the one your namespace's pages get:
namespace Acme.Lighting;
using Osysharp.Ui;
[Composable] component Badge(string text) { render { Text(text); } }
[Route("/")] component Home() {
render { Badge(text: "on"); } // Acme.Lighting.Badge — nearer than Osysharp.Ui.Badge
}A namespace that encloses neither of two same-named declarations is still ambiguous there, and the error names both full spellings.
Two packages' functions of one name are told apart by their arguments, as C# picks an overload: a call whose
arguments only ONE of them can take is that one, bare. Only a call that both could take — the same parameter list —
has to be qualified, and the error says so naming both. A qualified call works anywhere a call does, and so does a
using alias for the namespace:
using Rota.Kit; // SignUp(Shift shift, string who)
using Accounts.Kit; // SignUp(string email, string password)
using R = Rota.Kit;
string Join(Shift s) => SignUp(s, "ada"); // Rota.Kit's — only it takes a Shift
string Register() => SignUp("[email protected]", "hunter22"); // Accounts.Kit's
string Drop() => Rota.Kit.Remove(1) + R.Remove(2); // both kits have Remove(int): qualify, or aliasA package's own call looks only among the packages it imports. Code in a package resolves a bare call against
that package and the ones its own package.osy names with use — never against everything the app happens to use
beside it. So a kit that calls NameOf(person) from the one kit it depends on keeps compiling when an app also uses
an unrelated kit with a NameOf of its own. For the app's own code the packages are the ones its app.osy names.
osy kits check warns a kit's author when the kit exports a function with the same name and parameters as a kit
that is already published (KIT_NAME_PUBLISHED), since every app using both would have to qualify it. The thirteen built-in type names (Markdown, Json, DateTime, …) follow the same order: a
declaration in an enclosing namespace wins, and everywhere else the bare name is the built-in — see
[[types-name-shadowing#built-ins]].
The Osyrin namespace is the platform's#
Everything the platform ships lives under Osyrin. Its core types need no import. Its optional capabilities are
sub-namespaces — Osysharp.Memory, Osysharp.Storage, Osysharp.Ui, … — that you first depend on with a
use in your app { } manifest, then import with a using in each file that references their
names. You cannot declare a type in Osyrin yourself.
Reaching another namespace#
A sibling namespace's types are not visible bare — qualify them, exactly as in C#:
namespace Billing;
int Count() {
var orders = Shop.Order.Where(o => o.Code == "x").ToList(); // qualified
return orders.Count;
}Declaration names never contain a dot#
entity Shop.Order is an error. A dot in a declared name is how you'd spell a namespace, not how you declare
one — use namespace Shop; at the top of the file.
Examples#
using Osysharp.Markdown;
// catalog.osy
namespace Shop;
entity Order {
[Required] string Code;
decimal Total;
}
int OpenOrders() {
// `Order` resolves to Shop.Order — the namespace this file is written in.
return Order.Where(o => o.Total > 0).ToList().Count;
}using Osysharp.Markdown;
// billing.osy — a different namespace, so Shop.Order must be qualified.
namespace Billing;
entity Invoice {
Shop.Order Source;
decimal Amount;
}Step 3 applies in every position a type name can appear, including a property's type — a core type needs no import there either:
using Osysharp.Markdown;
namespace Filing;
entity Attachment {
// `MarkdownDocument` is `Osysharp.Markdown.MarkdownDocument`, found at step 3. Nothing is imported and nothing is qualified.
MarkdownDocument Body;
}A using of a namespace your own app declares, then its types by their bare names:
using Shop.Orders;
namespace Shop.Orders {
public class Order { public string Code = ""; }
}
// This part of the file is in the global namespace; `Order` is found through the `using` above.
string CodeOf(Order order) {
return order.Code;
}See also#
- use —
usedeclares the capability dependency in the manifest;usingimports its names in a file. - type visibility (public / internal) —
publicandinternaldecide which of a namespace's types others can reach. - Osysharp.Ui (the UI kit) —
usingbrings a kit's public types into scope under their bare names.