Skip to content

Volume 07: Classes

1. Introduction

This volume defines class types in the Rethon programming language. It describes class declarations, class names, identity, reference semantics, fields, field type annotations, field defaults, class initialization, constructors, methods, static members, visibility, inheritance direction, protocol conformance, generic classes, equality and identity, lifecycle and memory safety, interaction with structs, and future extension points.

This document is part of the official Rethon Language Specification. It is normative rather than descriptive. Implementations, tooling, documentation systems, and other source-processing tools shall treat the rules in this volume as authoritative.

This volume does not define backend layout details, ABI conventions, garbage collection strategy, reference-counting details, or runtime-specific implementation mechanisms. It defines the source-level and language-level behavior of classes as programmer-visible reference types.

Rethon is Python-inspired in language philosophy and Python-first in surface syntax where practical. It uses static typing, classes as reference types, structs as value types, protocols as behavior contracts, value-oriented design, memory safety by default, ownership transparency, explicit immutability through let, mutable bindings by default, and lowercase primitive types. The class model shall therefore prioritize readability, explicit contracts, predictable identity behavior, and long-term maintainability while remaining compatible with future runtime implementation strategies.

Classes shall be reserved for objects whose identity, shared ownership, lifecycle, or mutable state is semantically important.

2. Class Declarations

A class declaration shall introduce a named reference type.

A class declaration shall use the class keyword.

Example:

class Logger:
    def log(message: str) -> None:
        ...

A class declaration shall define a nominal reference type whose instances are treated as identity-bearing objects rather than as value copies.

A class declaration shall introduce a new type name and a new member scope for the fields and members declared within its body.

A class declaration may appear at module scope or within other later-defined type or namespace constructs if the surrounding syntax permits it.

A class body may contain stored fields, constructors, methods, static members, inheritance declarations, protocol conformance declarations, and other later-defined member declarations.

A class body shall use indentation-based structure.

A class declaration shall be a declaration in the sense defined by the declarations and bindings volume.

3. Class Names

A class name shall be an identifier that names the class declaration.

Class names shall be case-sensitive.

Class names should normally use PascalCase.

Examples:

class Logger:
    pass

class UserSession:
    pass

A class name shall not be reserved solely because it names a class. Ordinary identifier rules shall apply, subject to keyword reservation and any later contextual restrictions defined by the language.

A class name should clearly express the conceptual object model the class represents.

Class names should describe identity-bearing concepts, lifecycle-bearing resources, service objects, controllers, sessions, or other abstractions where reference semantics are meaningful.

Examples of preferable naming patterns include:

  • Logger
  • Session
  • FileHandle
  • Connection
  • Renderer
  • Cache

A class name should not obscure meaning through unnecessary abbreviation when a clearer name is available.

4. Class Identity

A class instance shall have identity.

Identity shall distinguish one class instance from another even when their visible field values are the same.

Class identity shall be stable for the lifetime of the instance.

Two class references may refer to the same underlying object.

Example:

let first = Session()
let second = first

The bindings first and second shall refer to the same identity-bearing object.

Class identity shall be conceptually distinct from equality.

A class may be compared by identity, by semantic equality, or by both concepts in different language operations, but those concepts shall not be conflated.

A class shall not imply value semantics by default.

5. Reference Semantics

Classes shall be reference types.

Assigning a class value shall copy the reference, not create a new object.

Passing a class value to a function shall preserve reference semantics.

Returning a class value from a function shall preserve reference semantics.

Example:

class Logger:
    def log(message: str) -> None:
        pass

let primary = Logger()
let alias = primary

The bindings primary and alias shall refer to the same object.

Reference semantics shall allow shared mutable state when that state is semantically appropriate.

Reference semantics shall also support sharing of long-lived services, resource handles, caches, and other identity-bearing objects.

Reference semantics shall not force ordinary source code to expose backend ownership mechanics.

6. Fields

A field is a named stored member declared within a class body.

A class may declare zero or more fields.

Example:

class Connection:
    host: str
    port: int

Fields shall define the stored state of the class instance.

Fields shall appear in the class body according to the syntax rules of the language.

A field shall introduce a named member of the class type and shall be part of the class's state model.

Fields shall be part of the class's declared object model, not incidental implementation details.

A field shall be visible according to the visibility rules defined elsewhere in the specification.

Fields may be used by constructors, methods, equality rules, identity rules, lifecycle behavior, and future pattern-matching or introspection considerations defined in later sections of this volume or in related specification volumes.

A class may contain no fields when it represents a service object, a behavior-only abstraction, or a type whose meaning is carried by its methods and identity rather than by stored data.

Example:

class Logger:
    pass

A class without fields shall still be a reference type.

7. Field Type Annotations

A field type annotation shall state the type of the field explicitly.

Example:

class Session:
    id: str
    retries: int

Field type annotations shall be required for ordinary stored fields.

The type of a field shall be part of the class's declared public or internal contract, depending on its visibility.

Field type annotations shall follow the rules of the type system volume.

A field annotation may use any valid type expression defined by the type system, including primitive types, value types, reference types, protocol types, nullable types, generic types, and type aliases.

Examples:

class CacheEntry:
    key: str
    hits: int
    active: bool

class Response:
    body: str
    status_code: int
    note: str?

A field annotation shall be read as a contract on the stored object state.

A field annotation shall not imply backend storage details.

A field annotation shall not be inferred from later usage in place of an explicit declaration unless a later language rule explicitly permits such inference.

8. Field Defaults

A field may declare a default value.

A default value shall be used when the field is not supplied or assigned explicitly during initialization and when the language rules permit defaulted omission.

Example:

class Connection:
    host: str
    port: int = 443

Field defaults shall be type-compatible with the declared field type.

A default value shall be a valid expression in the class declaration context.

A field default shall express the ordinary starting state for the field when no explicit initialization is supplied.

Default values improve readability and reduce boilerplate in common object models.

Example:

class Logger:
    level: str = "info"

A field without a default value shall be treated as required during class initialization unless later rules define a specialized initialization path.

A field default shall be visible in the class declaration so that initialization behavior remains explicit in source code.

9. Class Initialization

A class shall be initialized using a source-level form that creates a new object instance and supplies constructor arguments or initialization data.

The preferred initialization form shall be Python-inspired and explicit.

Example:

let logger = Logger(level: "debug")
let session = Session(id: "abc123", retries: 3)

Class initialization shall ensure that every required field is assigned a value before the instance becomes observable to ordinary program code.

If a required field is omitted, the program shall be diagnosed as invalid at compile time when the omission is statically detectable.

If a field has a default value, the initializer may omit that field and use the declared default.

Class initialization shall respect the class's immutability rules, inheritance rules, and visibility rules.

The exact evaluation order of initialization expressions shall be governed by the language's general expression rules and shall remain consistent with safety and clarity requirements.

A class initialization shall produce a reference to a newly created instance of the declared class type.

A class initialization shall not allow partial objects to escape into ordinary source-level use.

10. Constructors

Constructors shall be defined with ordinary function syntax inside a class body.

The preferred constructor form shall be def __init__ with an explicit receiver parameter.

Example:

class Session:
    id: str
    retries: int = 0

    def __init__(self, id: str, retries: int = 0):
        self.id = id
        self.retries = retries

A constructor shall initialize the instance so that all required fields receive a valid value before construction completes.

Immutable fields declared with let shall be assignable only during initialization and shall not be reassigned after initialization completes.

Example:

class Account:
    let id: str
    balance: int

    def __init__(self, id: str, balance: int):
        self.id = id
        self.balance = balance

A constructor may use self to initialize fields and establish object state.

A constructor shall not permit ordinary source code to observe a partially initialized instance.

If a class inherits from a base class, base-class initialization shall complete according to the inheritance rules before the subclass constructor finishes.

The language may support synthesized constructors for straightforward classes, but any such synthesis shall obey the same field-label, required-field, default-value, and immutability rules as explicit constructors.

Constructors shall be compatible with the language's Python-first surface style while remaining statically typed and source-visible.

11. Methods

Classes shall support methods.

A method is a function declared within a class body and associated with that class type.

Example:

class Logger:
    level: str = "info"

    def log(self, message: str) -> None:
        ...

Methods shall be able to access the class's fields according to the language's object-model and visibility rules.

Methods shall be part of the class's source-level API and shall participate in ordinary scope and visibility behavior.

An instance method shall conventionally receive the current instance through an explicit receiver parameter such as self.

Methods may return values, perform transformations, coordinate state changes, or provide behavior associated with the class's object model.

Methods may be used to support protocol conformance, inheritance, and reusable abstractions.

Methods declared inside a class shall remain readable and consistent with the Python-like surface syntax that the language has already chosen.

A method shall not be required to imply hidden runtime behavior beyond the language rules defined in this specification.

12. Static Members

Classes shall support members that belong to the type itself rather than to an individual instance.

Static members shall be suitable for shared constants, type-level helpers, factory-like functions, and other declarations that conceptually belong to the class type.

Example:

class Logger:
    default_level: str = "info"

    static def create_default() -> Logger:
        return Logger(level: Logger.default_level)

The exact surface syntax for static members shall be defined consistently with the language's later member-declaration rules.

A static member shall not require an instance of the class in order to be accessed.

A static member shall be associated with the class type rather than with per-instance data.

Static members shall obey the same visibility rules as other class members.

Static members shall not be confused with instance methods or stored fields.

A static member may return or operate on class values, but it shall not depend on per-instance state unless an explicit value is supplied as a parameter.

13. Visibility

Classes and their members shall follow the visibility rules established elsewhere in the specification.

A class declaration shall have a visibility level according to the declarations and bindings volume.

Stored fields, constructors, methods, and static members shall also obey visibility rules.

The default visibility for class members shall be internal unless explicitly marked otherwise by the language's visibility syntax.

Public class members shall form part of the class's external contract.

Private class members shall be accessible only within the lexical region that defines them and any nested regions permitted by the language.

Visibility shall support encapsulation without undermining the readability of reference-oriented design.

Example:

class UserRecord:
    public name: str
    private let secret_token: str

    def __init__(self, name: str, secret_token: str):
        self.name = name
        self.secret_token = secret_token

The exact surface syntax for member visibility shall remain consistent with the language's broader declaration rules.

Visibility rules shall not require backend-specific representation details.

14. Inheritance Direction

Class inheritance shall be supported in a limited form.

A class may inherit from at most one base class.

Multiple inheritance shall not be supported.

Example:

class Animal:
    def speak(self) -> str:
        return ""

class Dog(Animal):
    def speak(self) -> str:
        return "woof"

Inheritance shall be used sparingly and intentionally.

Inheritance shall be reserved for carefully justified reuse of implementation or for compatibility with an established class hierarchy when a protocol alone is insufficient.

Protocol conformance shall be preferred over inheritance for shared behavior contracts.

Inheritance shall not be the primary mechanism for ordinary polymorphism in Rethon.

A subclass shall inherit the accessible instance and static behavior of its base class according to the language's visibility and overriding rules.

A subclass shall be able to override base-class methods when the language permits overriding for that member.

A subclass shall preserve the base class's initialization and lifecycle requirements.

A class hierarchy shall remain shallow where possible to preserve readability and maintainability.

15. Protocol Conformance

Classes shall be able to conform to protocols.

Protocols are the canonical mechanism for expressing behavioral contracts in Rethon, and classes shall participate in those contracts explicitly.

Example:

class Logger:
    def log(self, message: str) -> None:
        ...

A class shall conform to a protocol only when the language's protocol rules are satisfied.

Conformance shall be explicit and shall not be inferred merely from surface similarity.

A class may conform to multiple protocols when its methods and associated types satisfy the required contracts.

Protocol conformance shall support reference-oriented programming by allowing reusable behavior to be expressed without forcing inheritance hierarchies.

A class's ability to conform to protocols shall remain compatible with static typing, generic constraints, and the language's long-term maintainability goals.

The exact syntax of protocol conformance shall follow the object-model and protocol-model rules established elsewhere in the specification.

16. Generic Classes

Classes shall support generic parameters.

Example:

class Box<T>:
    value: T

Generic classes shall remain reference types.

Generic classes shall integrate with the type system and generic constraints defined elsewhere in the specification.

A generic class may use protocol-based constraints on its type parameters.

Example:

class CacheEntry<T: Hashable>:
    key: T
    let age: int = 0

Generic class values shall preserve reference semantics across type instantiations.

Generic class declarations shall remain readable and explicit.

Generic classes shall be suitable for reusable identity-bearing abstractions, caches, services, adapters, and other reference-oriented designs.

Generic specialization shall be treated as a language goal for efficient native code generation, but that is an implementation concern rather than a source-level guarantee.

17. Equality and Identity

Equality and identity shall be distinct concepts for classes.

Identity shall identify the object itself.

Equality shall describe semantic equivalence when the language or the class's protocol conformance defines such a notion.

A class shall not automatically acquire value-style structural equality merely because its fields happen to resemble a record layout.

Absent an explicit equality contract, class comparisons shall be identity-based.

If a class conforms to an equality protocol or equivalent contract, equality shall follow that contract and may differ from identity when the language rules permit it.

Example:

class Session:
    token: str

let first = Session(token: "abc123")
let second = Session(token: "abc123")

The values first and second may represent different objects even if a later equality contract considers them equivalent.

Identity comparison shall remain a separate concept and shall not be replaced by equality comparison.

Identity semantics shall preserve the class model's suitability for shared ownership, object lifecycle, and stateful services.

18. Lifecycle and Memory Safety

Classes shall participate in the language's memory-safety model.

Ordinary source code shall not require manual memory management for common class usage.

The language shall preserve ownership transparency at the source level. Developers shall not need to expose retain/release operations, pointer manipulation, or other low-level lifetime mechanics in ordinary code.

The specification intentionally does not prescribe a single runtime strategy such as reference counting, garbage collection, or a hybrid design. Those are implementation concerns as long as the observable language guarantees are preserved.

A class instance shall be considered fully usable only after initialization completes.

A class shall not be observable in a partially initialized state by ordinary source code.

Lifecycle-sensitive objects such as handles, sessions, caches, and service objects shall remain easy to model without leaking backend lifetime mechanics into source code.

If later language work introduces deinitializers, weak references, or other lifecycle features, those features shall remain compatible with the language's memory-safety and ownership-transparency goals.

19. Interaction with Structs

Structs shall remain the preferred data-modeling construct.

Classes shall be used when identity, shared ownership, mutable state, or lifecycle management matters.

Structs and classes shall coexist as complementary type forms rather than as competing defaults.

Example:

struct Point:
    x: int
    y: int

class Session:
    token: str

A Point shall be modeled as a value-oriented struct, while a Session shall be modeled as an identity-bearing class.

Struct values and class references may be stored together in larger data models when the design requires both value semantics and identity semantics.

Structs may contain references to classes, and classes may contain struct fields, as long as the surrounding type rules are satisfied.

Protocol conformance shall remain available to both structs and classes so that shared behavior does not depend on whether a type is value-based or reference-based.

20. Future Extensions

This volume defines the current direction for classes in Rethon, but future specification work may refine the model in carefully controlled ways.

Possible future extensions include:

  • richer constructor convenience syntax
  • synthesized equality or hashing for simple class forms
  • final or sealed class modifiers if later needed
  • more detailed override and dispatch rules
  • lifecycle hooks such as deinitialization if the memory model warrants them
  • weak or unowned reference categories if future memory-model work requires them
  • more explicit class-method syntax if later readability work justifies it
  • controlled inheritance refinements, provided protocols remain the preferred abstraction mechanism

Any future extension shall preserve the current direction:

  • classes are reference types
  • classes are identity-bearing objects
  • classes are reserved for identity, lifecycle, shared ownership, or mutable state
  • structs remain the preferred data-modeling construct
  • fields are mutable by default
  • let introduces immutable fields where needed
  • initialization remains explicit and complete
  • protocol conformance remains preferred over inheritance
  • equality and identity remain distinct
  • ownership transparency remains visible at the source level

21. Summary

Classes shall be reference types in Rethon.

A class shall model an identity-bearing object whose lifecycle, shared ownership, or mutable state matters semantically.

Class fields shall follow the same mutability model established for structs, with mutable fields by default and let for immutability. Immutable fields shall be assignable only during initialization.

Class initialization shall be explicit and Python-inspired, with def __init__ as the preferred constructor form. Required fields shall be initialized, defaults shall participate in initialization, and partial initialization shall not be allowed in ordinary source code.

Inheritance shall be limited to single inheritance and shall remain secondary to protocol conformance. Equality and identity shall remain distinct, and ownership transparency shall hide runtime memory-management details from ordinary source code.

Classes shall therefore fit Rethon's broader design: Python-first where practical, statically typed, value-oriented by default, protocol-oriented for shared behavior, and safe for long-lived maintainable code.