Attributes and Properties
Klyn distinguishes between stored implementation state and the public surface that exposes it. In practice, properties are the primary way to publish state cleanly, while attributes or backing fields remain an implementation detail.
public class Rational:
private _numerator as Int
private _denominator as Int
Backing fields usually carry a leading underscore and stay private to the class.
public property denominator as Int:
get:
return this._denominator
set:
if value == 0:
throw ValueException("denominator cannot be 0")
this._denominator = value
Properties can enforce validation or compute values before they expose data.
public class Point:
public property x = 0
public property y = 0
Auto-properties are ideal when you want a normal property surface but do not need custom getter or setter logic.
import klyn.collections
class Project:
public readonly property name as String
public readonly property files as ArrayList<String>
public Project(name as String):
this.name = name
this.files = []
project = Project("Klyn")
# project.name = "renamed" # Compile-time error: no writable setter.
project.files.add("main.kn") # Allowed: readonly is shallow.
Use readonly when callers may observe a property but must not replace its value.
This is a shallow restriction: if the property returns a mutable object, that object's own API
remains usable. Unlike a class-level const, a readonly property is not an implicitly
static constant; its getter may return instance state or compute a value on every access. Use an
immutable collection or value type when deep immutability is required.
A custom readonly property may declare a get block only. Declaring a
set block would contradict the public contract and is rejected during compilation.
class SecretSink:
private _receivedSize as UInt = 0u
public writeonly property secret as String:
set:
this._receivedSize = value.size
public readonly property receivedSize as UInt:
get:
return this._receivedSize
sink = SecretSink()
sink.secret = "temporary value"
assert sink.receivedSize == 15u
# print(sink.secret) # Compile-time error: no readable getter.
Use writeonly when callers may submit a value but must not read it back. The
modifier removes the getter from both auto-implemented and custom properties; a custom
write-only property must declare a set block. A property cannot be both
readonly and writeonly, and signals and events always remain readable.
class TokenReceiver:
public writeonly property token as String
receiver = TokenReceiver()
metadata = type(receiver).getProperty("token")
assert metadata.isWriteonly
metadata.setValue(receiver, "one-use token")
# metadata.getValue(receiver) # TypeError: the property is write-only.
Reflection preserves the same contract: Property.setValue() may write the value,
while Property.getValue() raises TypeError. A write-only declaration is
valid only for a property, never for a field, method, constructor, signal, event, or entity
relationship. An implementation or override of a write-only property must preserve its setter.
Add signal when a property represents observable state. Assignment keeps the same
property syntax, but the compiler compares the old and new values and notifies the shared
binding engine automatically when the value actually changes. Application code must not call
Binding.notify() itself.
import klyn.binding
public class Counter:
public signal property value as Int = 0
counter = Counter()
mirror = Counter()
Binding.oneWay(counter::value, mirror::value)
counter.value = 1
assert mirror.value == 1
Use signal properties for durable state such as text, selection, progress, or a current value.
Use an event instead for a one-shot action such as a click. The complete distinction
is covered in Signals vs Events.
r = Rational(1, 3)
print(r.numerator)
r.denominator = 5
Properties use ordinary member access syntax. Callers do not need to know whether the property is auto-implemented or backed by custom getter and setter blocks.
| Need | Preferred form |
|---|---|
| Internal implementation state only | Private backing field |
| Simple public state | Auto-implemented property |
| Validation or derived logic | Custom property with get and/or set |
| Read-only public surface | readonly property |
| Accept a value without exposing it | writeonly property |
| Observable state for binding | signal property |
An ordinary property is readable and writable, a readonly property is readable only,
and a writeonly property is writable only. These capabilities are part of interface
and inheritance contracts, not merely implementation hints.