Explain the design decisions in this EK9 code for a team wiki.

← Getting Started · Ref: Q1031

Design Rationale for UserService module:

Structure: The module separates validation (pure functions) from state management (the UserService class). This separation means validation logic can be tested without setting up a service instance.

Validation functions are marked 'as pure' because they only compute results from their inputs — no side effects, no state changes. This makes them safe to call from any context, including concurrent code.

The UserService class uses guard expressions in its methods. When findUser returns a value, the guard (if user <- findUser(name)) ensures the block only runs when a valid user was found. There is no null checking and no exception handling for missing users — the guard handles it.

Return values are declared at the top of each function with <- . This is how EK9 functions declare what they return — it is not a statement that causes an early exit. Every code path must initialise the return value.

The class has a default private constructor because its properties are uninitialised at construction time. Only the parameterised constructor is public.

Example

defines module qa.gettingstarted.designrationale

  defines function

    isValidName() as pure
      -> name as String
      <- rtn as Boolean: name? and name.length() > 0

    isValidEmail() as pure
      -> email as String
      <- rtn as Boolean: email? and email.contains("@")

  defines class

    User
      name as String?
      email as String?

      default private User()

      User()
        ->
          name as String
          email as String
        this.name: name
        this.email: email

      override operator ? as pure
        <- rtn as Boolean: name? and email?

      operator $ as pure
        <- rtn as String: `${name} (${email})`

  defines program

    DesignRationaleDemo()
      stdout <- Stdout()

      name <- "Alice"
      email <- "alice@example.com"

      if isValidName(name) and isValidEmail(email)
        user <- User(name, email)
        stdout.println(`Created: ${user}`)

Common mistakes

E07110 — A non-abstract operator or method must provide an implementation — declaring only an uninitialised return '<- rtn as String?' leaves it with no body, so give the return a value. See ek9 -h E07110 for details.

Incorrect:

      operator $ as pure
        <- rtn as String?

Correct:

      operator $ as pure
        <- rtn as String: `${name} (${email})`
Other ways to ask this
  • Document the design rationale behind this code
  • Write a technical explanation of this module for new team members
  • Explain why this code is structured this way

Coming from another language?

EK9 design documentation explains what the code does and why, without comparing to other languages.

Keywords: wiki, explain, team, rationale, document, design