Nanyx

Classes

How class-based designs map to modern Nanyx

Nanyx does not use classes or inheritance. Instead, it separates data, behavior, and encapsulation into explicit language features.

For people coming from C#, Java, TypeScript, or Kotlin, the mental model is:

  • class fields → record fields,
  • instance/static methods → regular functions or attached definitions,
  • inheritance hierarchies → composition and tagged unions,
  • private internals → module boundaries and nominal/protected types.

Data: records instead of class instances

Use records to model object-like data:

type Point = (x: int, y: int)

def p = Point(x = 10, y = 20)

Records are structural and immutable by default. To “update an object,” create a new value using with:

def moved = p with (
  x = p.x + 5
  y = p.y + 3
)

See Records and tuples for the full model.

Behavior: functions and attached definitions

Behavior is defined separately from data:

type Point = (x: int, y: int)

def Point.translate = { point, dx, dy ->
  point with (
    x = point.x + dx
    y = point.y + dy
  )
}

def moved = Point.translate(p, 5, 3)
def moved2 = p \translate(5, 3)

def Point.origin = Point(x = 0, y = 0)

This gives method-like ergonomics without classes.

Encapsulation: modules and type boundaries

Encapsulation is done with module exports and type boundaries, not class visibility modifiers.

  • Keep construction/validation logic in a module.
  • Expose smart constructors and helper functions.
  • Use nominal/protected types when representation must be controlled.

See Nominal and Protected Types.

State and mutation

Nanyx supports mutation, but it is explicit and scoped through contexts (for example memory { ... }).

This keeps mutation local rather than making every instance implicitly mutable.

See Memory for details.

Inheritance alternatives

Most inheritance use-cases map to one of these patterns:

  • Shared structure → composition with records.
  • Shared behavior → attached definitions and reusable functions.
  • Variant families / polymorphism → tagged unions plus pattern matching.

This usually leads to APIs that are easier to read and evolve because behavior is explicit at call sites.

Quick mapping

Class-based conceptNanyx approach
Class instanceRecord value
Instance methodAttached definition or function
Static methodNamespaced function / attached definition
ConstructorRecord literal or validated factory function
InheritanceComposition + tagged unions
Private fieldsModule boundary + nominal/protected type

If you are modeling OOP-style APIs, start with records + attached definitions first; add nominal/protected types when you need stronger boundaries.