What can and cannot be called from a pure method?

← Purity Contracts · Ref: Q566

A pure method can only call other pure methods and functions. Calling a non-pure method from a pure context produces error E08130.

THE RULE

Pure can only call pure. If method A is pure, every method A calls must also be pure. This creates a trust chain: if the entry point is pure, the entire execution path is pure.

THE ERROR: E08130

Calling a non-pure function or method from within a pure context produces E08130: non-pure call in pure scope.

WHAT PURE CAN CALL

Other pure methods on the same class. Pure methods on other objects. Built-in pure operators (+, -, *, /, comparisons). Pure functions defined in the module.

WHAT PURE CANNOT CALL

Non-pure methods (E08130). Methods that use mutation operators internally. Functions not marked as pure.

I/O EXCEPTION

Stdout, Stderr, Stdin, and other I/O types carry the IO marker trait. Methods on IO types are allowed in pure context because I/O is considered a separate concern from data mutation.

BUILT-IN PURE METHODS

Most accessor methods on built-in types (length, upperCase, contains) are pure. Mutation methods (+=, append, etc.) are not.

See Q560 for pure method basics. See Q561 for purity in overrides. See Q273 for purity as security boundary. See Q54 for Consumer (pure) vs Acceptor (non-pure).

Example

defines module qa.purity.restrictions

  defines function

    //Pure function: only uses pure operations
    doubleValue() as pure
      -> number as Integer
      <- result as Integer: number + number

    //Pure function calling another pure function
    quadrupleValue() as pure
      -> number as Integer
      <- result as Integer: doubleValue(doubleValue(number))

  defines class

    Processor
      prefix <- String()

      Processor()
        -> prefix as String
        this.prefix: prefix

      //Pure method: calls only pure functions and methods
      transform() as pure
        -> text as String
        <- rtn as String: `${prefix}: ${text.upperCase()}`

      //Pure method calling pure function
      processNumber() as pure
        -> number as Integer
        <- rtn as String: `${prefix}: ${quadrupleValue(number)}`

      //Non-pure method: this is fine, just cannot be called from pure context
      logAndTransform()
        -> text as String
        <- rtn as String: `${prefix}: ${text.upperCase()}`

      default operator ?

  defines program

    PureMethodRestrictionsDemo()
      stdout <- Stdout()

      //Pure function calls
      stdout.println(`Double 5: ${doubleValue(5)}`)
      stdout.println(`Quadruple 5: ${quadrupleValue(5)}`)

      //Pure method calls
      proc <- Processor("APP")
      stdout.println(proc.transform("hello"))
      stdout.println(proc.processNumber(7))

      //Non-pure method call (allowed from program, which is non-pure)
      stdout.println(proc.logAndTransform("world"))

Common mistakes

E50001 — Renaming the variable means later references to 'proc' become unresolved, triggering E50001. See ek9 -h E50001 for details.

Incorrect:

procXYZ <- Processor("APP")

Correct:

proc <- Processor("APP")

E50001 — Referencing a variable 'procXYZ' that was never declared triggers E50001. The original variable was 'proc'. See ek9 -h E50001 for details.

Incorrect:

stdout.println(procXYZ.transform("hello"))

Correct:

stdout.println(proc.transform("hello"))
Other ways to ask this
  • What is E08130 non-pure call in pure scope?
  • Can a pure method call a non-pure method?
  • How does EK9 enforce pure call chains?

Coming from another language?

Java: no call chain purity enforcement. Kotlin: no purity tracking. Rust: no purity annotation on function calls. Haskell: pure functions cannot call IO without the monad. EK9: E08130 enforces pure-calls-pure chain, I/O types are exempted via IO trait.

Keywords: scope, pure, side-effect, call, chain, non-pure, migrate, immutable, purity, function, E08130, contract, restriction, trust