What are the naming conventions for types in EK9?

← Variable Naming Rules and Conventions · Ref: Q293

EK9 uses PascalCase for all type names: classes, records, traits, enumerations, and functions used as types. Module names use lowercase dot-separated segments.

CLASSES

PascalCase nouns describing what the class represents: CustomerOrder, PaymentProcessor, EmailValidator. Classes model entities or processors.

RECORDS

PascalCase nouns describing the data structure: Coordinate, Temperature, HttpResponse. Records are data-focused types with public fields, constructors, and operators (but no methods). Records are mutable — use 'as pure' to control mutation.

TRAITS

PascalCase adjectives or capability names: Printable, Sortable, Configurable. Traits describe what a type can do, not what it is.

ENUMERATIONS

PascalCase singular nouns: Colour, DayOfWeek, HttpStatus. Each value represents one instance of the concept.

FUNCTIONS AS TYPES

PascalCase verb phrases when used as abstract function types: FormatName, CalculateTotal, ValidateInput. Standalone functions use camelCase (see Q294).

GENERIC TYPE PARAMETERS

Single uppercase letters: T for general type, K for key, V for value, E for element. These are universally understood conventions.

MODULES

Lowercase dot-separated: com.example.banking, org.ek9.core. Module names follow reverse domain convention.

CONSTRAINED TYPES

Named for the constraint they represent: EmailAddress as String constrain, PositiveInteger as Integer constrain. The name describes what the constraint enforces.

See Q292 for variable naming. See Q294 for function naming. See Q93 for classes. See Q97 for records.

Example

defines module qa.naming.types

  defines trait

    Printable
      printTo() as pure abstract
        -> target as String
        <- formattedOutput as String?

  defines record

    Coordinate
      xPosition as Float: 0.0
      yPosition as Float: 0.0

      Coordinate()
        ->
          initialX as Float
          initialY as Float
        this.xPosition :=: initialX
        this.yPosition :=: initialY

      operator $ as pure
        <- rtn as String: `(${xPosition}, ${yPosition})`

      operator <=> as pure
        -> other as Coordinate
        <- rtn as Integer: xPosition <=> other.xPosition

      override operator ? as pure
        <- rtn as Boolean: xPosition? and yPosition?

  defines class

    ShapeRenderer with trait of Printable
      shapeName as String: String()

      ShapeRenderer()
        -> name as String
        this.shapeName :=: name

      override printTo() as pure
        -> target as String
        <- formattedOutput as String: `${shapeName} -> ${target}`

      operator $ as pure
        <- rtn as String: `Renderer(${shapeName})`

      override operator ? as pure
        <- rtn as Boolean: shapeName?

  defines type

    DayOfWeek
      Monday
      Tuesday
      Wednesday
      Thursday
      Friday
      Saturday
      Sunday

  defines function

    FormatCoordinate() as pure abstract
      -> point as Coordinate
      <- formattedPoint as String?

  defines program

    TypeConventionsDemo()
      stdout <- Stdout()

      // === RECORD: PascalCase noun ===

      origin <- Coordinate(0.0, 0.0)
      destination <- Coordinate(3.5, 7.2)
      stdout.println(`Origin: ${origin}`)
      stdout.println(`Destination: ${destination}`)

      // === CLASS WITH TRAIT: PascalCase ===

      renderer <- ShapeRenderer("Circle")
      renderedOutput <- renderer.printTo("Canvas")
      stdout.println(renderedOutput)

      // === ENUMERATION: PascalCase singular ===

      today <- DayOfWeek.Wednesday
      stdout.println(`Today: ${today}`)

      // === GENERIC TYPE PARAMETER CONVENTION ===

      names <- ["Alice", "Bob", "Charlie"]
      for n in names
        stdout.println(`Name: ${n}`)

      // === FUNCTION TYPE: PascalCase verb phrase ===

      formatter <- (capturedOrigin: origin) is FormatCoordinate as pure function
        formattedPoint :=? `Point at ${point} from ${capturedOrigin}`

      stdout.println(formatter(destination))

Common mistakes

E50060 — The method is named 'printTo' not 'render'. Calling a method that does not exist on the type produces a resolution error. See ek9 -h E50060 for details.

Incorrect:

renderedOutput <- renderer.render("Canvas")

Correct:

renderedOutput <- renderer.printTo("Canvas")

E08090 — If the variable 'today' is declared but never referenced in any expression, the compiler rejects it as unused. See ek9 -h E50050 for details.

Incorrect:

todaySummary <- `Today: ${today}`

Correct:

stdout.println(`Today: ${today}`)
Other ways to ask this
  • How should I name classes, records, and traits in EK9?
  • What naming style do EK9 types use?
  • How do I name modules and generic type parameters in EK9?

Coming from another language?

Java: PascalCase for classes (convention, not enforced), package names lowercase. Python: PascalCase for classes (PEP 8, not enforced), snake_case for modules. Rust: PascalCase for types (enforced as warning), snake_case for modules. Go: PascalCase for exported types, lowercase for unexported. C++: no standard, varies widely. Kotlin: PascalCase for classes (convention), package names lowercase. Swift: PascalCase for types (convention). EK9: PascalCase for all types, lowercase dot-separated for modules, single uppercase for generics, descriptive naming enforced by compiler.

Keywords: type, record, function, module, generic, naming, PascalCase, class, convention, constrained, enumeration, trait, identifier