N NezamDocumentation

Function declarations#

Functions express executable behavior. Use them for queries, commands, validation helpers, orchestration, and resolvers that turn configuration-table rows into decisions.

Declaration shape#

bl
public async function post_invoice(
  invoice_id: uuid,
  posting_date: date = today()
): Invoice {
  let invoice = load_invoice(invoice_id);
  return invoice;
}

A function can have annotations, an access modifier, function modifiers, a name, typed parameters, an optional return type, and a body.

PartShape
Annotations@audited
Accesspublic, private, protected, or internal
Modifiersstatic, abstract, virtual, override, async, inline
Parametersname: Type with optional = expression
Return: Type, -> Type, : async Type, or -> async Type
BodyBlock, semicolon, arrow statement, arrow expression, or arrow query

Parameters#

Parameters are typed bindings. A parameter may include a default expression.

bl
function find_vendor(
  company_code_param: CompanyCode,
  active_only: bool = true
): Vendor? {
  return load_vendor(company_code_param, active_only);
}

Use explicit parameter names for scope values such as company, tenant, process, document type, and effective date. Hidden globals make policy resolution harder to test.

Return types#

Return types can use : or ->. The return type can be marked async.

bl
function get_vendor(id: VendorId): Vendor?;
function list_vendors(company_code: CompanyCode) -> Json;
function sync_vendor(id: VendorId) -> async Vendor;

Use a non-null return type when missing data is a closed failure. For required configuration, prefer raising a declared message-backed error instead of returning nullable policy.

Block bodies#

Block bodies contain statements.

bl
private function resolve_posting_policy(
  company_code_param: CompanyCode,
  document_type_param: DocumentType,
  posting_date_param: date
): PostingPolicy {
  select var policy: PostingPolicy
    where PostingPolicy.company_code = company_code_param
      and PostingPolicy.document_type = document_type_param
      and PostingPolicy.active = true;

  if (policy is null) {
    raise message posting_policy_required(company_code_param, document_type_param);
  }

  return policy;
}

Function bodies can include query statements, control flow, raises, try/catch, saves, and nested declarations when those statements are valid in the language.

Abstract declarations#

A semicolon body declares the signature without implementation.

bl
public function approve_invoice(invoice_id: uuid): bool;

Use abstract declarations for contracts or implementation points. Do not use them to hide configurable policy that should be visible as table data.

Arrow bodies#

Arrow bodies can be statements, expressions, or query statements.

bl
function display_vendor(vendor: Vendor): string => vendor.vendor_name;

function deactivate_vendor(vendor: Vendor) => {
  vendor.active = false;
  save vendor;
}

Use arrow expression bodies for small pure helpers. Use block bodies for commands that validate input, resolve policy, update state, or raise declared messages.

Query bodies#

Arrow bodies can return query statements directly.

bl
function active_vendors(company_code_param: CompanyCode): Json =>
  select *
  from Vendor
  where Vendor.company_code = company_code_param
    and Vendor.active = true;

function delete_draft_invoice(invoice_id_param: uuid): Json =>
  delete from Invoice
  where Invoice.invoice_id == invoice_id_param
    and Invoice.status == "draft"
  returning invoice_id;

For the full query surface, see Queries and DML.

Nested functions#

Function bodies can contain nested function declarations.

bl
function normalize_vendor(vendor: Vendor): Vendor {
  function normalize_name(name: string): string => name.trim();

  vendor.vendor_name = normalize_name(vendor.vendor_name);
  return vendor;
}

Keep nested functions local to a single workflow. Shared rules, reusable validations, and cross-module behavior should be declared at module scope.

Policy boundary#

Functions are where configuration is resolved and enforced. They should not encode configurable policy as hardcoded branch trees.

bl
let policy = resolve_posting_policy(company_code, document_type, posting_date);

if (policy.requires_approval && amount > policy.approval_limit) {
  raise message invoice_approval_required(invoice_id);
}

If thresholds, statuses, routes, reason codes, or workflow outcomes vary by tenant or process, read them from tables and fail closed when required configuration is missing.

Source: packages/business/language/function-declarations.md