Functional Weave
Code in Rust

health.bmi Unreviewed

Adult body mass index (kg/m², 1 decimal place) and its NICE/WHO weight category.

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

Pinned by 22 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

Body mass index for an **adult**: weight in kilograms divided by the square of height in metres, rounded half away from zero to one decimal place, with the weight category it falls in.

**Adults only.** These bands do not apply to children and young people, whose BMI is read against age- and sex-specific centile charts (UK-WHO / UK90). Do not call this for anyone under 18.

For example

  • bmi(70, 175) → value 22.9, category healthy-weight 70 kg, 175 cm: 22.857 is 22.9, healthy weight
  • bmi(63.5, 165.1) → value 23.3, category healthy-weight 63.5 kg, 165.1 cm (10 st, 5 ft 5 in): 23.3
  • bmi(50, 180) → value 15.4, category underweight 50 kg, 180 cm: 15.4, underweight

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.

pub fn bmi(weight_kg: f64, height_cm: f64) -> Bmi
weight_kgfloatbody weight in kilograms, 10 to 650
height_cmfloatheight in centimetres, 50 to 272; not metres
returnsBmiadults only: children need BMI centiles, not these categories

The types it declares, generated into your project

// BmiCategory is a string in Rust, one of: "underweight", "healthy-weight", "overweight", "obesity-class-1", "obesity-class-2", "obesity-class-3".
// Parameters take it as &str and results hold it as String.

/// The index and the category read from it.
#[derive(Debug, Clone, PartialEq)]
pub struct Bmi {
    /// kg/m², rounded half away from zero to 1 decimal place
    pub value: f64,
    /// read from the rounded value, as the published bands are
    pub category: String,
}

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

fune!(health.bmi@^1);  // then call bmi(…)
impl/rust.rs · 55 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.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_float::round_float;  ← from math.round-float ^1.0.0 · built alongside by fune

fn check_range(name: &str, value: f64, low: f64, high: f64, unit: &str) {
    if !value.is_finite() || value < low || value > high {
        panic!("{} must be a number from {} to {} {}, received {}", name, low, high, unit, value);
    }
}

/// Body mass index for an adult: weight over height squared, to one decimal
/// place, and the band it falls in. The bands are published to one decimal
/// place (18.5-24.9, 25-29.9, ...), so the category is read from the rounded
/// index: 24.97 is reported as 25.0 and is overweight, not healthy.
///
/// # Panics
/// Panics when the weight or height is outside its range.
pub fn bmi(weight_kg: f64, height_cm: f64) -> Bmi {
    check_range("weightKg", weight_kg, 10.0, 650.0, "kg");
    // Height in metres (1.75) is the commonest unit slip; 50 cm is the floor.
    check_range("heightCm", height_cm, 50.0, 272.0, "cm");
    let metres = height_cm / 100.0;
    let value = round_float(weight_kg / (metres * metres), 1);
    let category = if value < 18.5 {
        "underweight"
    } else if value <= 24.9 {
        "healthy-weight"
    } else if value <= 29.9 {
        "overweight"
    } else if value <= 34.9 {
        "obesity-class-1"
    } else if value <= 39.9 {
        "obesity-class-2"
    } else {
        "obesity-class-3"
    };
    Bmi { value, category: category.to_string() }
}

pub fn bmi_to_value(result: &Bmi) -> Value {
    Value::obj(vec![("value", Value::Float(result.value)), ("category", Value::str(&result.category))])
}

fn number_arg(value: &Value, name: &str, low: f64, high: f64, unit: &str) -> f64 {
    match value {
        Value::Int(_) | Value::Float(_) => value.as_f64(),
        _ => panic!("{} must be a number from {} to {} {}, received {:?}", name, low, high, unit, value),
    }
}

pub fn fune_vector(args: &[Value]) -> Value {
    bmi_to_value(&bmi(
        number_arg(&args[0], "weightKg", 10.0, 650.0, "kg"),
        number_arg(&args[1], "heightCm", 50.0, 272.0, "cm"),
    ))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the Rust 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. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add health.bmi
Download for Rust health.bmi-1.0.1-rust.fune · 11,815 bytes sha256 e8f7422fe3a58e7dff341854aa56c5b3ef93f1174987ba83c7075a8c33cccbc1

The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./health.bmi-1.0.1-rust.fune, or fetch it from a terminal with fune pull health.bmi@1.0.1:rust.

The whole function, every language, is one file too: health.bmi-1.0.1.fune, 14,901 bytes, sha256 2288b649366d888521b2232654f5e07174beeac4280a8d070cc3bdb001bcb593. 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.bmi

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

// fune: after health.bmi

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 math.round-float in health.bmi

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.bmi --steps.

// fune: step health.bmi 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
70 kg, 175 cm: 22.857 is 22.9, healthy weight 70, 175 → value 22.9, category healthy-weight
63.5 kg, 165.1 cm (10 st, 5 ft 5 in): 23.3 63.5, 165.1 → value 23.3, category healthy-weight
50 kg, 180 cm: 15.4, underweight 50, 180 → value 15.4, category underweight
exactly 18.5 is the bottom of healthy weight 74, 200 → value 18.5, category healthy-weight
18.425 rounds to 18.4, underweight 73.7, 200 → value 18.4, category underweight
exactly 24.9 is still healthy weight 99.6, 200 → value 24.9, category healthy-weight
24.975 is reported 25.0 and is overweight: a naive < 25 on the raw value says healthy 99.9, 200 → value 25, category overweight
29.925 rounds to 29.9, overweight 119.7, 200 → value 29.9, category overweight
exactly 30.0 is obesity class 1 120, 200 → value 30, category obesity-class-1
34.975 is reported 35.0, obesity class 2 139.9, 200 → value 35, category obesity-class-2
Show the other 12 tests
CaseArgumentsExpected
92 kg, 160 cm: 35.9375 is 35.9, obesity class 2 92, 160 → value 35.9, category obesity-class-2
39.925 rounds to 39.9, obesity class 2 159.7, 200 → value 39.9, category obesity-class-2
39.975 is reported 40.0, obesity class 3 159.9, 200 → value 40, category obesity-class-3
110 kg, 160 cm: 42.97 is 43.0, obesity class 3 110, 160 → value 43, category obesity-class-3
the lowest accepted weight and height 10, 50 → value 40, category obesity-class-3
the highest accepted weight and height 650, 272 → value 87.9, category obesity-class-3
height in metres is refused, not read as 1.75 cm 70, 1.75 → error: heightCm must be a number from 50 to 272 cm
zero weight is refused 0, 175 → error: weightKg must be a number from 10 to 650 kg
negative weight is refused -70, 175 → error: weightKg must be a number from 10 to 650 kg
weight in pounds past the maximum is refused 700, 175 → error: weightKg must be a number from 10 to 650 kg
a height over 272 cm is refused 70, 1,750 → error: heightCm must be a number from 50 to 272 cm
a weight given as text is refused 70, 175 → error: weightKg must be a number from 10 to 650 kg

More from the author

## Categories

| category | BMI (kg/m²) | |-------------------|--------------| | `underweight` | below 18.5 | | `healthy-weight` | 18.5 to 24.9 | | `overweight` | 25 to 29.9 | | `obesity-class-1` | 30 to 34.9 | | `obesity-class-2` | 35 to 39.9 | | `obesity-class-3` | 40 or more |

The bands are published to one decimal place, so the category is read from the rounded index. 99.9 kg at 200 cm is 24.975, reported as 25.0, and is overweight; a classifier that compares the raw 24.975 with `< 25` says healthy weight, which disagrees with the number it prints.

## What it deliberately does not do

- **Lower thresholds for some ethnic backgrounds.** NICE recommends lower thresholds (overweight 23 to 27.4, obesity 27.5 or more) for people with a South Asian, Chinese, other Asian, Middle Eastern, Black African or African-Caribbean family background. This function returns the general bands only; apply the lower thresholds to `value` where they are relevant. - **Interpretation.** BMI does not measure body fat or central adiposity; NICE asks for clinical judgement, particularly in the healthy-weight band, and a measure of central adiposity alongside it.

## Inputs

Weight is 10 to 650 kg and height 50 to 272 cm. Anything outside is refused, not clamped: in practice it is a unit slip, such as height in metres (1.75) or weight in pounds. The ranges are wide enough to include every recorded adult. The arithmetic is plain division and multiplication, which is correctly rounded in every language, and the rounding is `math.round-float`, so the three implementations return the same double (`floats exact`).

## Sources

- NICE guideline NG246, *Overweight and obesity management*, recommendations 1.9.10 (the bands above, adults) and 1.9.11 (lower thresholds for some ethnic backgrounds): https://www.nice.org.uk/guidance/ng246/chapter/Identifying-and-assessing-overweight-obesity-and-central-adiposity - The cut-offs are those of the World Health Organization's adult classification (WHO, *Obesity: preventing and managing the global epidemic*, Technical Report Series 894, 2000). `underweight` (below 18.5) is WHO's; NICE's list starts at healthy weight, 18.5. (The WHO report itself was not retrieved when this was written; every cut-off here was checked against NICE NG246.)

## 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.md4,078
impl/python.py1,507
impl/rust.rs2,039
impl/typescript.ts1,425
vectors.json3,028