How do sanitized parameters interact with pure functions in EK9?

← Security and Sanitization · Ref: Q217

In pure functions, sanitized parameters must use the copy constructor pattern because reassignment is forbidden. Purity and security reinforce each other.

PURE + SANITIZED = COPY CONSTRUCTOR

Pure functions cannot reassign, so the only option is:

  pureProcess() as pure
    -> input as sanitized String
    <- result as String?
    localCopy <- String(input)
    result: localCopy

The copy constructor creates an independent value.

WHY THEY INTERACT

Purity prevents mutation, sanitization tracks tainted data. Together they force the safest pattern: create a defensive copy and use only the copy. No escape hatches.

NON-PURE ALTERNATIVES

Non-pure functions have additional options:

  safeCopy <- String(input)
  result: ""
  result :=: safeCopy     // copy operator
  result :~: safeCopy     // merge operator

EXPRESSION USE

Both pure and non-pure can use sanitized parameters directly in expressions:

  result: "Prefix: " + input

Concatenation creates a new value, so no aliasing risk.

See Q215 for sanitized basics. See Q54 for pure functions. See Q141 for constant immutability. See Q273 for purity as security boundary.

See Q662 for safe copy patterns. See Q666 for injection pure context.

Example

defines module qa.security.pure

  defines function

    // Pure function MUST use copy constructor
    pureWithSanitized() as pure
      -> input as sanitized String
      <- result as String?

      // Only option in pure context: copy constructor
      cleanInput <- String(input)
      result: "Clean: " + cleanInput

    // Non-pure has more options
    nonPureWithSanitized()
      -> input as sanitized String
      <- result as String?

      // Option 1: Copy constructor (works everywhere)
      safeCopy <- String(input)

      // Option 2: :=: on initialized variable
      result: ""
      result :=: safeCopy

    // Direct expression use works in both contexts
    directUse() as pure
      -> input as sanitized String
      <- result as String?

      // Concatenation creates new value — safe
      result: "Output: " + input

  defines program

    SanitizedPureDemo()
      stdout <- Stdout()

      userInput <- "external input"

      // === PURE + SANITIZED ===

      result1 <- pureWithSanitized(userInput)
      if result1?
        stdout.println(result1)

      // === NON-PURE + SANITIZED ===

      result2 <- nonPureWithSanitized(userInput)
      if result2?
        stdout.println(result2)

      // === DIRECT EXPRESSION ===

      result3 <- directUse(userInput)
      if result3?
        stdout.println(result3)

      stdout.println("Purity + sanitization = forced defensive copies")

Common mistakes

E50060 — String does not have a copy() method. Use the copy constructor String(value) to create a defensive copy. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.

Incorrect:

cleanInput <- input.copy()

Correct:

cleanInput <- String(input)
Other ways to ask this
  • Why must pure functions use copy constructors with sanitized parameters?
  • How do purity and sanitization work together in EK9?
  • What is the copy constructor pattern for sanitized parameters?

Coming from another language?

Java: no purity concept, PreparedStatement for SQL safety. Python: no purity enforcement. Rust: ownership model prevents some aliasing. Go: no purity. Kotlin: no purity. Haskell: purity prevents side effects but no sanitization concept. EK9: purity and sanitization synergize, forcing defensive copy pattern.

Keywords: copy, pure, context, constructor, sanitize, protect, side-effect, defensive, function, immutable, purity, security, safe, sanitized