Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Types

Optional and required members

string Title; // required · string? Note; // optional

A member is required or optional by how you spell its type. A bare value type with a natural zero reads that zero; a bare value-shaped type with no natural zero (string, enum, date, id) is REQUIRED — you must give it a value. Add `?` to make any member optional (it reads back null). Entity references are the one exception: a bare reference is optional, and you write `[Required]` to demand it.

stable1 example compiled by CItypesentityauthoring

Summary#

Whether a member is required or optional is carried by its type spelling — there is no separate keyword to remember. A bare member is non-nullable; the ? suffix makes it optional (it reads back null). What "bare" means depends on whether the type has a natural zero:

  • A value type with a natural zeroint, long, double, decimal, bool, TimeSpan — reads that zero when left unset. int Count; reads back 0, exactly as a C# field would.
  • A value-shaped type with no natural zerostring, any enum, DateTime/DateOnly/TimeOnly, Guid, Json — is required: the platform invents no value for it, so you must supply one before the row is saved.
  • An entity reference is the exception: a bare reference is optional. Write [Required] to demand it.
  • A Markdown member is also optional, for a different reason: it holds a document stored as its own sections, so a document nobody has written simply is not there and there is nothing to supply. See Markdown.

Signature#

entity Ticket {
  string Title;                 // REQUIRED  — a bare string has no natural zero, so you must set it
  string? Note;                 // optional  — reads back null when unset
  int Priority;                 // reads back 0 (int has a natural zero)
  Status State;                 // REQUIRED  — an enum has no natural zero
  Status State2 = Status.Open;  // optional-with-a-default — the default supplies the value
  DateTime? ResolvedAt;         // optional  — null until it is resolved

  Customer Reporter;            // OPTIONAL  — references are optional by default
  [Required] Team Team;         // required  — opt a reference in with [Required]
}

Description#

Three ways to spell a member#

Every member falls into one of three shapes:

  1. Bare (string Title;) — non-nullable. For a natural-zero type this reads the zero; for everything else it is required.
  2. Optional (string? Note;) — nullable. Reads back null when nobody set it.
  3. Defaulted (Status State = Status.Open;) — non-nullable, but you supplied the value once at the declaration, so the row is never without one.

Why a bare string is required#

In C#, default(string) is null, not "". An empty string is a value someone typed, not the absence of one — conflating them hides bugs. So Osy# does not invent an empty string for you: a bare string Title; must be given a value by the time the row is saved. If you genuinely want "maybe unset," say so with string? Title;, which reads back null. The same reasoning covers DateTime, Guid, and Json — there is no honest "zero" of those types to fall back on.

Enums are required, not silently first#

A bare enum is required. It is not quietly defaulted to whichever member you happened to list first — reordering the members would silently change the stored default, and a value at position zero may not even be a member you named. Give it an explicit default when you want one (Status State = Status.Open;), make it optional (Status? State;), or supply it before the row is saved.

References are optional by default#

An entity reference is the deliberate exception. The everyday shape is: create the row, then let the user pick the related record from a dropdown — so demanding the reference at creation would be wrong. A bare Customer Reporter; is therefore optional (reads back null). When a reference truly must be present, mark it [Required] Team Team;.

When a required member is checked#

"Required" means present when the value becomes real — and that moment depends on what you are building:

  • Entities are saved, so a required entity member is checked when you commit the row. This is what makes the everyday create-form work: seed an empty draft (draft = new Ticket {}), bind each field to an input, and let the user fill it — the required members are checked when the row is saved, not while it is still being typed. Commit a row with a required member still unset and it is refused, naming the member.

  • A class is a transient value with no save step, so its required members are checked at construction — the compiler stops you at new, naming the member and the three fixes:

    new Receipt { }         // error: 'Receipt.Number' must be given a value — set it here
                            //        (new Receipt { Number = … }), give it a default (Number = …;),
                            //        or make it optional (string? Number;).

Examples#

A bare string parameter is required; the ? suffix makes one optional and it reads back null — the same spelling rule that governs entity members, shown here on ordinary values so it compiles on its own.

string DisplayName(string name, string? nickname) {
  // `name` is required (a bare string); `nickname` is optional (`?`), so it may be null.
  return nickname == null ? name : name + " (" + nickname + ")";
}
// DisplayName("Ada", null)   ->  "Ada"
// DisplayName("Ada", "Countess")   ->  "Ada (Countess)"

See also#

  • DateTime — a bare DateTime is required; use DateTime? for "unset until it happens".
  • enum — declaring enums and their defaults.
  • entity — declaring entities, references, and [Required].

Related

DateTime

A date and time. It is a wall-clock value, not an instant on a timeline, so it is never shifted by anybody's timezone…

Markdown

A member that holds a markdown document. You read and write it as ordinary text, but it is stored as a list of sections…

enum

A fixed set of named values, used as a member type. Stored as a number by default, or as the member's own name with…

entity

Declares a persisted type — a table of rows the app stores, queries and secures. Every entity gets an Id and audit…