Summary#
Osy# is C#, and the goal is that your C# instincts are right. They almost always are — which is exactly why the few places they are not cost more than their number suggests: you write the C# form, and only a failed compile tells you otherwise.
Three of those live in and around a component. All three produce a clear error naming the replacement, so nothing
here is a trap you can ship — but reading them once is cheaper than meeting them one failed compile at a time.
Description#
Ordinary C# that works — write it, do not route around it#
| Write this | Where |
|---|---|
r switch { Room.Kitchen => 1, Room.Bedroom => 2, Room.Bath => 3 } | a function, a computed, a render block |
Room.Members.Length — and Enum.GetValues<Room>() is the same array | anywhere |
Math.Floor(x) · Math.Ceiling(x) · Math.Round · Math.Abs · Math.Min · Math.Max | anywhere |
p.Name?.Trim() ?? "none" — null-conditional and null-coalescing, chained | anywhere |
x ??= fallback · ternaries · foreach · var · string interpolation · LINQ | anywhere |
Write the C# form first. A construct Osy# does not take is a compile error naming the line and the replacement.
enum Room { Kitchen, Bedroom, Bath }
entity Kiln {
[MaxLength(80)] string Name = "";
Room Where = Room.Kitchen;
decimal Litres = 0m;
security { allow read, create, update, delete when IsAnonymous || IsAuthenticated; }
}
int Rank(Room r) => r switch { Room.Kitchen => 1, Room.Bedroom => 2, Room.Bath => 3 };
int RoomCount() { return Room.Members.Length; }
decimal WholeLitres(Kiln p) { return Math.Floor(p.Litres); }
string Caption(Kiln p) { return p.Name?.Trim() ?? "unnamed"; }There is no method keyword#
A component method is a return type and a name, exactly as in C#. The token after the name is what distinguishes a method from a field.
component ReportRow(Report report) {
string Load() { … } // ✓ a method — return type, name, parameter list
method string Load() { … } // ✗ `method` is not a keyword
}method is the word most people try, because the surrounding declarations (action, on change) do read as
keywords. They are different things: action and on change name reactive machinery that has no C# equivalent, so
they get a word. A method is just a method, so it looks like one.
See component for the full member list.
public is a class modifier, not an entity one#
This is the difference most likely to bite, because the two forms sit a line apart and look alike:
class ReceiptFields {
public decimal? Amount; // ✓ a class member IS private by default — exactly C#
}
entity Report {
public string Title; // ✗ refused
string Title; // ✓
}A class is an ordinary C# type: its members are private by default and public opens them. An entity is not —
its fields are a data surface, and who may read or write them is not a property of the field but a declared rule on
the entity (security { }). Access control there is the security { } block, and allowing public on
a field would suggest a second, weaker answer to a question that already has one.
The compiler says so directly:
Visibility/
constmodifiers apply toclassmembers; entity access control is thesecurity { }block.
Note this is about the FIELD. Entity and class types do take a visibility modifier, and their defaults differ — see type visibility (public / internal).
A reactive side effect is on change { … }, not a named effect#
There is no effect keyword. A block that reacts to its dependencies changing is on change, one of the lifecycle
family alongside on mount and on unmount:
component Search(string term) {
on change { … } // ✓ runs when what it reads changes
effect Watch { … } // ✗ `effect` is not a keyword
}Writing the old form gets an error that names the replacement and the rest of the family, so you land in the right
place from the first attempt. on change covers when it runs and what it depends on; on mount / on unmount covers
on mount / on unmount.
Examples#
All three correct at once — a method declared C#-style, a class whose members take public, and an entity whose
fields do not. This one is compiled by the documentation build, so it is the shape to copy:
class Filters {
public string? Term; // class member: private by default, `public` opens it
}
entity Report {
[MaxLength(200)] string Title = ""; // entity field: no visibility modifier
}
[Page("/reports")]
[AllowAnonymous]
component ReportList() {
var filters = new Filters();
string Caption() { // a method — a return type and a name, no `method` keyword
return filters.Term == null ? "All reports" : "Filtered";
}
render { Text(Caption()); }
}See also#
component — the component archetype and every member kind it can hold.
on change — the reactive side-effect block, and what makes it re-run.
on mount / on unmount — on mount and on unmount.
type visibility (public / internal) — visibility on TYPES (where entity and class defaults genuinely differ).
class methods — methods on a class, which follow the same shape as a component's.