soma.cell

E0122: nominal_mismatch

E0122 nominal_mismatch — two nominal types mixed (cell type Lots = Int with Cents), or a plain value where a nominal one is declared.

A compile-time error of soma check, the checker of the Soma programming language (soma explain E0122).

The rule

Two nominal types (cell type Lots = Int, cell type Cents = Int) never mix: adding, comparing or passing one where the other is declared is refused, and so is a plain Int variable where a Lots is declared. Here a cap in cents is applied to a position in lots; with plain Int everywhere, soma check passes and soma verify even proves the bound, on the wrong quantity. Declare the cap in what it counts (const MAX_LOTS = Lots(50)), or write the conversion (Cents(to_int(lots) * to_int(price))); a literal (lots <= 50, ?? 0) takes the type of the other side. The type is erased after soma check: at run time, in storage and in JSON a Lots is an Int, and soma verify proves the same things.

A program that triggers it

wrong.cell

cell type Lots = Int
cell type Cents = Int

cell Risk {
    const MAX_EXPOSURE = Cents(5000000)
    memory {
        position: Map<String, Lots> [persistent]
    }
    on order(sym: String, lots: Lots) {
        let held = position.get(sym) ?? 0
        require held + lots <= MAX_EXPOSURE else OverCap
        position.set(sym, held + lots)
        return held + lots
    }
}

What soma check prints:

$ soma check wrong.cell
error: `held + lots <= Cents(5000000)` compares Lots with Cents — two nominal types never compare: if both sides count Lots, declare the Cents side as Lots (`Lots(…)`, `const … = Lots(…)`); if a conversion is meant, write it (`to_int(held + lots)`, `to_int(Cents(5000000))`)
  --> wrong.cell:11:17
   |
11 |         require held + lots <= MAX_EXPOSURE else OverCap
   |                 ^
  = code: E0122
✗ 1 error(s), 0 warning(s).

The fix field of soma check --json: two nominal types never compare: if both sides count Lots, declare the Cents side as Lots (`Lots(…)`, `const … = Lots(…)`); if a conversion is meant, write it (`to_int(held + lots)`, `to_int(Cents(5000000))`)

On line 5, const MAX_EXPOSURE = Cents(5000000) becomes const MAX_LOTS = Lots(50); on line 11, require held + lots <= MAX_EXPOSURE else OverCap becomes require held + lots <= MAX_LOTS else OverCap.

wrong.cell → right.cell

--- wrong.cell
+++ right.cell
@@ -3,5 +3,5 @@
 
 cell Risk {
-    const MAX_EXPOSURE = Cents(5000000)
+    const MAX_LOTS = Lots(50)
     memory {
         position: Map<String, Lots> [persistent]
@@ -9,5 +9,5 @@
     on order(sym: String, lots: Lots) {
         let held = position.get(sym) ?? 0
-        require held + lots <= MAX_EXPOSURE else OverCap
+        require held + lots <= MAX_LOTS else OverCap
         position.set(sym, held + lots)
         return held + lots

With the fix, soma check accepts the program:

$ soma check right.cell
✓ All checks passed.