Properties
A property is a behavioral member that uses field-like access syntax. It can control how a value is read, how assignment is handled, or both.
impl Vehicle {
property TopSpeed: Speed {
get {
return self._speed
}
set speed {
self._speed = speed
}
}
}
Callers use normal member syntax:
let speed := vehicle.TopSpeed
vehicle.TopSpeed = requestedSpeed
Fields vs Properties
A field is stored state:
type Vehicle struct {
_speed: Speed,
}
A property is controlled behavior:
impl Vehicle {
property TopSpeed: Speed {
get {
return self._speed
}
}
}
The property may read a field, compute a result, synchronize access, translate another representation, observe foreign state, or perform other operations permitted by Sec's ordinary rules.
The property syntax itself never means “create a hidden backing field”.
If no behavioral boundary is needed, use a field or another direct stored member. Use a property when the access itself has meaning.
Property Declaration
An instance property is declared inside an implementation:
impl Vehicle {
property TopSpeed: Speed {
get {
return self._speed
}
set speed {
self._speed = speed
}
}
}
The declaration has:
- a property name;
- a declared property type;
- one or more accessors.
The general shape is:
property Name: Type {
accessors
}
A property must contain at least one accessor.
A property body is not a general declaration block. It may contain only the property accessors permitted by the language.
Accessor Combinations
A property may contain:
- one
get; - one
set; - or one
try set.
set and try set are mutually exclusive because both
define assignment behavior.
| Accessors | Property behavior |
|---|---|
get |
Read-only |
set name |
Write-only, infallible |
try set name ErrorType |
Write-only, fallible |
get + set name |
Read-write, infallible assignment |
get + try set name ErrorType |
Readable, fallible assignment |
A property without a getter is not readable.
A property without a setter is not assignable.
Getter Semantics
A getter returns the property's declared type:
property CurrentSpeed: Speed {
get {
return self._speed
}
}
A getter:
- must return a value assignable to the property type;
- must return on every reachable path required by normal return analysis;
- uses ordinary expression, ownership and borrowing rules;
- receives implicit
selffor an instance property; - is behavioral and is not assumed to be a simple stored-field load.
Getters are infallible in Sec 0.1
Sec 0.1 does not define try get.
This is not property syntax:
property Data: Data {
try get DataError {
...
}
}
If reading an abstraction can legitimately fail, expose that fallibility through an ordinary method or another API whose type communicates the failure.
The Setter Parameter Is Always Explicit
Every setter names its incoming value:
property Name: string {
set value {
self._name = value
}
}
The parameter name is chosen by the programmer.
property Name: string {
set name {
self._name = name
}
}
The setter parameter automatically has the declared property type.
There is no implicit C#-style setter variable named value.
This is invalid:
property Name: string {
set {
self._name = value
}
}
Infallible set
An infallible setter uses set:
property TopSpeed: Speed {
set speed {
self._speed = speed
}
}
Assignment:
vehicle.TopSpeed = requestedSpeed
means that:
- the receiver is evaluated;
requestedSpeedis evaluated;- the setter parameter
speedreceives that value; - the setter body executes.
An infallible setter does not expose an error result.
If assignment can legitimately fail, the property must use
try set instead of hiding the failure.
Fallible try set
A property whose assignment may fail uses try set and declares
the error type explicitly:
property TopSpeed: Speed {
get {
return self._speed
}
try set speed SpeedError {
if speed < MinimumSpeed {
return Err(SpeedError.TooLow)
}
self._speed = speed
}
}
The shape is:
try set ParameterName ErrorType {
...
}
ErrorType must be error or a concrete Sec error type.
It is part of the public property contract and is not inferred from the body.
Success
Normal completion means success:
try set speed SpeedError {
self._speed = speed
}
A bare return is also success:
try set speed SpeedError {
if speed == self._speed {
return
}
self._speed = speed
}
Failure
Failure is explicit:
return Err(SpeedError.TooLow)
No return Ok()
This is invalid:
try set speed SpeedError {
self._speed = speed
return Ok()
}
A try set accessor is not written as a source-level function returning
Result[void, E]. Its error contract is expressed by the accessor syntax.
Fallible Property Assignment
Assignment through a fallible setter must be visibly fallible at the call site.
try vehicle.TopSpeed = requestedSpeed {
Err(error) => Handle(error)
}
Omitting try is invalid:
vehicle.TopSpeed = requestedSpeed
when TopSpeed selects a try set accessor.
This keeps assignment syntax honest: a property cannot hide a failing operation behind syntax that looks infallible.
Propagation and other try forms follow the ordinary Sec error-handling rules.
Compound Assignment
Compound assignment uses both property read and property write behavior.
vehicle.Count += 1
requires:
- a getter;
- a valid setter;
- a valid underlying compound operation for the property type.
A write-only property cannot be used with compound assignment because there is no value to read first.
If the selected setter is fallible, compound assignment also requires
try:
try vehicle.TopSpeed += delta {
Err(error) => Handle(error)
}
Compound Assignment Evaluation Order
Sec gives compound property updates precise evaluation guarantees.
For:
GetVehicle().Count += NextDelta()
the conceptual order is:
- evaluate
GetVehicle()exactly once; - read the getter exactly once;
- evaluate
NextDelta()exactly once; - compute the compound result;
- invoke the setter exactly once with the computed value.
The compiler must preserve this source-visible order and every observable side effect.
Implicit self
Instance property accessors receive compiler-provided self.
impl Counter {
property Value: int {
get {
return self._value
}
set next {
self._value = next
}
}
}
The programmer does not declare self,
ref self or ref mut self in property syntax.
The compiler derives the receiver access required by each accessor from the accessor body and ordinary ownership and borrowing rules.
A setter normally requires mutable receiver authority when it mutates instance state. A getter may require only shared access when its implementation only reads state.
Primary Impl and impl extends
Properties may appear in the primary implementation:
impl Vehicle {
property Speed: Speed {
get {
return self._speed
}
}
}
or in a same-module extension fragment:
impl extends Vehicle {
property IsMoving: bool {
get {
return self._speed > 0
}
}
}
All valid fragments form one combined implementation member surface.
A property therefore cannot be duplicated or conflict with another member merely because the declarations are in different source files.
Static Properties
A type-level property uses explicit static property:
impl Application {
static let mut _mode: ApplicationMode := ApplicationMode.Normal
static property Mode: ApplicationMode {
get {
return Application._mode
}
set mode {
Application._mode = mode
}
}
}
Use the type to access it:
let current := Application.Mode
Application.Mode = ApplicationMode.Safe
A static property:
- has no instance receiver;
- may not use
self; - uses the same explicit setter-parameter rule;
- supports
get,setandtry setunder the same property rules; - is accessed through the type, not an instance.
Static properties do not themselves create static storage. Their implementation may use explicit static storage or compute a value by another valid mechanism.
Properties in Interface Contracts
An interface may require observable property behavior without requiring any particular stored field.
interface Positioned {
property Position: Point {
get
try set position PositionError
}
}
An interface property requirement specifies:
- the property name;
- the property type;
- required readable access with
get; - required infallible writable access with
set name; - or required fallible writable access with
try set name ErrorType.
The setter parameter remains explicit even though the interface contains no setter body.
The implementing type may satisfy the contract using:
- stored state;
- a computed value;
- foreign state;
- register access;
- another valid implementation strategy.
Conformance checks include:
- property type;
- required accessor availability;
- setter fallibility;
- fallible setter error contract;
- static versus instance membership;
- receiver capability.
Property Identity and Member Namespace
A property has semantic identity including:
- name;
- declared property type;
- getter availability;
- setter kind;
- fallible setter error type where applicable;
- static or instance membership;
- source location.
Property names participate in the target type's member namespace.
A property may not silently collide with:
- a stored field;
- a register field;
- another property;
- a method where the member naming rules forbid the collision;
- another associated member occupying the same namespace.
Member registration happens before property bodies are analyzed, so accessors may use compatible members declared later in the same complete implementation surface.
Effects and Side Effects
Member-access syntax does not imply purity.
let value := object.Property
may execute getter behavior.
A getter is not assumed pure merely because it visually resembles field access. A setter is not assumed to be a simple field store.
Property semantics preserve:
- evaluation order;
- getter side effects;
- setter side effects;
- fallible setter behavior;
- ownership and borrowing;
- cleanup and
defer; - volatile effects reached through the implementation;
- foreign effects reached through the implementation.
Optimizations may inline or simplify a property only when all language-visible behavior remains equivalent.
Ownership and Borrowing
Properties use ordinary Sec ownership and borrowing rules.
The property type determines the value that crosses the accessor boundary. The accessor body must respect the same move, copy, borrow and lifetime rules as equivalent ordinary code.
A property does not become addressable stored state merely because its access
uses object.Name syntax.
Receiver requirements are derived from what the accessor actually does. A property that mutates state requires the appropriate mutable authority; a read-only computation may require only shared receiver access.
Fallibility does not weaken ownership guarantees. A failed
try set follows ordinary path-sensitive ownership and cleanup rules.
Properties and Constrained Types
Contracts belong to types, not to properties.
type Percent int range 0..100
A property may use the constrained type directly:
type Settings struct {
_volume: Percent,
}
impl Settings {
property Volume: Percent {
get {
return self._volume
}
set volume {
self._volume = volume
}
}
}
Because the setter parameter already has type Percent, its value has
already satisfied the type's contract before entering the setter.
If an accessor performs a separate checked conversion or other fallible operation,
that fallibility follows the ordinary try rules and, for a setter,
must be represented by a try set error contract where failure may escape.
Compiler-Known SizeOf Properties
Sec also uses the read-only property shape for certain compiler-known authoritative properties.
In particular, where defined by the compiler-known registry:
value.SizeOf
TypeName.SizeOf
have canonical uint property shapes.
For example:
let valueSize: uint := value.SizeOf
let typeSize: uint := Packet.SizeOf
These are compiler-known properties rather than ordinary user declarations. User code cannot replace them where the compiler-known property applies.
Sec 0.1 has no global source form:
SizeOf(Packet)
Property Access Has Distinct Semantic Meaning
The compiler must preserve the difference between:
- field read;
- field write;
- property read;
- property write;
- fallible property write;
- compound property update.
A backend must not infer property semantics later from ordinary field access.
This distinction is important even when optimization eventually makes a trivial property access as cheap as a direct field access.
Source semantics are defined by the accessor behavior, not by the final machine instruction sequence.
Diagnostics
Important property diagnostics include:
- missing property name;
- missing
:or property type; - property with no accessor;
- duplicate getter;
- duplicate setter;
- both
setandtry seton one property; - setter without an explicit value parameter;
- missing or invalid error type on
try set; - getter value incompatible with the property type;
- getter missing a required return;
- read from a write-only property;
- assignment to a read-only property;
- assignment value incompatible with the property type;
- fallible property assignment without
try; return Ok()insidetry set;- compound assignment without a getter;
- invalid receiver or borrow use in an accessor;
- instance/static access mismatch;
- duplicate or conflicting property across implementation fragments;
- interface property conformance mismatch.
A useful fallibility diagnostic is:
assigning fallible property TopSpeed requires try
A missing setter parameter should identify the property directly:
setter for Name must declare a value parameter
LSP hover should expose whether a property is:
- instance or static;
- readable;
- infallibly writable;
- fallibly writable;
- and the explicit setter error type where applicable.
Best Practices
- Use fields for stored representation.
- Use properties for controlled behavior.
- Prefer a field when no behavioral boundary is required.
- Use a property when access must validate, compute, synchronize, translate or observe.
- Name setter parameters for clarity;
valueis conventional but never implicit. - Keep getters unsurprising when possible, even though getters may legally have effects.
- Use
try setwhenever assignment can legitimately fail. - Do not hide failure inside an infallible
set. - Remember that compound assignment performs both a read and a write.
- Use methods instead of properties when an operation's cost, fallibility or action-like nature would be clearer as a call.
- Keep related properties near the type's main behavior; use
impl extendswhen coherent source separation improves readability.
Summary
- A property is behavior presented through member-like access syntax.
- A property does not create hidden storage.
- Properties belong to implementations or interface contracts.
- A property must contain at least one accessor.
- A property may have one
getand either onesetor onetry set. setandtry setare mutually exclusive.- A property without
getis write-only. - A property without a setter is read-only.
- Getters are infallible in Sec 0.1.
- Sec 0.1 has no
try get. - A getter must produce the declared property type on every continuing path.
- Every setter names its incoming value parameter explicitly.
- The setter parameter automatically has the property type.
- Sec has no implicit setter variable named
value. set namedefines infallible assignment.try set name ErrorTypedefines fallible assignment.- A fallible setter's error type is explicit and is not inferred from the body.
- Normal completion or bare
returnmeans success intry set. return Err(error)reports setter failure.return Ok()is invalid insidetry set.- Assignment through a fallible setter requires explicit
try. - Compound property assignment requires both a getter and a setter.
- A fallible compound update also requires
try. - Compound assignment evaluates receiver, getter, right-hand side and setter exactly once in defined order.
- Instance property accessors receive implicit
self. - The compiler derives accessor receiver requirements from the accessor bodies.
- Properties may appear in the primary implementation or same-module
impl extendsfragments. - Static properties use explicit
static property, have noselfand are accessed through the type. - Interface properties describe observable behavior, not stored fields.
- Interface conformance checks property type, accessors, fallibility, error contract, static/instance category and receiver capability.
- Property names participate in the type's combined member namespace.
- Properties may not silently collide with fields, register fields, other properties or incompatible members.
- Property syntax does not imply purity.
- Getter and setter side effects, ownership, cleanup, volatile effects and foreign effects must be preserved.
- Properties use ordinary ownership and borrowing rules.
- A constrained property type preserves that type's contracts.
- Compiler-known
value.SizeOfandTypeName.SizeOfuse canonical read-onlyuintproperty shapes where defined. - Sec 0.1 has no global
SizeOf(TypeName)source form. - Semantic lowering preserves property reads, writes, fallible writes and compound updates as distinct operations until optimization can safely simplify them.