soma.cell

A/B test rollup: per-variant conversions and lift over control

Demonstrates filtering a List<Map> by group, count over a predicate lambda, and computing a derived percentage with integer-aware floor division.

A Soma program from the data part of the verified corpus: 60 lines, cells AbTest, AbTestTests. It passes soma check with soma 3.2.4.

How it is built

The cell AbTest has no face and no memory. Handlers: samples (line 6), of (line 19), rate (line 22), lift (line 29), summary (line 31) and run (line 38).

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

Generated from soma describe on this file.

The source

ab_test.cell

// A/B test rollup: per-variant conversions and lift over control.
// Demonstrates filtering a List<Map> by group, count over a predicate lambda,
// and computing a derived percentage with integer-aware floor division.

cell AbTest {
    on samples() {
        return list(
            map("variant", "A", "converted", 1),
            map("variant", "A", "converted", 0),
            map("variant", "A", "converted", 1),
            map("variant", "A", "converted", 0),
            map("variant", "B", "converted", 1),
            map("variant", "B", "converted", 1),
            map("variant", "B", "converted", 1),
            map("variant", "B", "converted", 0)
        )
    }

    on of(variant: String) { return samples() |> filter_by("variant", "=", variant) }

    // Conversion rate (%) for a variant.
    on rate(variant: String) {
        let rows = of(variant)
        let conv = rows |> count(r => r.converted == 1)
        return floor(conv * 100 / len(rows))
    }

    // Absolute lift of B over A, in percentage points.
    on lift() { return rate("B") - rate("A") }

    on summary() {
        return list(
            map("variant", "A", "rate", rate("A")),
            map("variant", "B", "rate", rate("B"))
        ) |> sort_by("rate", "desc")
    }

    on run() {
        let ra = rate("A")
        let rb = rate("B")
        let l = lift()
        print("A: {ra}%, B: {rb}%, lift: {l}pp")
        return summary()
    }
}

cell test AbTestTests {
    rules {
        // A: 2/4 = 50%, B: 3/4 = 75%
        assert rate("A") == 50
        assert rate("B") == 75
        // lift = 75 - 50 = 25 points
        assert lift() == 25
        // B wins
        assert summary()[0].variant == "B"
        // each variant has 4 samples
        assert len(of("A")) == 4
    }
}

What the toolchain says

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

$ soma check ab_test.cell
✓ All checks passed.
$ soma test ab_test.cell
test AbTestTests ...
  ✓ assert rate("A") == 50
  ✓ assert rate("B") == 75
  ✓ assert lift() == 25
  ✓ assert summary()[0].variant == "B"
  ✓ assert len(of("A")) == 4

5 tests: 5 passed, 0 failed
$ soma verify ab_test.cell
Verifying ab_test.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)

Builtins used

count, filter_by, floor, len, list, map, print, sort_by

Source file: /corpus/data/ab_test.cell · examples/corpus/data/ab_test.cell