Functional Weave
Code in Python

health.nhs-number-format Unreviewed

Display an NHS number in the 3-3-4 format ("943 476 5919") after checking its modulus 11 check digit.

1.0.1 · published 2026-10-03 by charlie · Anterra

Pinned by 13 tests, run in TypeScript, Python and Rust.

Unreviewed. This capability’s implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified clinician has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.

Not professional advice. This capability calculates health figures from published rules. It is a software component for developers, not medical advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a clinician review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.

Not a medical device. It is not intended to diagnose, treat or support clinical decisions about any individual. Anyone building it into clinical software is responsible for that software’s regulatory status, and must validate it under their own clinical governance.

What it does

An NHS number in the 3-3-4 display format, `943 476 5919`, after a full modulus 11 check by `validation.nhs-number` (`validateNhsNumber`, ^2.0.0).

NHS numbers are stored as ten digits and conventionally shown to people as three groups separated by spaces, which makes a transcription error easier to spot on a screen, a letter or a wristband. Input may already be spaced or hyphenated; the digits are what count.

For example

  • format_nhs_number(9434765919) → 943 476 5919 ten plain digits are spaced 3-3-4
  • format_nhs_number(943 476 5919) → 943 476 5919 an already formatted number comes back unchanged
  • format_nhs_number(943-476-5919) → 943 476 5919 hyphens are replaced by spaces

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

def format_nhs_number(value: str) -> str
valuestringten digits, optionally already spaced or hyphenated
returnsstringthree digits, a space, three digits, a space, four digits

Your code names it in one line, in the file that uses it

from fune.health.nhs_number_format import format_nhs_number  # health.nhs-number-format@^1
impl/python.py · 14 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from .validation_nhs_number import validate_nhs_number  ← from validation.nhs-number ^2.0.0 · built alongside by fune


def format_nhs_number(value: str) -> str:
    """The NHS number in the 3-3-4 display format NHS systems use,
    after a full check with validation.nhs-number. An invalid number is an
    error, not a formatted string: a well-spaced wrong number looks more
    trustworthy than a badly spaced right one. The error names the reason but
    never echoes the number, which is a patient identifier and has no place in
    a log."""
    result = validate_nhs_number(value)
    if not result.valid or result.formatted is None:
        raise ValueError("not a valid NHS number (%s)" % (result.reason,))
    return result.formatted

Install

fune build

With that line in your source, in a Python project (language python in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the Python package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add health.nhs-number-format
Download for Python health.nhs-number-format-1.0.1-python.fune · 7,004 bytes sha256 a2517cbbd23a56c4219c880243dcd41f5c172fd2653d74990b60c22f4ef207ad

The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./health.nhs-number-format-1.0.1-python.fune, or fetch it from a terminal with fune pull health.nhs-number-format@1.0.1:python.

The whole function, every language, is one file too: health.nhs-number-format-1.0.1.fune, 8,836 bytes, sha256 1f6e8488a8c34ccf99aad7db26c1105d866646b494890512370348ab1e566dd8. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

# fune: before health.nhs-number-format

after — your function gets the result and the arguments, and returns the final result.

# fune: after health.nhs-number-format

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

# fune: replace validation.nhs-number in health.nhs-number-format

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show health.nhs-number-format --steps.

# fune: step health.nhs-number-format after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
ten plain digits are spaced 3-3-4 9434765919 → 943 476 5919
an already formatted number comes back unchanged 943 476 5919 → 943 476 5919
hyphens are replaced by spaces 943-476-5919 → 943 476 5919
stray and doubled spaces are normalised 94 34 76 5919 → 943 476 5919
another valid number 4010232137 → 401 023 2137
a check digit of 0: remainder 0 gives 11, which means 0 4000000020 → 400 000 0020
a wrong check digit is refused, not formatted 9434765918 → error: not a valid NHS number (bad-check-digit)
nine digits are refused 943476591 → error: not a valid NHS number (bad-length)
eleven digits are refused 94347659190 → error: not a valid NHS number (bad-length)
a letter is refused 943476591A → error: not a valid NHS number (bad-character)
Show the other 3 tests
CaseArgumentsExpected
an empty string is refused → error: not a valid NHS number (empty)
0000000000 passes the arithmetic but is a placeholder 0000000000 → error: not a valid NHS number (placeholder)
a number with no possible check digit (remainder 1) is refused 1000000010 → error: not a valid NHS number (bad-check-digit)

More from the author

## Why an invalid number is an error

A formatter that spaces out whatever it is given turns a mistyped number into something that looks official. So the number is checked first, and an invalid one raises `not a valid NHS number (<reason>)`, where the reason is `validation.nhs-number`'s: `empty`, `bad-character`, `bad-length`, `bad-check-digit` or `placeholder` (0000000000 and the like, which pass the arithmetic). The message deliberately does not repeat the number: it is a patient identifier, and error messages end up in logs.

A valid check digit means the number is well formed, not that it was issued or belongs to this patient; that needs a Personal Demographics Service trace.

## Level

The catalogue lists this at level 0, but it requires `validation.nhs-number`, which is level 1, and a capability may not depend on a higher level; so it is level 1.

## Source

NHS Data Model and Dictionary, *NHS NUMBER*: ten numeric digits, the tenth a check digit validated by the modulus 11 algorithm (https://www.datadictionary.nhs.uk/attributes/nhs_number.html). The check itself, and the 3-3-4 `formatted` form returned here, are `validation.nhs-number`'s; see its README. The Data Dictionary entry does not itself define the display grouping; 3-3-4 is the grouping NHS systems and correspondence conventionally use.

## Before you rely on this

**Not professional advice.** This capability calculates health figures from published rules. It is a software component for developers, not medical advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed above, and have a clinician review how you use it, before anyone relies on the output. Provided "as is" under its licence, without warranty.

**Not a medical device.** It is not intended to diagnose, treat or support clinical decisions about any individual. Anyone building it into clinical software is responsible for that software's regulatory status, and must validate it under their own clinical governance.

**Unreviewed.** This capability's implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified clinician has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.

1.0.1 marks it unreviewed. The code and the tests are unchanged.

Files

PathBytes
README.md3,108
impl/python.py681
impl/rust.rs1,058
impl/typescript.ts700
vectors.json1,570