soma.cell

Blood bank — an ABO-incompatible transfusion is unrepresentable, and

you cannot issue a unit you do not have Acute hemolytic transfusion reaction — giving group A blood to a group O patient — is one of the few transfusion errors that kills within minutes. It is almost always a clerical/lookup error: the wrong unit pulled, a label misread. Every blood bank runs a compatibility check; the failure mode is that it is advisory, a screen a tired tech can click past. Here `issue` REFUSES an incompatible match outright (compatible() is the real ABO/Rh donor→recipient lattice), AND the inventory is a conserved quantity: a unit count can never go negative and you cannot issue from an empty shelf. The stock invariant makes "issued more units than exist" a rejected write, not a reconciliation headache.

A Soma program from the medical part of the verified corpus: 129 lines, cells BloodBank, BloodBankTests. It passes soma check with soma 3.2.4.

How it is built

The cell BloodBank declares 5 signals in its face: stock(group: String, units: Int) -> Map, compatible(donor: String, recipient: String) -> Bool, issue(donor: String, recipient: String) -> Map, on_hand(group: String) -> Int and demo() -> String; keeps 1 memory slot: inventory (Map<String, Int>, persistent and consistent) under the invariant inventory >= 0 && inventory <= 500. Handlers: stock (line 41), compatible (line 51), issue (line 69), on_hand (line 84) and demo (line 88).

The file also holds 1 test cell (BloodBankTests) with 12 assertions, run by soma test.

Generated from soma describe on this file.

The source

bloodbank.cell

// ============================================================================
// BLOOD BANK — an ABO-incompatible transfusion is unrepresentable, and
// you cannot issue a unit you do not have
// ============================================================================
//
// Acute hemolytic transfusion reaction — giving group A blood to a group
// O patient — is one of the few transfusion errors that kills within
// minutes. It is almost always a clerical/lookup error: the wrong unit
// pulled, a label misread. Every blood bank runs a compatibility check;
// the failure mode is that it is advisory, a screen a tired tech can
// click past.
//
// Here `issue` REFUSES an incompatible match outright (compatible() is
// the real ABO/Rh donor→recipient lattice), AND the inventory is a
// conserved quantity: a unit count can never go negative and you cannot
// issue from an empty shelf. The stock invariant makes "issued more units
// than exist" a rejected write, not a reconciliation headache.
//
//   soma check examples/corpus/medical/bloodbank.cell
//   soma test  examples/corpus/medical/bloodbank.cell
//   soma run   examples/corpus/medical/bloodbank.cell demo
// ============================================================================

cell BloodBank {
    face {
        signal stock(group: String, units: Int) -> Map
        signal compatible(donor: String, recipient: String) -> Bool
        signal issue(donor: String, recipient: String) -> Map
        signal on_hand(group: String) -> Int
        signal demo() -> String
    }

    memory {
        // Units on hand per ABO/Rh group. A conserved quantity: never
        // negative, capped at a sane shelf size. "Issued a unit we don't
        // have" becomes a rejected write, not a silent -1.
        inventory: Map<String, Int> [persistent, consistent]
        invariant inventory >= 0 && inventory <= 500
    }

    on stock(group: String, units: Int) {
        let cur = inventory.get(group) ?? 0
        let w = try { inventory.set(group, cur + units) }
        if w.error != () { return map("ok", false, "reason", "{w.error}") }
        return map("ok", true, "group", group, "on_hand", cur + units)
    }

    // The ABO/Rh donor → recipient compatibility lattice. O- is the
    // universal donor; AB+ the universal recipient. This is the rule a
    // hemolytic reaction violates — encoded once, checked at issue.
    on compatible(donor: String, recipient: String) {
        let table = map(
            "O-",  list("O-", "O+", "A-", "A+", "B-", "B+", "AB-", "AB+"),
            "O+",  list("O+", "A+", "B+", "AB+"),
            "A-",  list("A-", "A+", "AB-", "AB+"),
            "A+",  list("A+", "AB+"),
            "B-",  list("B-", "B+", "AB-", "AB+"),
            "B+",  list("B+", "AB+"),
            "AB-", list("AB-", "AB+"),
            "AB+", list("AB+")
        )
        let recips = table[donor] ?? list()
        return any(recips, r => r == recipient)
    }

    // Issue a unit ONLY if it is ABO-compatible AND in stock. Both walls
    // must hold; the inventory decrement passes through the conservation
    // invariant.
    on issue(donor: String, recipient: String) {
        let ok = compatible(donor, recipient)
        if !ok {
            return map("ok", false, "reason",
                "INCOMPATIBLE: {donor} -> {recipient} would hemolyze")
        }
        let have = inventory.get(donor) ?? 0
        if have <= 0 {
            return map("ok", false, "reason", "no {donor} units on hand")
        }
        inventory.set(donor, have - 1)
        return map("ok", true, "donor", donor, "recipient", recipient,
                   "remaining", have - 1)
    }

    on on_hand(group: String) {
        return inventory.get(group) ?? 0
    }

    on demo() {
        stock("O-", 3)
        stock("A+", 2)
        let safe = issue("O-", "B+")          // universal donor — fine
        let kill = issue("A+", "O-")          // A into O — REFUSED
        let compat = compatible("O-", "B+")
        let left = on_hand("O-")
        print("compatible O- -> B+: {compat}")
        print("issue safe:  {safe}")
        print("issue fatal: {kill}")
        print("O- on hand:  {left}")
        return "demo done"
    }
}

cell test BloodBankTests {
    rules {
        assert stock("O-", 2).ok == true
        assert stock("A+", 1).ok == true

        // the lattice: universal donor compatible, A->O lethal
        assert compatible("O-", "AB+") == true
        assert compatible("A+", "O-") == false
        assert compatible("B+", "A+") == false

        // a compatible, in-stock issue succeeds and decrements
        assert issue("O-", "A+").ok == true
        assert on_hand("O-") == 1

        // the incompatible transfusion is REFUSED
        assert issue("A+", "O-").ok == false

        // conservation: cannot issue past empty
        assert issue("O-", "O-").ok == true        // drains last O-
        assert on_hand("O-") == 0
        assert issue("O-", "O-").ok == false       // shelf empty

        // the slot itself refuses an impossible negative count
        assert_fails inventory.set("A+", -1)
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check bloodbank.cell
✓ All checks passed.
$ soma test bloodbank.cell
test BloodBankTests ...
  ✓ assert stock("O-", 2).ok == true
  ✓ assert stock("A+", 1).ok == true
  ✓ assert compatible("O-", "AB+") == true
  ✓ assert compatible("A+", "O-") == false
  ✓ assert compatible("B+", "A+") == false
  ✓ assert issue("O-", "A+").ok == true
  ✓ assert on_hand("O-") == 1
  ✓ assert issue("A+", "O-").ok == false
  ✓ assert issue("O-", "O-").ok == true
  ✓ assert on_hand("O-") == 0
  ✓ assert issue("O-", "O-").ok == false
  ✓ assert_fails inventory.set("A+", -1) — raised memory invariant violated on 'inventory': inventory >= 0 && inventory <= 500 — rejected write of -1 (key "A+"); the slot is unchanged

12 tests: 12 passed, 0 failed
$ soma verify bloodbank.cell
Verifying bloodbank.cell...

BloodBank/invariants:

  ✓ invariant inventory >= 0 && inventory <= 500 — writer 'issue' proven by induction (writes have - 1)
  ⚠ invariant inventory >= 0 && inventory <= 500 — runtime-checked (computed values): stock → inventory because `units` is a parameter (narrow it: `require (cur + units) >= 0 && (cur + units) <= 500 else …`)

1 passed, 1 warnings, 0 failures
Temporal: 0 passed, 0 failed
VERIFY OK
$ soma run bloodbank.cell demo
compatible O- -> B+: true
issue safe:  {"ok": true, "donor": "O-", "recipient": "B+", "remaining": 2}
issue fatal: {"ok": false, "reason": "INCOMPATIBLE: A+ -> O- would hemolyze"}
O- on hand:  2
demo done

Builtins used

any, list, map, print

Source file: /corpus/medical/bloodbank.cell · examples/corpus/medical/bloodbank.cell