soma.cell

CSV-shaped transform: clean -> derive -> project

Each row is a Map as if parsed from a CSV header. Demonstrates a block-lambda map(...) that derives new columns, filter for valid rows, and select to project the output shape.

A Soma program from the data part of the verified corpus: 51 lines, cells CsvTransform, CsvTransformTests. It passes soma check with soma 3.2.4.

How it is built

The cell CsvTransform has no face and no memory. Handlers: raw (line 6), line_items (line 17), invoice (line 27), grand_total (line 29) and run (line 31).

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

Generated from soma describe on this file.

The source

csv_transform.cell

// CSV-shaped transform: clean -> derive -> project. Each row is a Map as if
// parsed from a CSV header. Demonstrates a block-lambda map(...) that derives
// new columns, filter for valid rows, and select to project the output shape.

cell CsvTransform {
    on raw() {
        return list(
            map("sku", "A1", "qty", 4, "unit_price", 25, "tax_rate", 20),
            map("sku", "B2", "qty", 0, "unit_price", 99, "tax_rate", 20),
            map("sku", "C3", "qty", 2, "unit_price", 50, "tax_rate", 5),
            map("sku", "D4", "qty", 10, "unit_price", 3, "tax_rate", 0),
            map("sku", "E5", "qty", 1, "unit_price", 200, "tax_rate", 20)
        )
    }

    // Derive net, tax, gross per line; keep only rows that actually shipped.
    on line_items() {
        return raw()
            |> filter_by("qty", ">", 0)
            |> map(r => {
                let net = r.qty * r.unit_price
                let tax = floor(net * r.tax_rate / 100)
                r |> with("net", net) |> with("tax", tax) |> with("gross", net + tax)
            })
    }

    on invoice() { return line_items() |> select("sku", "qty", "net", "tax", "gross") }

    on grand_total() { return line_items() |> sum_by("gross") }

    on run() {
        for r in invoice() { print("{r.sku} x{r.qty}: net {r.net} + tax {r.tax} = {r.gross}") }
        return grand_total()
    }
}

cell test CsvTransformTests {
    rules {
        // B2 has qty 0 -> dropped; 4 of 5 rows remain
        assert len(line_items()) == 4
        // A1: net 100, tax 20, gross 120
        assert (line_items() |> filter_by("sku", "=", "A1"))[0].gross == 120
        // C3: net 100, tax 5 (5%), gross 105
        assert (line_items() |> filter_by("sku", "=", "C3"))[0].tax == 5
        // D4: tax_rate 0 -> tax 0
        assert (line_items() |> filter_by("sku", "=", "D4"))[0].tax == 0
        // grand total: 120 + 105 + 30 + 240 = 495
        assert grand_total() == 495
    }
}

What the toolchain says

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

$ soma check csv_transform.cell
✓ All checks passed.
$ soma test csv_transform.cell
test CsvTransformTests ...
  ✓ assert len(line_items()) == 4
  ✓ assert (line_items() |> filter_by("sku", "=", "A1"))[0].gross == 120
  ✓ assert (line_items() |> filter_by("sku", "=", "C3"))[0].tax == 5
  ✓ assert (line_items() |> filter_by("sku", "=", "D4"))[0].tax == 0
  ✓ assert grand_total() == 495

5 tests: 5 passed, 0 failed
$ soma verify csv_transform.cell
Verifying csv_transform.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

filter_by, floor, len, list, map, print, select, sum_by, with

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