Question: Write a higher-order function that takes a predicate and counts matching items in a list.

How do I pass a function as a parameter in EK9?

Show me an abstract function used as a predicate with a dynamic function implementation.

Write a countMatching function that accepts a StringCheck predicate and a List of String.

In Java I'd use Predicate<String> — what is the EK9 pattern for higher-order functions?

Answer:
EK9 uses abstract functions as the equivalent of functional interfaces. Define the abstract function with its signature, then create concrete implementations as DYNAMIC FUNCTIONS using the syntax '() is AbstractName as function'.

ABSTRACT FUNCTION (the predicate type)
  StringCheck as abstract
    -> text as String
    <- rtn as Boolean?

DYNAMIC FUNCTION (the implementation)
  isLong <- () is StringCheck as function
    rtn: length text > LONG_THRESHOLD

Note: the dynamic function body uses the SAME parameter names as the abstract ('text', 'rtn'). Parameters are inherited, not redeclared.

HIGHER-ORDER FUNCTION (accepts the predicate)
  countMatching()
    -> items as List of String, predicate as StringCheck
    <- rtn as Integer: 0
    for item in items
      if predicate(item)
        rtn++

Call the predicate with normal function-call syntax: 'predicate(item)' returns Boolean.

See Q709 for built-in Predicate/Assessor types. See Q714 for Comparator. See Q1238 for strategy pattern using this approach.

defines module qa.functionsandmethods.predicatehigherorder

  defines constant

    LONG_THRESHOLD <- 5

  defines function

    StringCheck as abstract
      -> text as String
      <- rtn as Boolean?

    countMatching()
      ->
        items as List of String
        predicate as StringCheck
      <- rtn as Integer: 0
      for item in items
        if item?
          matched <- predicate(item)
          if matched
            rtn++

  defines program

    PredicateDemo()
      stdout <- Stdout()

      words <- List() of String
      words += "hi"
      words += "EK9-Language"
      words += "compiler"
      words += "go"
      words += "abstract-function"

      isLong <- () is StringCheck as function
        rtn: length text > LONG_THRESHOLD

      longCount <- countMatching(words, isLong)
      stdout.println(`Words longer than ${LONG_THRESHOLD}: ${longCount}`)

Java: Predicate<String> isLong = s -> s.length() > 5; items.stream().filter(isLong).count(). Kotlin: val isLong: (String) -> Boolean = { it.length > 5 }; items.count(isLong). Python: is_long = lambda s: len(s) > 5; sum(1 for s in items if is_long(s)). EK9: abstract function + dynamic function '() is StringCheck as function' + higher-order parameter passing.

abstract function
countMatching
delegate
dynamic function
function parameter
higher-order
predicate

higher-order functions
abstract function as predicate
dynamic function implementation

Explanation: Bare literal values in comparisons are not allowed — extract to a named constant. 'LONG_THRESHOLD <- 5' in a defines constant block. See ek9 -h E11064 for details.


Question: What makes an EK9 package publishable?

When does EK9 enforce capability declarations on my package?

What conditions trigger the publishable package rules in EK9?

How do I know whether my package is treated as publishable by the compiler?

I'm preparing to publish a package — what must be in the defines package block?

Answer:
An EK9 package is treated as 'publishable' — and therefore subject to compile-time capability enforcement — only when all FOUR of these are declared in the 'defines package' block:

  1. publicAccess (true for the public EK9 repository, false for a private repository)
  2. version (consumers need a specific version to reference)
  3. license (consumers need to know their legal obligations)
  4. capabilities (the list of gated system types the package uses)

All four must be present. A package that has publicAccess + version + license but omits the 'capabilities' block is NOT publishable in the enforcement sense — it compiles as if the feature did not exist, even when it uses gated types. This preserves backward compatibility for packages written before the capability feature landed and gives authors explicit control over when their module enters the enforcement regime.

The 'capabilities' declaration itself is the opt-in signal. Adding it is how an author says 'I am entering the supply chain and accepting the capability-review obligations.'

THE FOUR-CONDITION EXAMPLE
  defines package
    publicAccess <- true
    version <- 1.0.0-0
    description <- "Simple publishable library"
    license <- "MIT"
    capabilities <- ["org.ek9.lang::Stdout"]

WHY EXPLICIT OPT-IN
Existing published packages predate the capability feature. Forcing retroactive enforcement would break every existing library the day the feature landed. The fourth-condition rule means each package author decides when their module is ready to declare capabilities, upgrading on their own schedule rather than being blocked by a compiler change.

See Q1271 for how to declare capabilities once your package is publishable. See Q1275 for non-publishable packages. See Q1276 for pure-computation libraries. See Q1277 for error E12010.

defines module qa.packagecapabilities.publishablefourconditions

  defines package
    version <- 1.0.0-0
    description <- "Demonstrates the four publishable conditions"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout"]

  defines function

    showCapabilities()
      stdout <- Stdout()
      stdout.println("This module is publishable: all four conditions are set.")
      stdout.println("publicAccess + version + license + capabilities block")

Maven: package metadata is free-form; no declarative capability model. npm: package.json has no permission/capability concept; dependencies run arbitrary postinstall scripts. PyPI: no enforcement — package installs and runs arbitrary code. Cargo: same — no compile-time capability model. Go modules: no capability metadata at all. EK9: four-condition publishable rule activates compile-time capability enforcement per module, with explicit opt-in via the capabilities block.

capabilities
four conditions
license
package
publicAccess
publishable
supply chain
version

publishable package
four-condition rule
capability opt-in

Explanation: A publishable package declared only TCP as a capability but its code uses Stdout. Each gated type used in the module's non-dev source must be covered by a matching entry in the capabilities list. See ek9 -h E12010 for details.


Question: How do I declare package capabilities in EK9?

Show me the syntax for declaring capabilities in a defines package block.

Where do I put the capabilities list for a publishable package?

How do I tell the compiler my package uses Stdout or File?

What does a working capabilities declaration look like?

Answer:
The 'capabilities' declaration sits inside the 'defines package' block alongside 'version', 'license', and the other package properties. Each entry is a fully-qualified gated type name using the 'org.ek9.lang::' module path.

COMPACT SINGLE-LINE FORM
  capabilities <- ["org.ek9.lang::Stdout"]

MULTI-LINE FORM (for multiple entries)
  capabilities <- [
    "org.ek9.lang::Stdout",
    "org.ek9.lang::File",
    "org.ek9.lang::EnvVars"
    ]

BOTH FORMS COMPILE IDENTICALLY. Use the compact form for a single capability, the multi-line form for readability when you have several.

THE FULL EXAMPLE
  defines module my.publishable.library

    defines package
      publicAccess <- true
      version <- 1.0.0-0
      description <- "My library"
      license <- "MIT"
      capabilities <- ["org.ek9.lang::Stdout"]

    defines function
      sayHello()
        stdout <- Stdout()
        stdout.println("hello")

IMPORTANT RULES
1. Capabilities list entries MUST be non-empty. EK9 list literals do not accept '[]'. If you want a pure-computation library, omit the 'capabilities' block entirely — see Q1276.
2. Each gated type used in your non-dev source must be covered. The full gated type list appears in Q1273.
3. Grouped types count as one capability. Declaring 'org.ek9.lang::TCP' covers TCPConnection, TCPHandler, and NetworkProperties automatically.
4. Dev code (files under dev/) is exempt — see Q1278.

See Q1270 for the four-condition publishable rule. See Q1273 for the full gated types list. See Q1277 for what error E12010 means.

defines module qa.packagecapabilities.declaresyntax

  defines package
    version <- 1.0.0-0
    description <- "Shows a publishable package declaring Stdout capability"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout"]

  defines function

    sayHello()
      stdout <- Stdout()
      stdout.println("Hello from a publishable library")

Maven pom.xml: no capability concept. npm package.json: 'scripts' can run arbitrary code with no declared surface. Cargo.toml: no capability model. pyproject.toml: no enforcement. EK9: declarative 'capabilities <- [...]' list in 'defines package' is the single source of truth, enforced by the compiler at the module boundary.

capabilities
declare
gated type
org.ek9.lang
package
syntax

capability declaration syntax
capabilities list
publishable package

Explanation: The module uses Stdout but the capabilities list declares TCP. TCP does not cover Stdout — they are independent gated types in different groups. Declare 'org.ek9.lang::Stdout' to match the actual gated type the code references. See ek9 -h E12010.


Question: Why does EK9 require capability declarations for publishable packages?

What problem does EK9 capability security solve?

Why can't I just use any type in a published EK9 package?

What supply chain attack does EK9 capability enforcement prevent?

Explain the rationale for gated types in EK9.

Answer:
Every major software supply chain attack of the last decade shares the same kill chain: a compromised dependency quietly accesses system resources — reading environment variables, opening network connections, writing files — that its stated purpose does not require, and exfiltrates data. A JSON parser and a credential-stealing trojan look identical in their published metadata on npm, PyPI, Maven Central, or crates.io. Nothing in those ecosystems tells a consumer what a dependency will do at the system level.

EK9 closes the gap structurally at the compiler. Every publishable package must declare the gated system types it uses in a 'capabilities' list. The compiler verifies the declarations match the actual code: you cannot use 'Stdout', 'TCP', 'File', or 'EnvVars' in a publishable module without declaring the matching capability. When a previously-pure package suddenly adds 'org.ek9.lang::TCP' to its capabilities list, that change is visible to every consumer the moment they fetch the new version from the repository — before a single line of it executes.

THE TWO CAPABILITIES EVERY ATTACK NEEDS
  1. Reading secrets — EnvVars for API keys, File for credential files, Stdin for pipes
  2. Exfiltration — TCP for network sockets, HTTPRequest for REST calls, File for disk

EK9 gates both. A package that declares 'capabilities <- ["org.ek9.lang::TCP"]' has explicitly said 'I need network access' — a claim that consumers can evaluate against the package's advertised purpose. A package that declares no network capability literally cannot open a socket in its non-dev code; the compiler refuses to emit the bytecode.

MOST PACKAGES NEED ZERO CAPABILITIES
A JSON parser. A collection library. A string-manipulation utility. A mathematical package. None of them touch system resources — they should declare no capabilities, and the compiler enforces that pure-computation shape. When a legitimate library author updates their package they can be confident no accidental capability has crept in; when an attacker tries to insert one, the change is unmissable.

THE COST
A few extra lines in your 'defines package' block when you write a publishable library. The occasional compile error when you start using a new gated type and forget to declare it. That's the entire developer tax.

THE BENEFIT
Structural supply chain defence. No silent access escalation. No JSON parser that quietly reads your AWS credentials. No 'helper utility' that opens a reverse shell. The capability change is a compile error before it's a compromise.

See Q1270 for what makes a package publishable. See Q1271 for the capability declaration syntax. See Q1273 for the full gated types list. See Q1277 for error E12010.

defines module qa.packagecapabilities.whyrationale

  defines package
    version <- 1.0.0-0
    description <- "Shows a pure computation library that needs no capabilities"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout"]

  defines function

    explainWhy()
      stdout <- Stdout()
      stdout.println("Capability declarations make supply chain attacks structurally visible.")
      stdout.println("When a new version declares org.ek9.lang::TCP, every consumer sees it before it runs.")

npm (2015-2025): dozens of typosquatted and maintainer-hijack attacks; no capability model. PyPI (2022-2025): credential-stealing packages active for days before detection; no compile-time defence. Maven Central: no runtime permission enforcement beyond Java SecurityManager (deprecated). crates.io: safe Rust but no capability metadata. Go modules: no capability concept. EK9: compile-time capability enforcement at the module boundary — the compiler refuses to emit bytecode for undeclared gated type usage.

attack
capability
defence
gated type
rationale
security
supply chain
why

supply chain security
capability rationale
attack prevention


Question: What is a 'gated type' in EK9 and which types are currently gated?

Which EK9 types require a capability declaration in publishable packages?

Show me the list of gated types and their groupings.

Do I need to declare TCPConnection separately from TCP?

What are all the system types the compiler considers gated?

Answer:
A GATED TYPE is a built-in EK9 type that represents access to a system resource — network sockets, filesystem, environment variables, standard streams, clocks, signals, and so on. Every gated type is defined in 'org.ek9.lang' and requires a matching entry in a publishable package's 'capabilities' list.

The full gated-type list (from GatedTypeMapping.java) groups related types under a single primary capability. Declaring the primary automatically covers the related types.

NETWORK
  org.ek9.lang::TCP           covers TCP, TCPConnection, TCPHandler
  org.ek9.lang::UDP           covers UDP, UDPPacket
  org.ek9.lang::NetworkProperties — covered by declaring either TCP or UDP

HTTP
  org.ek9.lang::HTTPRequest
  org.ek9.lang::HTTPResponse

FILESYSTEM
  org.ek9.lang::File
  org.ek9.lang::TextFile
  org.ek9.lang::FileSystem
  org.ek9.lang::FileSystemPath

STANDARD STREAMS
  org.ek9.lang::Stdin
  org.ek9.lang::Stdout
  org.ek9.lang::Stderr

SYSTEM AND ENVIRONMENT
  org.ek9.lang::EnvVars
  org.ek9.lang::OS
  org.ek9.lang::Signals

TIME AND CLOCKS
  org.ek9.lang::Clock
  org.ek9.lang::SystemClock

SENSITIVE DATA
  org.ek9.lang::Sensitive

GROUPING RULES
Declaring the primary type covers all associated types in its group. For example, 'capabilities <- ["org.ek9.lang::TCP"]' is sufficient to use TCPConnection, TCPHandler, and NetworkProperties in the module. You do not need to list each related type individually.

NetworkProperties is a special case: it is shared between TCP and UDP, so declaring either one covers it.

MULTI-CAPABILITY EXAMPLE
A package that uses HTTP, writes to stdout, and reads environment variables declares three capabilities:
  capabilities <- [
    "org.ek9.lang::HTTPRequest",
    "org.ek9.lang::HTTPResponse",
    "org.ek9.lang::Stdout",
    "org.ek9.lang::EnvVars"
    ]

WHAT IS NOT GATED
Everything else — String, Integer, Float, Boolean, Date, DateTime, List, Dict, Optional, Result, and every user-defined type. Pure-computation code uses only these and needs no capability declarations.

See Q1271 for declaration syntax. See Q1272 for the rationale. See Q1277 for error E12010. See Q1276 for pure-computation libraries that need no capabilities.

defines module qa.packagecapabilities.gatedtypeslist

  defines package
    version <- 1.0.0-0
    description <- "Publishable package demonstrating multiple gated type declarations"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout", "org.ek9.lang::Stderr"]

  defines function

    useBothStreams()
      stdout <- Stdout()
      stderr <- Stderr()
      stdout.println("standard output goes here")
      stderr.println("error output goes here")

Java SecurityManager (deprecated): runtime permission checks via AccessController; no compile-time visibility. .NET Code Access Security (deprecated): similar runtime-only model. Rust: no standard-library capability model; unsafe blocks flag memory safety but not I/O. Go: no capability metadata. Haskell IO monad: types carry effects but no package-level declaration. EK9: compile-time gated-type list with grouped related types, each declared explicitly in the package block.

EnvVars
File
Stdout
TCP
gated type
grouping
list
org.ek9.lang

gated types
capability groupings
system resource types

Explanation: The module uses both Stdout and Stderr. Declaring only Stdout leaves the Stderr usage uncovered and fires E12010. Each gated type used must have a matching entry — declaring one gated type does not cover any other. See ek9 -h E12010.


Question: Does my EK9 package inherit its dependencies' capability declarations?

If I depend on a package that uses Stdout, do I also need to declare Stdout?

Does EK9 check that my dependencies' capabilities are covered in my package?

Are capability declarations transitive through dependencies in EK9?

When I call a library function that uses TCP internally, do I need to declare TCP in my own package?

Answer:
No — EK9 capability enforcement is STRICTLY PER-MODULE. Each publishable package declares only its own capabilities, and the compiler verifies those declarations against the package's own non-dev source code. Depending on another package does not inherit that package's capabilities, and the compiler performs no cross-module capability reasoning.

THE RULE
Your 'capabilities' list must cover every gated type that appears in YOUR OWN code — meaning every type your source file directly mentions, not types that dependencies happen to use internally. If you call a library function that returns an Integer, the type your code mentions is Integer; whether the function uses Stdout internally is the library's business, not yours.

THE ONE CASE WHERE IT APPLIES
If a dependency's public API takes or returns a gated type — for example, a function signature like 'makeConnection() as pure <- rtn as TCPConnection' — and your code calls that function and stores the result, then YOUR code mentions TCPConnection. TCPConnection is grouped under TCP, so your package must declare 'org.ek9.lang::TCP'. This is not transitive inheritance; it is the normal per-module rule applied to a cross-module reference: the type appears in your AST, so you must declare it.

WHY PER-MODULE, NOT TRANSITIVE
The compiler's responsibility stays tight and local: one question per module, no cross-module reasoning, no acknowledgment maps to maintain. Visibility into what dependencies declare is a repository/tooling concern — you can inspect any dependency's 'defines package' block in its source to see its capabilities, and the EK9 repository surfaces capability changes between versions for human review at upgrade time.

The trade-off is that a dependency silently adding a new capability does not automatically break consumer builds. Instead, the upgrade workflow handles it: you review what's changed when you pull the new version, not when you rebuild.

EXAMPLE
  // Library: declares Stdout, uses it internally, exports pure functions
  defines module my.library
    defines package
      publicAccess <- true
      version <- 1.0.0-0
      license <- "MIT"
      capabilities <- ["org.ek9.lang::Stdout"]
    defines function
      compute() as pure
        -> n as Integer
        <- rtn as Integer: n * 2
      internalLog()
        stdout <- Stdout()
        stdout.println("log")

  // Consumer: calls compute(), never references Stdout itself
  // Only needs its own capabilities — nothing to inherit from my.library
  defines module my.consumer
    references
      my.library::compute
    defines package
      publicAccess <- true
      version <- 1.0.0-0
      license <- "MIT"
      capabilities <- ["org.ek9.lang::Stderr"]
    defines function
      useCompute()
        stderr <- Stderr()
        result <- compute(21)
        stderr.println(`Got: ${result}`)

The consumer does NOT declare Stdout even though 'my.library' uses it internally. Only the types the consumer's own source mentions (Stderr, Integer, String) matter for its own capability check.

See Q1270 for the four-condition publishable rule. See Q1271 for declaration syntax. See Q1273 for the gated types list.

defines module qa.packagecapabilities.permoduleenforcement

  defines package
    version <- 1.0.0-0
    description <- "Demonstrates per-module capability enforcement"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout"]

  defines function

    pureHelper() as pure
      -> n as Integer
      <- rtn as Integer: n * 2

    usesOwnCapability()
      stdout <- Stdout()
      result <- pureHelper(21)
      stdout.println(`Result: ${result}`)

Maven transitive dependencies: resolved automatically with no capability awareness. npm audit: flags CVEs but not capability changes. Cargo features: compile-time but orthogonal to permissions. Java jlink modules: declare service use but not system access. EK9: per-module capability enforcement, no transitive propagation — each module stands on its own declarations.

capability dependency enforcement inheritance per-module reference transitive

per-module enforcement
non-transitive capabilities
cross-module references

Explanation: This module's code uses Stdout directly. Declaring Stderr instead does not cover Stdout — each gated type is checked independently against the module's own source. This illustrates per-module enforcement: even if a dependency declared Stdout, the consumer must still declare its own. See ek9 -h E12010.


Question: Do non-publishable EK9 packages need capability declarations?

Can I use Stdout and File freely in a local project that I'm not publishing?

When is my package exempt from capability enforcement?

Does the capability rule apply to scripts and local experiments?

What happens if my package block has only a description?

Answer:
No — non-publishable packages are entirely exempt from capability enforcement. You can freely use Stdout, File, TCP, EnvVars, or any other gated type without declaring them in a 'capabilities' block.

A package is NON-PUBLISHABLE when any of the four publishable conditions is missing:
  - publicAccess is not declared
  - version is not declared
  - license is not declared
  - capabilities block is not present

When any of these is missing, the compiler does not activate capability enforcement for the module. This matches the reality that local scripts, developer experiments, file-organisation wrappers, and pure dependency-management 'defines package' blocks are not entering a supply chain. The developer's own machine, the developer's own risk.

COMMON NON-PUBLISHABLE SHAPES

1. Description only — just naming the package for organisation:
  defines package
    description <- "My local project"

2. File organisation with applyStandardIncludes:
  defines package
    description <- "Collected local utilities"
    applyStandardIncludes <- true

3. Dependency management without publishing intent:
  defines package
    description <- "Pulls in external libraries for local use"
    deps <- {
      "ekopen.math.simple.constants": "2.3.14-0"
      }

All three compile freely and may use any gated types in their source code. The compiler does not run the capability check because 'publishable' evaluates to false.

THE TRANSITION POINT
The moment you declare publicAccess, version, license, AND a capabilities block, your package opts into enforcement. That is the single moment a module 'enters the supply chain' in EK9's model, and the only moment the compiler starts asking 'is this gated type covered by a declared capability?' Until then, you are writing ordinary code on your own machine and nothing is gated.

WHY THE EXEMPTION EXISTS
Forcing capability declarations on every package — including scripts, experiments, and throwaway builds — would create enormous friction for zero security benefit. The attacks capability security is designed to stop are supply-chain attacks: compromised dependencies that end up in other people's builds. Code that never leaves your machine cannot be a supply chain attack. The exemption respects that reality.

See Q1270 for the four publishable conditions. See Q1276 for pure-computation publishable libraries. See Q1278 for the dev/ test exemption within publishable packages.

defines module qa.packagecapabilities.nonpublishable

  defines package
    description <- "Non-publishable local package — capabilities not required"

  defines function

    freelyUseStdout()
      stdout <- Stdout()
      stdout.println("This module has no publicAccess, no version, no license.")
      stdout.println("It is not publishable, so capability enforcement is not active.")

    freelyUseStderr()
      stderr <- Stderr()
      stderr.println("Stderr is also freely usable here.")

Python virtualenv: local experiments run arbitrary code; no package-level policy. Node.js local package.json: same. Rust workspaces: local members can use any std APIs. Java local projects: no security manager by default. EK9: non-publishable packages explicitly exempt — capability enforcement activates only at the publishable threshold, which is deliberately opt-in.

dependency management
exemption
experiment
local
non-publishable
publishable
script

non-publishable exemption
local package
no-capability required


Question: How do I write a pure-computation publishable library in EK9?

How do I publish a library that uses no system resources?

What is the idiom for a JSON parser or maths library that doesn't need capabilities?

Can I publish a package without declaring any capabilities?

How do I say 'this library is pure computation' in the package block?

Answer:
For a publishable library that touches no system resources — a JSON parser, a string-manipulation utility, a collection library, a mathematical package — the idiom is simple: OMIT THE 'capabilities' BLOCK ENTIRELY.

Without a 'capabilities' declaration, the fourth publishable condition is not met, so the compiler does not activate capability enforcement for the module. The package compiles exactly as it would have before the capability feature existed. Users pulling the library can see from its package block that no 'capabilities' declaration is present and therefore the library author has not entered the capability enforcement regime — which is fine, because the library genuinely uses no gated types.

THE PURE COMPUTATION PATTERN
  defines module com.example.jsonparser

    defines package
      publicAccess <- true
      version <- 2.1.0-0
      description <- "Pure JSON parsing library"
      license <- "MIT"
      //No 'capabilities' block: pure computation, no gated types used.

      deps <- {
        "org.ek9.collections": "1.0.0-0"
        }

    defines function
      parseJson() as pure
        -> input as String
        <- rtn as JSON: JSON()
        //Pure parsing logic — only String, JSON, and collection types

WHY NOT USE 'capabilities <- []'?
EK9 list literals currently require at least one element, so 'capabilities <- []' does not parse. The design intent of an empty list — 'this package is explicitly pure computation' — is achieved instead by omitting the block, which has the same practical effect: no enforcement, no gated types needed, no supply-chain risk.

WHEN TO ADD A 'capabilities' BLOCK
As soon as your library legitimately needs to touch a system resource — for example, a networking library that needs TCP, or a logging utility that needs Stdout — add the 'capabilities' block with the required entries. The moment the block exists, your module enters enforcement: every gated type in its code must be covered.

WHAT CONSUMERS SEE
A consumer who fetches a pure-computation library sees its 'defines package' block and immediately knows (a) the library is published and versioned and (b) the author has not declared any capabilities — meaning they have not opted into enforcement. A careful consumer can verify by reading the library's source that no gated types are actually used. A pure-computation library has an audit story that matches its claim.

See Q1270 for the four-condition publishable rule. See Q1271 for declaration syntax. See Q1272 for the supply chain rationale. See Q1275 for non-publishable packages (which also skip enforcement but for a different reason — they aren't published at all).

defines module qa.packagecapabilities.purecomputation

  defines package
    publicAccess <- true
    version <- 1.0.0-0
    description <- "Pure computation library — no capabilities block declared"
    license <- "MIT"

  defines function

    doubleValue() as pure
      -> input as Integer
      <- rtn as Integer: input * 2

    concatenate() as pure
      ->
        prefix as String
        suffix as String
      <- rtn as String: `${prefix}${suffix}`

    isPositive() as pure
      -> candidate as Integer
      <- rtn as Boolean: candidate > 0

Maven/pom.xml: no way to declare 'this library is pure computation.' npm: no equivalent. Cargo: no equivalent. Haskell: pure functions are tracked by the type system but package-level purity is not distinguished. EK9: pure-computation publishable libraries omit the capabilities block entirely; audit is trivial because the absence of the declaration matches the absence of gated type usage.

JSON
computation
library
mathematical
no capabilities
omit
parser
pure

pure computation
library without capabilities
omitting the capabilities block


Question: What does EK9 error E12010 mean and how do I fix it?

I'm getting E12010 UNDECLARED_CAPABILITY — what does this error mean?

The compiler says a type requires a capability declaration — how do I add it?

How do I fix 'type requires declared package capability' errors?

Answer:
E12010 fires when your publishable package uses a gated system type but has not declared the corresponding capability. The error message looks like:

  Error : E12010: 'Stdout' on line 14 position 16: 'Stdout' requires capability 'org.ek9.lang::Stdout': type requires declared package capability

The fix is always the same: add the required capability to the 'capabilities' list in your 'defines package' block. The error message tells you exactly which capability entry is missing.

THE SCENARIO
Your package meets all four publishable conditions (publicAccess + version + license + capabilities block), so capability enforcement is active. Somewhere in your non-dev source you reference a type that is gated — Stdout, File, TCP, EnvVars, or any other — and your capabilities list does not include it (or its primary-type grouping).

THE FIX
Add the required entry to the capabilities list. For Stdout:
  capabilities <- ["org.ek9.lang::Stdout"]

For multiple gated types, use the multi-line form:
  capabilities <- [
    "org.ek9.lang::Stdout",
    "org.ek9.lang::Stderr",
    "org.ek9.lang::File"
    ]

For grouped types, declare the primary: 'org.ek9.lang::TCP' covers TCPConnection, TCPHandler, and NetworkProperties automatically.

COMMON TRIGGERS
1. Using Stdout/Stderr/Stdin for basic I/O in a publishable library without declaring the stream capabilities.
2. Reading environment variables via EnvVars without declaring 'org.ek9.lang::EnvVars'.
3. Declaring TCPConnection as a field type instead of declaring the primary TCP capability.
4. Using File for persistence without 'org.ek9.lang::File' in the list.
5. Declaring some capabilities but missing one — each gated type needs its own entry (or its primary grouping).

EACH GATED TYPE COUNTS INDEPENDENTLY
Declaring Stdout does not cover Stderr. Declaring TCP does not cover File. The compiler reports a separate E12010 for each undeclared gated type it finds, so fixing one does not mask the others.

ALTERNATIVES TO ADDING A CAPABILITY
1. If the code that uses the gated type is actually test or fixture code, move it under a 'dev/' directory — dev code is exempt from capability enforcement (see Q1278).
2. If the package does not actually need to be publishable yet, remove one of the publishable conditions (publicAccess, version, license, or the capabilities block itself) and the enforcement will deactivate. This is the right move for local packages and experiments (see Q1275).
3. If the code shouldn't use the gated type at all — maybe an accidental Stdout in a pure-computation library — remove the usage instead of declaring the capability. The clean declaration 'this library needs no capabilities' is preserved (see Q1276).

See Q1270 for publishable conditions. See Q1271 for declaration syntax. See Q1273 for the full gated types list.

defines module qa.packagecapabilities.e12010fix

  defines package
    version <- 1.0.0-0
    description <- "Correctly declares all capabilities used — E12010 would have fired without these"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout", "org.ek9.lang::Stderr"]

  defines function

    writeInfo()
      stdout <- Stdout()
      stdout.println("Info message on stdout")

    writeError()
      stderr <- Stderr()
      stderr.println("Error message on stderr")

Java runtime SecurityException: similar in spirit but runtime-only, deprecated, and covers a narrower surface. .NET permissions: also runtime, similarly deprecated. Rust compile-time enforcement: limited to memory safety, not I/O capability. EK9 E12010: compile-time gated type check with immediate fix guidance from the rich error message and QA cross-references.

E12010
UNDECLARED_CAPABILITY
error
fix
gated type
missing capability

E12010 error
fixing capability errors
compiler enforcement

Explanation: The module uses both Stdout and Stderr. Declaring only Stdout leaves the Stderr usage uncovered — the compiler reports a separate E12010 for each undeclared gated type. Add 'org.ek9.lang::Stderr' to cover it. See ek9 -h E12010.


Question: Are EK9 dev/ test files exempt from capability enforcement?

Do my test files need capability declarations?

Can test code use Stdout and File freely without adding capabilities?

Why are dev/ directories treated specially for capability checks?

Where do I put test fixtures that use gated types?

Answer:
Yes — code under a 'dev/' directory is EXEMPT from capability enforcement. Test files, fixtures, and development-time utilities living under dev/ may freely use any gated type without declaring the matching capability in the package's 'capabilities' list.

THE RULE
The capability check in 'CapabilityTypeReferenceOrError.checkNode' early-returns when 'symbolsAndScopes.isDevSource()' is true. Dev sources are identified by the directory structure: anything under 'dev/' is flagged as dev source and skipped during capability enforcement.

PRACTICAL EFFECT
  my.publishable.library/
    main.ek9                // publishable, subject to capability rules
    helper.ek9              // publishable, subject to capability rules
    dev/
      tests.ek9             // EXEMPT — can use Stdout, File, EnvVars freely
      testFixtures.ek9      // EXEMPT

The main.ek9 and helper.ek9 must declare every gated type they use. tests.ek9 can import, call, assert, print to stdout, read environment variables, open test files — whatever the tests need — without any of those types appearing in the capabilities list.

WHY THE EXEMPTION EXISTS
Tests naturally need access to system resources in ways that production code does not:
  - Reading fixture files from the filesystem
  - Mocking environment variables
  - Capturing stdout/stderr to verify output
  - Constructing instances of types that wrap TCP, File, or Sensitive data

If dev/ code had to declare every gated type used in test fixtures, test suites would force the production 'capabilities' list to grow to include every test-only dependency. That would defeat the purpose of the rule: the production code's capability declaration would no longer be a trustworthy claim about what the library does at runtime — it would be contaminated by test-fixture requirements.

Separating test code into 'dev/' and exempting it means:
  - Production capability declarations stay clean and accurate
  - Tests remain free to exercise edge cases without ceremony
  - Consumers auditing a library's capabilities see only what the production code needs

WHAT COUNTS AS DEV CODE
Any file whose path includes a 'dev' directory segment (at the package root or nested). The EK9 project layout convention is to keep tests under 'dev/' directly inside the package directory, alongside the main source files.

WHAT IS NOT EXEMPT
  - Files NOT under dev/ — regardless of whether they contain tests, fixtures, or development-time code. If a file is in the main source tree and the package is publishable, it goes through capability enforcement.
  - Files under dev/ in non-publishable packages — but this doesn't matter, because non-publishable packages are entirely exempt regardless of directory (see Q1275).

HOW THIS INTERACTS WITH THE TEST RUNNER
EK9's built-in test runner ('ek9 -t') discovers and executes tests in the 'dev/' directory. The test runner itself is not bound by the library's capability declarations — it operates at a higher trust level because it is explicitly invoked by the developer to exercise their own code, not by a downstream consumer pulling a dependency.

See Q1270 for the four-condition publishable rule. See Q1275 for non-publishable exemption. See Q1271 for capability declaration syntax.

defines module qa.packagecapabilities.devexemption

  defines package
    version <- 1.0.0-0
    description <- "Shows main code declaring only what it uses; dev/ tests would be exempt"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout"]

  defines function

    productionHelper()
      stdout <- Stdout()
      stdout.println("This is production code — must declare its own capabilities")

Maven/Gradle test scope: dependencies in 'test' scope are not packaged with the production artifact but have no runtime capability implications. npm devDependencies: similar, only controls what's installed. Python dev-dependencies: same pattern. EK9: dev/ directory is a structural boundary for capability enforcement — dev code is not part of the published supply chain and is exempt from the rule.

dev
development
exemption
fixture
test
test runner

dev code exemption
test files
development-time code


Question: How should an AI assistant handle EK9 package capability changes?

Can ek9 -ai autonomously approve a new capability declaration?

Should an AI coder add capabilities to a package without asking?

What is the human-in-the-loop requirement for capability changes?

Why can't an AI just fix E12010 by adding the required capability?

Answer:
An AI assistant MUST NOT autonomously approve or add capability declarations when working with publishable EK9 packages. Every change to a package's 'capabilities' list — whether adding a new entry, modifying an existing one, or introducing the block for the first time — requires human review and explicit human approval before it is committed.

THE RULE
Capability declarations are the supply chain trust boundary. When a publishable package gains a new capability, every downstream consumer is implicitly accepting increased risk: the dependency can now touch a system resource it previously could not. The value of the capability system depends on that decision being made by a human who understands the trade-off, not by an automated agent that just wanted E12010 to go away.

An AI that mechanically adds 'org.ek9.lang::TCP' to a package's capabilities list because the compiler complained has performed a silent supply chain escalation. A human reviewer would ask 'why does this JSON parser suddenly need network access?' The AI does not.

THE CORRECT AI BEHAVIOUR

1. WHEN E12010 FIRES in non-publishable or dev code:
   - The AI may freely fix the error by adjusting the capabilities list, because non-publishable packages and dev sources are not part of the supply chain.

2. WHEN E12010 FIRES in a publishable package:
   - The AI must STOP and escalate to the human. The appropriate response is something like:
     'The publishable package needs a new capability declaration to compile. Adding capabilities to a publishable package is a supply chain decision I should not make autonomously. Please review whether this library should actually use org.ek9.lang::TCP, and if so, approve the change.'
   - The AI may propose the fix as a diff for human review, but must not apply it.

3. WHEN AN EXISTING CAPABILITY WOULD BE REMOVED:
   - Removing a capability is also a decision that affects trust but in the opposite direction. An AI may suggest removal when it is reducing scope, but the human should confirm — especially for libraries that are already published.

4. WHEN THE CAPABILITY IS CLEARLY ACCIDENTAL:
   - If the AI can see that a Stdout usage is leftover debug code in a library that is meant to be pure computation, the appropriate fix is to REMOVE the debug code, not to add the capability. The AI should propose the removal and explain the reasoning.

WHY THIS MATTERS FOR THE ek9 -ai TOOL
The 'ek9 -ai' framework is designed as a delegated-agent development loop — a local AI coder handling routine tasks with Frontier escalation for complex ones. Capability declarations are one of a small number of decisions where the Frontier (and ultimately the human) must be in the loop even for otherwise-trivial changes. The AI's value proposition is in writing correct idiomatic EK9 code; it is not to decide what system resources a published library is allowed to access.

A SLEEPER AGENT SCENARIO
Consider: an AI assistant is asked to fix a failing test in a published library. The fix happens to involve code that touches a new gated type. If the AI autonomously adds the capability, commits the change, and publishes the new version, the library has gained a new system access point without any human noticing. Every consumer pulling the new version inherits the escalation silently. This is exactly the attack vector that capability security is designed to prevent. AI automation would reintroduce it.

THE CORRECT RESPONSE is for the AI to surface the decision: 'This change would require declaring org.ek9.lang::TCP as a new capability. This is a supply chain escalation. Please review.' The human approves or rejects. The audit trail survives.

See Q1270 for the publishable rule. See Q1272 for the supply chain rationale.

defines module qa.packagecapabilities.aihumanreview

  defines package
    version <- 1.0.0-0
    description <- "Publishable package — AI must not autonomously modify its capabilities list"
    license <- "MIT"
    publicAccess <- true
    capabilities <- ["org.ek9.lang::Stdout"]

  defines function

    safeToMaintain()
      stdout <- Stdout()
      stdout.println("An AI may edit this function freely — the capabilities list is unchanged.")

    alsoSafe() as pure
      -> n as Integer
      <- rtn as Integer: n + 1

GitHub Copilot, Cursor, Claude Code, Amazon Q: none of these distinguish supply-chain-critical changes from routine edits. Any AI that can write code can silently escalate permissions in a package manifest. EK9: capability changes are a designated human-review boundary; the ek9 -ai framework treats them as non-delegable decisions regardless of how routine the surrounding code change appears.

AI assistant
autonomous
capability
ek9 -ai
escalation
human review
sleeper agent
supply chain

AI human-in-the-loop
capability change review
supply chain safety


Question: What does EK9 error E04050 mean when I declare a class field and how do I fix it?

I'm getting E04050 'not expecting complex expression' on a class field — what's wrong?

Why does 'names <- List() of String?' fail to compile in a class body?

How do I initialize a List field in an EK9 class without E04050?

The compiler says my field declaration needs a simple aggregate — how do I fix it?

Answer:
E04050 fires when you use the '<-' declaration form for a class field with an expression the compiler cannot resolve to a simple type during early phase processing. In practice the usual cause is writing the is-set operator '?' at the end of a parameterised constructor call, like 'names <- List() of String?'. That is NOT a nullable-type marker in EK9 — it is the is-set operator being invoked on the newly-created List, which produces a Boolean, which cannot be the type of a List field. The compiler reports E04050 because type inference cannot reconcile 'List of String' with 'Boolean'.

THE FIX
Drop the trailing '?' and use the simple parameterised constructor form:

  names <- List() of String

This declares a class field 'names' of type 'List of String' and initialises it to an empty list. The List type is always 'set' when created, so you do not need an is-set check on it.

WHY THIS CATCHES PEOPLE
C#, Kotlin, TypeScript, and Swift all use a trailing '?' on a type to mean 'nullable'. EK9 has no null. '?' in EK9 is the is-set operator — it is a unary postfix method that returns a Boolean telling you whether the object it is called on currently holds a meaningful value. Writing 'List() of String?' therefore means 'construct an empty List, then ask whether it is set' — not 'a List field that might be unset'.

FIELD DECLARATION FORMS THAT WORK
  names <- List() of String                 // empty List of String
  scores <- Dict() of (String, Integer)     // empty Dict
  tags <- ["alpha", "beta", "gamma"]         // List literal (type inferred)
  counters as Integer: 0                    // explicit type + initial value
  label as String: "default"                // explicit type + literal

FIELD DECLARATION FORMS THAT FAIL WITH E04050
  names <- List() of String?                // '?' invoked on the List
  result <- compute() + 10                  // arithmetic
  choice <- flag ? valueA : valueB          // no ternary in EK9 anyway

WHEN IN DOUBT USE EXPLICIT TYPING
If the field's type is at all complex, abandon '<-' and use 'name as Type: value'. The explicit form tells the compiler the type up front and lets full expression processing happen later:

  total as Float: price * quantity    // explicit type survives complex expr

See Q585 for field initialisation and constructor delegation. See Q104 for why EK9 requires explicit constructors on some uninitialised fields.

defines module qa.classesandoop.e04050fieldfix

  defines class

    UniqueNames
      names <- List() of String

      add()
        -> name as String
        if not names contains name
          names += name

      count() as pure
        <- rtn as Integer: length names

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

      operator $ as pure
        <- rtn as String: `UniqueNames(${length names})`

  defines program

    E04050FieldFixDemo()
      stdout <- Stdout()

      names <- UniqueNames()
      names.add("Alice")
      names.add("Bob")
      names.add("Alice")
      stdout.println($names)

C#/Kotlin: 'List<string>?' means nullable List — a valid type. EK9: 'List() of String?' means 'call ? on the newly-created List' — produces Boolean, fails type inference. If you want a field that might be unset in EK9, the collection itself is always set (empty List IS valid); the question 'is this field populated' is answered by '?' on the field at the point you check it, not in the type declaration.

E04050
List
complex expression
field declaration
is-set operator
nullable type
type inference

E04050 error
class field initialization
simple aggregate constructor

Explanation: The trailing '?' invokes the is-set operator on the constructed List, producing a Boolean. Type inference cannot reconcile that with a List-of-String field. Drop the '?' — EK9 has no nullable types, and the List itself is always set once created.


Question: When should I use 'name as Type: value' instead of 'name <- value' for a class field to avoid E04050?

What's the difference between '<-' and 'as Type:' for class fields in EK9?

How do I initialize a class field with a complex expression without E04050?

When is explicit type annotation required in EK9 class field declarations?

Why does 'total <- price * quantity' fail in a class body?

Answer:
EK9 offers two forms for initialising a class field and they are NOT interchangeable. Pick the right one for the expression you are writing and E04050 disappears.

FORM 1 — '<-' INFERRED DECLARATION
The compiler infers the field type from a simple constructor call, a literal, or a list/dict literal. This runs at phase 2, BEFORE full expression processing.

  count <- 0                                  // Integer
  tags <- ["alpha", "beta"]                   // List of String
  config <- {"timeout": 30, "retries": 3}     // Dict of (String, Integer)
  items <- List() of String                   // empty List of String
  scores <- Dict() of (String, Integer)       // empty Dict

FORM 2 — 'as Type: value' EXPLICIT DECLARATION
You state the type up front, and the initialiser can be any expression. This tells the compiler the field's type without needing to resolve the initialiser first, so complex expressions are allowed.

  totalPrice as Float: basePrice * quantityFactor
  discount as Float: Float(0.0)
  label as String: `Item ${itemId}`

WHICH FORM TO USE
If the initialiser is a simple constructor call or a literal, use '<-'. If the initialiser is ANY of the following, use 'as Type:' instead:
- Arithmetic or string interpolation involving other variables
- A method call on an existing object
- A function call that returns a value
- A conditional or multi-branch expression

THE E04050 TRAP
The natural instinct 'just use <- everywhere' fails on expressions like:

  total <- price * quantity                   // E04050 — arithmetic
  label <- `Item ${itemId}`                   // E04050 — interpolation
  head <- items.first()                       // E04050 — method call

The fix is mechanical: change '<-' to 'as <inferred-type>:' with no other edits.

  total as Float: price * quantity            // OK
  label as String: `Item ${itemId}`           // OK
  head as String: items.first()               // OK

See Q585 for field initialisation rules and Q1282 for the specific is-set-operator trap.

defines module qa.classesandoop.e04050explicittype

  defines class

    PriceTag
      unitPrice as Float: Float()
      itemId as String: String()

      PriceTag()
        ->
          unitPrice as Float
          itemId as String
        this.unitPrice :=: unitPrice
        this.itemId :=: itemId

      label() as pure
        <- rtn as String: `Item ${itemId} @ ${unitPrice}`

      override operator ? as pure
        <- rtn as Boolean: unitPrice? and itemId?

      operator $ as pure
        <- rtn as String: label()

  defines program

    E04050ExplicitTypeDemo()
      stdout <- Stdout()

      tag <- PriceTag(19.99, "WIDGET-001")
      stdout.println($tag)

Java/Kotlin/Swift all allow 'var total = price * quantity' in a class body — the inferrer runs after full expression resolution. EK9 field type inference runs earlier in the pipeline, so '<-' only works for simple constructors and literals. For anything else, EK9 asks you to state the type with 'as Type: value'. One character of annotation, complete clarity for the compiler.

E04050
as Type
class field
explicit type
field initialisation
type inference

E04050 error
class field declaration forms
when explicit typing is required

Explanation: Using '<-' with an arithmetic expression fails E04050 because type inference cannot resolve the expression type during phase 2. Use 'as Type: expression' instead — the explicit type annotation lets the compiler know the field's type without resolving the initialiser first.


Question: What does EK9 error E06060 mean on a generic class constructor and how do I fix it?

I'm getting E06060 'generic type does not support private or protected constructors' — what's wrong?

Why can't I make my generic class constructor private in EK9?

How do I write a generic class constructor that compiles without E06060?

The compiler says my parameterised type constructor must be public — why?

Answer:
E06060 fires when a generic type (a class declared as 'Name of type T' or 'Name of type (T, U)') has a PROTECTED constructor, or a PRIVATE PARAMETERISED constructor. The compiler reports this at phase 1 with a message like:

  Error : E06060: 'Container' on line 14 position 22: a generic type does not support private or protected constructors

The rule is nuanced, not absolute: the PARAMETERISED (inferred) constructors must be public so the compiler can infer type arguments and instantiate from any context. The no-argument DEFAULT constructor may be 'default private'. Protected is never allowed.

THE FIX (private parameterised constructor)
Make the parameterised constructor public — public is the default, so drop the access modifier:

  Container() as pure
    -> initialItem as T
    item :=? initialItem

GIVE THE OPTIONAL FIELD AN UNSET VALUE, NOT NULL
A conceptual field 'item as T?' must not be left null by the no-arg constructor — give it an unset 'T()':

  Container() as pure
    item :=? T()

THE 'default private' ESCAPE HATCH IS ALLOWED ON GENERICS
The 'default private Container() as pure' idiom from regular classes DOES work on a generic — use it when the field cannot be given an unset 'T()' (T may be abstract, may hold a function, or may have no public no-arg constructor). External code then cannot create an empty Container; it must pass a value:

  Container of type T
    item as T?
    default private Container() as pure       // allowed
    Container() as pure
      -> initialItem as T
      item :=? initialItem

WHY PARAMETERISED CONSTRUCTORS STAY PUBLIC
Generic types are reused with ANY valid type parameter; the compiler monomorphises each parameterisation (Container of String, Container of Integer, …). Type inference and instantiation need the parameterised constructor reachable from every call site, so it must be public. The no-arg default has no inference role, so it may be private.

See Q645 for the full rationale and the class-level rule comparison.

defines module qa.genericsdeep.e06060fix

  defines class

    Container of type T
      item as T?

      Container() as pure
        item :=? T()

      Container() as pure
        -> initialItem as T
        item :=? initialItem

      getItem() as pure
        <- rtn as T: item

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

      operator $ as pure
        <- rtn as String: $item

  defines program

    E06060FixDemo()
      stdout <- Stdout()

      stringContainer <- Container("hello")
      stdout.println($stringContainer)

      integerContainer <- Container(42)
      stdout.println($integerContainer)

Java/C# generics allow private generic constructors freely (used with static factory methods). EK9 generic types require public constructors — if you want factory-style controlled construction, use a public constructor PLUS a public factory method that callers reach for by convention. The private-constructor-for-factory idiom does not cross into EK9 generic types.

E06060
factory method
generic type
parameterised type
private constructor
protected constructor
public constructor

E06060 error
generic type constructors
constructor access on generics

      Container() as pure
        -> initialItem as T

      private Container() as pure
        -> initialItem as T

Explanation: Marking a generic type's parameterised constructor as 'private' triggers E06060 - parameterised constructors on a generic type must be public so any parameterisation can instantiate. See ek9 -h E06060 for details.


Question: Can a generic class have multiple constructors in EK9, and do they all have to be public to avoid E06060?

How do I write a generic class with more than one constructor in EK9?

Does E06060 block protected constructors as well as private ones on generic types?

Can I have an overloaded generic constructor where some are public and some are private?

Why does my generic class with a default constructor and a parameterised constructor fail E06060?

Answer:
Generic classes in EK9 can have as many constructors as they need — overloading, default, parameterised. The access rule is nuanced, not absolute: the PARAMETERISED (inferred) constructors must be public so the compiler can infer type arguments and instantiate from any context; the no-argument DEFAULT constructor may additionally be 'default private' as an escape hatch; and PROTECTED is never allowed on any generic constructor.

  Error : E06060: 'Box' on line 15 position 22: a generic type does not support private or protected constructors

E06060 fires for a PROTECTED constructor (any), or for a PRIVATE PARAMETERISED constructor. It does NOT fire for a 'default private' no-arg constructor.

CORRECT: MULTIPLE PUBLIC CONSTRUCTORS

  defines class

    Box of type T
      item as T?
      label as String?

      Box() as pure
        item :=? T()
        label :=? String()

      Box() as pure
        -> initialItem as T
        item :=? initialItem
        label :=? String()

      Box() as pure
        ->
          initialItem as T
          tag as String
        item :=? initialItem
        label :=? tag

Three constructors: no-argument, single-argument, two-argument. All public. All allowed. The no-arg constructor gives the conceptual field an unset 'T()' so it is present-but-unset, never null.

CORRECT: PRIVATE NO-ARG DEFAULT (the escape hatch)

  defines class

    Box of type T
      item as T?

      default private Box() as pure           // allowed: the no-arg default may be private

      Box() as pure
        -> initialItem as T
        item :=? initialItem

When the field cannot be given an unset 'T()' (T may be abstract, may hold a function, or may have no public no-arg constructor), make the no-arg default private. External code then cannot create an empty Box; it must pass a value to the inferred constructor.

INCORRECT: PRIVATE OR PROTECTED PARAMETERISED CONSTRUCTOR

  defines class

    Box of type T
      item as T?

      Box() as pure
        item :=? T()

      private Box() as pure                    // E06060 — a parameterised constructor must be public
        -> initialItem as T
        item :=? initialItem

Type inference needs the parameterised constructor public, so making it private (or protected) is E06060.

HIDING VALIDATED CONSTRUCTION: A FACTORY FUNCTION
To route callers through validation, keep the public parameterised constructors and add a factory function in the same module:

  defines function

    createValidatedBox() as pure
      -> value as String
      <- rtn as Box of String?
      if value?
        rtn: Box(value)

Use 'default private' to forbid EMPTY construction; use a factory to control VALUED construction with validation, defaulting, or logging.

See Q1286 for the single-constructor form. See Q645 for the private no-arg escape hatch.

defines module qa.genericsdeep.e06060multi

  defines class

    Box of type (T, U)
      first as T?
      second as U?

      Box() as pure
        first :=? T()
        second :=? U()

      Box() as pure
        ->
          firstValue as T
          secondValue as U
        first :=? firstValue
        second :=? secondValue

      getFirst() as pure
        <- rtn as T: first

      getSecond() as pure
        <- rtn as U: second

      override operator ? as pure
        <- rtn as Boolean: first? and second?

      operator $ as pure
        <- rtn as String: `(${first}, ${second})`

  defines program

    E06060MultiDemo()
      stdout <- Stdout()

      labelled <- Box(42, "answer")
      stdout.println($labelled)

      keyed <- Box("alpha", 3.14)
      stdout.println($keyed)

C#/Java allow private constructors on generic types, commonly paired with static factory methods inside the same class for controlled construction. EK9 takes a different position: generic constructors are public by rule, and factory functions live alongside the generic in the same module. This keeps monomorphisation simple and the code-generation stage deterministic.

E06060
constructor overloading
generic type
monomorphisation
multiple constructors
public constructor

E06060 error
generic constructor rules
multiple constructor overloads

      Box() as pure
        first :=? T()

      protected Box() as pure
        first :=? T()

Explanation: Generic types reject both 'private' and 'protected' constructor modifiers absolutely; a 'protected' constructor on a generic type fires E06060. See ek9 -h E06060 for details.


Question: What does EK9 error E50040 mean when I write an abstract method in a component and how do I fix it?

I'm getting E50040 'cannot be abstract' on a method inside defines component — what's wrong?

Why can't I write 'log() as abstract' inside my EK9 Logger component?

How do I make an extensible Logger component in EK9 without E50040?

The compiler says my abstract method's container is not abstract — how do I fix it?

Answer:
E50040 fires when a method is declared 'as abstract' inside a construct that has not itself been marked 'as abstract'. For components the error usually reads:

  Error : E50040: 'log' on line 7 position 6: is abstract, but construct on line 6 is not marked as abstract, 'public Void <- log(message as String) as abstract': cannot be abstract

The rule is symmetric: a method can only be 'as abstract' if the enclosing class, trait or component is ALSO 'as abstract'. Otherwise the compiler rejects the declaration at phase 1.

THE TWO VALID FIXES

1. Mark the enclosing component as abstract (preferred when you want an extensible base component that subclasses override):

  defines component

    Logger as abstract
      log() as abstract
        -> message as String

    LoggerImpl extends Logger
      override log()
        -> message as String
        stdout <- Stdout()
        stdout.println(`LOG: ${message}`)

2. Give the method a concrete implementation and drop 'as abstract' (preferred when the base component already has a sensible default that subclasses can override):

  defines component

    Logger
      log()
        -> message as String
        // default implementation
        stdout <- Stdout()
        stdout.println(`LOG: ${message}`)

COMPONENTS IN EK9
Components are the dependency-injection unit: they are registered in an application with 'register ComponentType() as Interface' and injected into other components and programs via 'field as Type!'. A component can be abstract (base type for a hierarchy) or concrete (a directly-instantiable implementation). The standard pattern is: ONE abstract component with the interface methods, ONE (or more) concrete extending components with the actual implementations, and the 'register' clause nominates the concrete implementation for the abstract interface.

WHY NOT USE A TRAIT?
Traits are for mix-in behaviour across class hierarchies — they compose interfaces without being the DI unit themselves. Components are specifically for dependency injection. Use a trait when you want multiple classes to share a method surface; use an abstract component when you want an injectable interface with multiple implementations registered per environment (test vs production, for example).

See Q103 for abstract classes and methods. See Q111 for components. See Q677 for how abstract flows through three-level hierarchies.

defines module qa.dependencyinjection.e50040fix

  defines component

    Logger as abstract
      log() as abstract
        -> message as String

    LoggerImpl extends Logger
      override log()
        -> message as String
        stdout <- Stdout()
        stdout.println(`LOG: ${message}`)

    UserService as abstract
      createUser() as abstract
        -> userName as String

    UserServiceImpl extends UserService
      logger as Logger!

      override createUser()
        -> userName as String
        logger.log(`Created user: ${userName}`)

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

  defines application

    MyApp
      register LoggerImpl() as Logger
      register UserServiceImpl() as UserService

  defines program

    E50040FixDemo() with application of MyApp
      service as UserService!
      service.createUser("Alice")
      service.createUser("Bob")

Java interfaces with abstract methods: the interface itself is implicitly abstract. EK9 components: you must state 'as abstract' explicitly on the containing component if the component has abstract methods. There is no implicit abstractness — 'defines component \n Logger \n log() as abstract' is a specific error (E50040), not a lenient shortcut.

E50040
Logger
abstract component
abstract method
component
dependency injection

E50040 error
abstract modifier on components
component inheritance

Explanation: Declaring a method 'as abstract' inside 'defines component / Logger' without marking Logger itself 'as abstract' fires E50040. A method can only be abstract when its enclosing construct is also abstract. Add 'as abstract' to the component header to fix it, or drop 'as abstract' from the method and provide a concrete implementation.


Question: Why does E50040 fire on my abstract method in a class but not in a trait?

When do I need to mark a construct 'as abstract' in EK9 — is it only classes and components?

Why does my trait compile with an abstract method but my class doesn't?

What's the difference between trait abstract methods and class abstract methods in EK9?

How do traits avoid the E50040 rule that classes and components are subject to?

Answer:
E50040 fires when an abstract method appears inside a construct that the compiler does not accept as an abstract container. The rule is NOT uniform across all constructs — it matters which kind of construct you are writing.

WHERE 'as abstract' IS REQUIRED ON THE CONTAINING CONSTRUCT

- Classes: a class with an abstract method must be declared 'Shape as abstract'
- Components: a component with an abstract method must be declared 'Logger as abstract'

Why: classes and components are instantiable by default. The compiler assumes you mean a concrete type unless you opt into abstractness. Leaving an abstract method inside a concrete container would produce an incomplete type with no way to construct it safely.

WHERE 'as abstract' IS NOT REQUIRED

- Traits: a trait is an inherently abstract interface-like construct. Abstract methods are the default expectation. You simply declare 'describe() as abstract' inside a trait and it compiles.
- Generic function declarations that happen to be abstract (E50040 is not the error code for those — a different error applies).

COMPARE THE TWO CASES DIRECTLY

  defines trait

    Printable                              // no 'as abstract' needed
      describe() as pure abstract          // OK — trait accepts abstract methods
        <- rtn as String?

  defines class

    Shape                                  // E50040 on area() below!
      area() as abstract                   // because 'Shape' is not 'as abstract'
        <- rtn as Float?

The trait compiles. The class does not — it reports E50040 on 'area'. Fixing the class means EITHER marking the container 'Shape as abstract' OR dropping 'as abstract' from the method and giving it a default implementation.

DI IMPLICATIONS
For dependency injection hierarchies, the idiomatic EK9 shape is:

- Declare the interface as a TRAIT (no 'as abstract' needed; all methods abstract by default)
- Declare an ABSTRACT CLASS or ABSTRACT COMPONENT that implements the trait and factors out shared state
- Declare CONCRETE subclasses or subcomponents that override the remaining abstract methods
- Register the concrete type in the application

This lets you express 'some shared implementation + some pluggable methods' without fighting E50040. If you find yourself reaching for 'as abstract' on a class or component, ask first whether the abstraction really belongs on a trait — traits are the lighter-weight vehicle for this kind of contract.

See Q1290 for the component-with-abstract-method variant. See Q103 for the full classes/methods/abstract story.

defines module qa.dependencyinjection.e50040classvstrait

  defines trait

    Printable
      describe() as pure abstract
        <- rtn as String?

  defines class

    Shape as abstract
      area() as pure abstract
        <- rtn as Float?

    Circle extends Shape with trait of Printable
      radius as Float: Float()

      Circle()
        -> radius as Float
        this.radius :=: radius

      override area() as pure
        <- rtn as Float: 3.14159 * radius * radius

      override describe() as pure
        <- rtn as String: `Circle(r=${radius})`

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

  defines program

    E50040ClassVsTraitDemo()
      stdout <- Stdout()
      c <- Circle(5.0)
      stdout.println(c.describe())
      stdout.println(`area=${c.area()}`)

Java: every interface method is implicitly abstract; abstract classes require the 'abstract' keyword on the class for abstract methods. EK9: traits behave like Java interfaces (methods implicitly abstract, no container modifier needed); classes and components behave more strictly than Java abstract classes (the container MUST be marked 'as abstract' if it contains any abstract methods). The distinction surfaces in E50040 — it fires on classes and components, never on traits.

E50040
abstract class
abstract method
component
dependency injection
trait

E50040 error
abstract modifier rules
trait vs class abstractness

Explanation: Declaring 'area() as abstract' inside 'defines class / Shape' without marking Shape itself 'as abstract' fires E50040. Unlike traits, classes are concrete by default and must opt into abstractness. Add 'as abstract' to the class header — 'Shape as abstract' — or convert the declaration to a trait if you only need an interface-like contract.


Question: How does service security posture work with 'open' and 'constrain by'?

What is E12050 service security not declared?

How do I declare security on an EK9 service?

What is the difference between open and constrain by on a service?

Can service operations have their own security posture?

Answer:
Every EK9 service operation must have a security posture. The posture is either inherited from the parent service or declared on the operation itself.

SERVICE-LEVEL POSTURE
Declare 'open' or 'constrain by X' on the service. All operations inherit:
  PublicApi :/api open
  SecureApi :/api constrain by JwtGate

OPERATION-LEVEL POSTURE
When the service has no posture, each operation must declare its own:
  MixedApi :/api
    health() as GET for :/status open
    data() as GET for :/data constrain by JwtGate

WHY REQUIRED (E12050)
Security must be a conscious decision. A service that silently defaults to 'open' creates bugs where developers forget authentication. The compiler enforces explicit posture on every endpoint.

SECURITYGATE AND CORSPOLICY
Constraints reference functions extending SecurityGate or CORSPolicy:
  MyGate is SecurityGate — authenticates requests
  MyCors is CORSPolicy — validates CORS origins
Use 'constrain by MyGate' for auth only, or 'constrain by MyGate and MyCors' for auth plus CORS.

See Q1293 for constraint type resolution. See Q1294 for constraint type validation. See Q1295 for duplicate constraint roles. See Q657 for URI mapping. See Q685 for method bodies.

defines module qa.webdeep.securityposture

  defines constant

    jsonType <- "application/json"
    noCache <- "no-cache"
    langEn <- "en"
    okStatus <- 200

  defines function

    <?-
      SecurityGate implementation that accepts all authenticated requests.
    -?>
    JwtGate is SecurityGate
      -> context as HTTPContext
      <- rtn as HTTPContext: context

    <?-
      CORSPolicy that allows specific origins.
    -?>
    AllowedOrigins is CORSPolicy
      -> origin as String
      <- rtn as Boolean: true

  defines service

    <?-
      Service-level posture: all operations inherit 'open'.
    -?>
    PublicApi :/api/public open

      health() as GET for :/health
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"status": "healthy"}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

    <?-
      Service-level posture: all operations inherit 'constrain by'.
    -?>
    SecureApi :/api/secure constrain by JwtGate

      data() as GET for :/data
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"items": []}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

    <?-
      Service with both SecurityGate and CORSPolicy.
    -?>
    FullApi :/api/full constrain by JwtGate and AllowedOrigins

      info() as GET for :/info
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"name": "FullApi"}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

    <?-
      Operation-level posture: service has none, each operation declares its own.
    -?>
    MixedApi :/api/mixed

      health() as GET for :/health open
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: "ok"
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: "text/plain"
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

      secure() as GET for :/secure constrain by JwtGate
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"secure": true}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

  defines application

    SecurityPostureApp
      register PublicApi()
      register SecureApi()
      register FullApi()
      register MixedApi()

  defines program

    ServiceSecurityPostureDemo()
      stdout <- Stdout()
      stdout.println("Service security posture patterns:")
      stdout.println("  PublicApi: service-level 'open'")
      stdout.println("  SecureApi: service-level 'constrain by JwtGate'")
      stdout.println("  FullApi: service-level 'constrain by JwtGate and AllowedOrigins'")
      stdout.println("  MixedApi: operation-level mixed postures")

Java: Spring Security uses @PreAuthorize or SecurityFilterChain — separate from controllers. Python: Flask-Login or decorators, optional and easily forgotten. Go: middleware wrapping, no compile-time check. Rust: Actix middleware, runtime only. EK9: security posture is part of the service grammar, enforced at compile time. No endpoint can exist without an explicit posture.

CORS CORSPolicy E12050 SecurityGate authentication constrain http open posture security service

Explanation: A service with no security posture whose operation also lacks its own posture is rejected — add 'open' or 'constrain by X'. See ek9 -h E12050 for details.


Question: What happens when a constrain by reference cannot be resolved?

What is E12060 constraint type not resolved?

Why does the compiler reject my constrain by clause?

How do I fix an unresolved security constraint reference?

What must I import for constrain by to work?

Answer:
When a 'constrain by' clause names a type that does not exist, the compiler emits E12060.

WHAT CAUSES E12060
The constraint reference name cannot be found in the current module scope or imports:
  MyApi :/api constrain by NonExistentGate
The compiler searches for 'NonExistentGate' but finds nothing.

COMMON CAUSES
1. Spelling error in the constraint name
2. Missing import for the module containing the function
3. The function has not been defined yet
4. The function is in a different module without a 'references' declaration

HOW TO FIX
Define a function that extends SecurityGate or CORSPolicy in the same module, or import it:
  defines function
    JwtGate is SecurityGate
      -> context as HTTPContext
      <- rtn as HTTPContext: context

Then reference it:
  MyApi :/api constrain by JwtGate

See Q1292 for security posture overview. See Q1294 for constraint type validation. See Q1295 for duplicate constraint roles.

defines module qa.webdeep.constraintresolution

  defines constant

    jsonType <- "application/json"
    noCache <- "no-cache"
    langEn <- "en"
    okStatus <- 200

  defines function

    <?-
      SecurityGate implementation for JWT authentication.
    -?>
    JwtGate is SecurityGate
      -> context as HTTPContext
      <- rtn as HTTPContext: context

  defines service

    <?-
      Service with a valid, resolvable constraint reference.
      JwtGate is defined in this module so it resolves correctly.
    -?>
    ProtectedApi :/api/protected constrain by JwtGate

      resource() as GET for :/resource
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"protected": true}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

  defines application

    ConstraintResolutionApp
      register ProtectedApi()

  defines program

    ConstraintResolutionDemo()
      stdout <- Stdout()
      stdout.println("Constraint type resolution:")
      stdout.println("  JwtGate defined in same module -> resolves OK")
      stdout.println("  'constrain by JwtGate' compiles successfully")
      stdout.println("  Missing or misspelled names trigger E12060")

Java: Spring Security bean references are resolved at runtime — a typo causes a NoSuchBeanException at startup. Python: decorator references fail at import time. Go: middleware references are compile-time checked. Rust: type references are compile-time checked. EK9: constraint references are resolved at compile time (Phase 5), so typos and missing imports are caught before deployment.

CORSPolicy E12060 SecurityGate constrain http import reference resolve service

:/api/protected constrain by NonExistentGate

Explanation: The 'constrain by' reference must resolve to an existing function; NonExistentGate is undefined, so the constraint type cannot be resolved. See ek9 -h E12060 for details.


Question: What types are valid in a constrain by clause?

Why must constrain by reference SecurityGate or CORSPolicy?

Can I use any function in a constrain by clause?

What types does the compiler accept for security constraints?

Answer:
A 'constrain by' clause must reference functions that extend SecurityGate, CORSPolicy, or both. The compiler emits E12062 if the referenced type does not extend either.

VALID CONSTRAINT TYPES
SecurityGate: authenticates HTTP requests.
  MyGate is SecurityGate
    -> context as HTTPContext
    <- rtn as HTTPContext: context

CORSPolicy: validates cross-origin requests.
  MyCors is CORSPolicy
    -> origin as String
    <- rtn as Boolean: true

INVALID CONSTRAINT TYPES (E12062)
A regular function or class that does not extend either base:
  defines function
    NotAGate()
      <- rtn as String: "hello"
  defines service
    MyApi :/api constrain by NotAGate  <!- E12062 -!>

WHY THIS RESTRICTION
The router needs to know what to do with the constraint at runtime. SecurityGate functions receive HTTPContext and return an enriched context (or unset for rejection). CORSPolicy functions receive an origin String and return Boolean. Arbitrary functions have unknown signatures.

SINGLE VS DUAL CONSTRAINTS
One constraint: 'constrain by MyGate' (SecurityGate only)
Two constraints: 'constrain by MyGate and MyCors' (one SecurityGate + one CORSPolicy)

See Q1292 for security posture overview. See Q1293 for constraint resolution. See Q1295 for duplicate constraint roles.

defines module qa.webdeep.constraintvalidation

  defines constant

    jsonType <- "application/json"
    noCache <- "no-cache"
    langEn <- "en"
    okStatus <- 200

  defines function

    <?-
      Valid SecurityGate: receives HTTPContext, returns HTTPContext.
    -?>
    TokenGate is SecurityGate
      -> context as HTTPContext
      <- rtn as HTTPContext: context

    <?-
      Valid CORSPolicy: receives origin String, returns Boolean.
    -?>
    OriginPolicy is CORSPolicy
      -> origin as String
      <- rtn as Boolean: true

  defines service

    <?-
      Service using a valid SecurityGate constraint.
    -?>
    GatedService :/api/gated constrain by TokenGate

      items() as GET for :/items
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"items": ["a", "b"]}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

    <?-
      Service using both SecurityGate and CORSPolicy.
    -?>
    GatedWithCors :/api/cors constrain by TokenGate and OriginPolicy

      items() as GET for :/items
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"items": ["c", "d"]}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

  defines application

    ConstraintValidationApp
      register GatedService()
      register GatedWithCors()

  defines program

    ConstraintTypeValidationDemo()
      stdout <- Stdout()
      stdout.println("Constraint type validation:")
      stdout.println("  TokenGate is SecurityGate -> valid")
      stdout.println("  OriginPolicy is CORSPolicy -> valid")
      stdout.println("  Regular functions -> E12062")

Java: Spring Security filters can be any class implementing OncePerRequestFilter — no compile-time type check. Python: Flask decorators are unchecked functions. Go: middleware is any http.Handler wrapper. Rust: Actix middleware must implement Transform trait. EK9: constraints must extend SecurityGate or CORSPolicy, verified at compile time.

CORSPolicy E12062 SecurityGate constrain function http service type validation

TokenGate()
      -> context as HTTPContext

Explanation: A 'constrain by' reference must extend SecurityGate or CORSPolicy; a plain function does not qualify. See ek9 -h E12062 for details.


Question: Why does constrain by X and Y fail when both extend SecurityGate?

What is E12064 duplicate constraint role?

Can I use two SecurityGate functions in constrain by?

Why must the two constraint references have different roles?

How do I combine SecurityGate and CORSPolicy in one clause?

Answer:
When 'constrain by X and Y' names two functions that both extend the same base (both SecurityGate or both CORSPolicy), the compiler emits E12064.

WHAT CAUSES E12064
Duplicate roles in a two-reference constraint:
  constrain by GateAlpha and GateBeta
If both GateAlpha and GateBeta extend SecurityGate, the roles are duplicated.

WHY ONE OF EACH
The runtime applies the two constraints differently:
  SecurityGate: called with HTTPContext, authenticates the request
  CORSPolicy: called with origin String, validates cross-origin access
Two gates or two policies create ambiguity — which one runs first? What happens if they disagree?

CORRECT PATTERN
One SecurityGate + one CORSPolicy:
  constrain by JwtGate and AllowedOrigins
The router knows exactly which function handles authentication and which handles CORS.

SINGLE CONSTRAINT IS FINE
If you only need authentication without CORS:
  constrain by JwtGate
Or only CORS without authentication (unusual):
  constrain by MyCors

See Q1292 for security posture overview. See Q1293 for constraint resolution. See Q1294 for constraint type validation.

defines module qa.webdeep.duplicaterole

  defines constant

    jsonType <- "application/json"
    noCache <- "no-cache"
    langEn <- "en"
    okStatus <- 200

  defines function

    <?-
      SecurityGate for JWT token validation.
    -?>
    JwtGate is SecurityGate
      -> context as HTTPContext
      <- rtn as HTTPContext: context

    <?-
      CORSPolicy that allows known origins.
    -?>
    KnownOrigins is CORSPolicy
      -> origin as String
      <- rtn as Boolean: true

  defines service

    <?-
      Correct: one SecurityGate + one CORSPolicy.
      The router knows which function handles which concern.
    -?>
    ValidDualConstraint :/api/dual constrain by JwtGate and KnownOrigins

      resource() as GET for :/resource
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"dual": "gate+cors"}`
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

  defines application

    DuplicateRoleApp
      register ValidDualConstraint()

  defines program

    DuplicateConstraintRoleDemo()
      stdout <- Stdout()
      stdout.println("Duplicate constraint role rules:")
      stdout.println("  'constrain by Gate and Cors' -> valid (different roles)")
      stdout.println("  'constrain by Gate1 and Gate2' -> E12064 (both SecurityGate)")
      stdout.println("  'constrain by Cors1 and Cors2' -> E12064 (both CORSPolicy)")

Java: Spring Security allows chaining multiple filters of the same type — order is explicit in filter chain. Python: multiple decorators stack. Go: middleware wraps in order. Rust: Actix Transform chain. EK9: at most one SecurityGate and one CORSPolicy per constraint clause, enforced at compile time. No ambiguous filter ordering.

CORSPolicy E12064 SecurityGate constrain duplicate http role service


Question: How do I capture a value into a dynamic function/class without sharing mutations with the caller?

Are dynamic captures by value or by reference in EK9?

If I capture a List into a closure and the closure mutates it, does the caller see the change?

How do I take a snapshot of a value when capturing into a dynamic class?

What is the EK9 equivalent of Java's effectively-final or JavaScript's closure-by-reference?

Answer:
EK9 captures hold the POINTER value at capture time, not a deep copy. Each dynamic class/function instance has its own pointer slot — outer pointer rebinding (`outer := newValue`) does NOT affect the closure. But mutating the captured object via `+=`, `:=:`, `:~:`, or any other operator that modifies the pointed-to object IS visible to the caller, because both share the same object.

THREE PATTERNS:

Pattern 1 — Default capture (shared mutation):
  original <- "Hello"
  sharedAppender <- (original) with trait of Appendable as class
    override append()
      -> extra as String
      original += extra   //mutates the SHARED String
    default operator ?
  sharedAppender.append(" World")
  //outer 'original' is now "Hello World"

Pattern 2 — Isolation via `:=:` copy before capture:
  master <- "Master"
  snapshot <- String()
  snapshot :=: master   //copy contents into snapshot
  isolatedAppender <- (snapshot) with trait of Appendable as class
    ...mutates snapshot, not master

Pattern 3 — Isolation via copy constructor:
  base <- "Base"
  copied <- String(base)   //String's copy constructor
  copyAppender <- (copied) with trait of Appendable as class
    ...mutates copied, not base

IMPORTANT — `:=:` IS NOT ALWAYS DEEP
The `default operator :=:` on records does a SHALLOW copy (copies field references). For deep copy of nested mutable state, the type must implement its own `:=:` that recursively calls `:=:` on each field. See operators.html for the deep-vs-shallow distinction. The same caveat applies to copy constructors — whether they deep-copy or share references is up to the constructor's implementation.

This is the same issue Java has with reference types — EK9's advantage is that the operators (`:=` rebind vs `:=:` copy vs `:~:` merge) make the intent grammatically explicit instead of per-API guessing.

See Q1125 for dynamic function basics. See Q1126 for dynamic class basics. See Q1128 for dynamic class as argument.

defines module qa.advancedtypes.dynamiccaptureisolation

  defines trait

    Appendable
      append() as abstract
        -> extra as String

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

  defines program

    DynamicCaptureIsolationDemo()
      stdout <- Stdout()

      //--- Pattern 1: Default capture — mutation is SHARED with caller ---
      //Captures hold pointer values; mutating the captured object affects the outer scope
      original <- "Hello"
      sharedAppender <- (original) with trait of Appendable as class
        override append()
          -> extra as String
          original += extra
        default operator ?

      stdout.println(original)
      sharedAppender.append(" World")
      stdout.println(original)

      //--- Pattern 2: Snapshot via :=: copy operator BEFORE capture ---
      //Take a copy first, then capture the copy — outer is isolated
      master <- "Master"
      snapshot <- String()
      snapshot :=: master
      isolatedAppender <- (snapshot) with trait of Appendable as class
        override append()
          -> extra as String
          snapshot += extra
        default operator ?

      isolatedAppender.append(" Backup")
      stdout.println(master)
      stdout.println(snapshot)

      //--- Pattern 3: Snapshot via copy constructor ---
      //Construct a new instance from the original — same isolation, different idiom
      base <- "Base"
      copied <- String(base)
      copyAppender <- (copied) with trait of Appendable as class
        override append()
          -> extra as String
          copied += extra
        default operator ?

      copyAppender.append(" Edition")
      stdout.println(base)
      stdout.println(copied)

Java: lambda captures are effectively final — outer rebind impossible at the language level, but the captured object is shared by reference and mutating its state IS visible to the caller (e.g., captured List). Same issue, less explicit. JavaScript: closures capture by scope-chain reference for both rebind AND mutation — even more permissive than EK9. Rust: closures distinguish Fn (read-only borrow), FnMut (mutable borrow), FnOnce (move) at the type-system level — explicit ownership semantics make isolation vs sharing a compile-time property. Kotlin: lambdas capture by reference (val and var both); mutation of captured mutable objects is visible. EK9: capture is by-pointer; rebind (`:=`) inside closure is local-only, mutation operators are shared with caller. Use `:=:` or copy constructor for explicit snapshot.

:=:
by reference
by value
capture
closure
copy constructor
deep copy
dynamic
isolation
mutation
shallow copy
snapshot

capture semantics
closure isolation
deep vs shallow copy

isolated <- (master) with trait of Appendable as class
        override append()
          -> extra as String
          master += extra   //BUG: mutates the caller's master
        default operator ?

Explanation: Capturing a mutable value directly does NOT isolate the closure from the caller. Mutating operators (+=, :=:, :~:) on a captured object affect the SAME object the caller sees. To isolate, copy the value via :=: or a copy constructor BEFORE capture.