# Lemma
> Lemma: declarative business rule language. Translate natural-language policy to readable, deterministic `.lemma` specs. Inputs derived from rules — declare `data` with constraints. No runtime value invention.
Goal: produce Lemma source for human reading and system evaluation. Prefer named pipeline rules. No clever one-liners. Split unrelated policies to separate specs.
Use `lemma show` to inspect static interface (types, constraints, normalized rules). Run-data needs come from rule `missing_data` on `run` (not `show`).
Explanations opt-in (`explain: true` / `lemma run -x`). Wire shape: `engine/schemas/api.v1.json` (`RuleResult.explanation` is `RuleNode`; nested nodes under `ExplanationNode`).
**NO INLINE COMMENTS.** Lemma has no `#`, `//`, or `--` comment syntax. `#` starts parse error. No trailing remarks on `data` or `rule` lines.
**Only doc in source:** commentary block `"""..."""`. Must be **very next tokens** after `spec` line (before `uses`, `data`, etc.). Nowhere else.
---
**Mandatory spec opening order:**
```
spec <name> [<effective>]
[""" commentary — optional, but if present must be HERE """]
uses ...
data ...
rule ...
```
Commentary after `uses` or `data` is invalid, parse fails. No `#`, `//`, `--` comment syntax. Use descriptive names. Put user explanations outside code fences — never inside ` ```lemma ` blocks.
---
**Organization: spec → rule**
Default: one file, one implicit repo. No `repo` blocks unless multi-namespace workspace requested. Structure: **spec → rule**.
**Spec** = namespace for `data` and `rules`. **Rule** = named computed value. Reference rules by name; engine resolves if name is data or rule. One file can have multiple specs.
Hierarchical names: `spec employee/contract`. Effective date for temporal changes: `spec pricing 2026-01-01`.
Sole documentation: commentary `"""..."""` immediately after `spec <name> [<effective>]` (before `uses`, `data`, or `rule`).
**Example A — minimal single-spec file**
```lemma
spec pricing 2026-01-01
"""
Pricing rules for bulk and member discounts.
Commentary must follow the spec line; it cannot go anywhere else.
"""
data qty: number
data base_price: 100
data is_member: false
rule vat_amount: base_price * 21%
rule price_with_vat: base_price + vat_amount
rule bulk_discount:
qty >= 100 and price_with_vat > 500
rule discount: 0%
unless qty >= 10 then 10%
unless bulk_discount then 15%
unless is_member then 20%
rule discount_amount: base_price * discount
rule price_with_discount: base_price - discount_amount
```
**Example B — multi-spec composition (same file)**
```lemma
spec base_config
data standard_discount: 5%
data tax_rate: 21%
data base_price: number -> minimum 0 -> suggest 100
rule tax_amount: base_price * tax_rate
rule price_with_tax: base_price + tax_amount
rule discount_amount: base_price * standard_discount
rule discounted_price: base_price - discount_amount
rule final_price: discounted_price * (100% + tax_rate)
spec line_item
data qty: number -> minimum 0 -> suggest 10
uses pricing: base_config
rule line_total: pricing.final_price * qty
rule has_discount: pricing.standard_discount > 0%
spec simple_order
uses line: line_item
with line.qty: 100
rule order_total: line.line_total
rule effective_unit_price: order_total / line.qty
```
- `uses alias: target_spec`: imports spec in same file.
- Reference members: `alias.field` or `alias.rule_name`.
- `with alias.field: value`: sets imported data. Do not use `data alias.field`. Local `with name: …` invalid — use `data name: …`.
**LemmaBase — shared specs from the registry**
Specs on [LemmaBase.com](https://lemmabase.com) imported with `@` repo qualifiers. Search: [lemmabase.com/search?q=](https://lemmabase.com/search?q=) (e.g. `?q=finance`).
```lemma
spec invoicing
"""
Invoice lines using ISO country codes from LemmaBase.
"""
uses lemma units
uses iso: @iso/countries alpha2 2026-01-01
data price: measure
-> unit eur 1
data country: iso.code
rule tariff: 0 eur
unless country is "NL" then price * 5%
rule total: price + tariff
```
Forms:
- `uses @user/repo spec_name`: import registry spec (alias = spec name)
- `uses alias: @user/repo spec_name`: import with alias (`iso.field`)
- `uses @user/repo spec_name 2026-01-01`: pin effective date
Reference imported members: `iso.code`. Detail: [Registry](https://github.com/lemma/lemma/blob/main/cli/documentation/reference/registry.md).
`repo` blocks namespace specs across contexts (e.g., `repo accounting`). Skip unless asked. Details: [Composing specs](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/composing_specs.md).
---
**Natural language → Lemma**
Request: *"Library charges €0.25/day for regular books, €0.50 for reference, €1 for new releases. First offense gets 50% off. Grace period 3 day except new releases. Block checkout if fee exceeds €10."*
Map logic:
- Book types → `data book_type` with `-> option` constraints
- Overdue days, first offense → `data` input slots
- Per-day rates → `rule daily_fee` with unless branches
- Grace period → `rule is_in_grace_period`
- Fee pipeline → `rule total_fee`, `rule final_fee`
- Checkout block → `rule can_checkout: yes` with `unless final_fee > 10 eur then no` (boolean, not veto)
**Example C — library fees (full spec)**
```lemma
spec library_fees
data money: measure
-> decimals 2
-> unit eur 1.00
-> minimum 0 eur
data book_kind: text
-> option "regular"
-> option "reference"
-> option "new_release"
data book_type: book_kind
data is_first_offense: boolean
data days_overdue: number
-> minimum 0
rule daily_fee: 0 eur
unless book_type is "regular" then 0.25 eur
unless book_type is "reference" then 0.5 eur
unless book_type is "new_release" then 1 eur
rule is_in_grace_period: days_overdue <= 3
unless book_type is "new_release" then no
rule total_fee: days_overdue * daily_fee
rule final_fee: total_fee
unless is_first_offense then total_fee * 50%
unless is_in_grace_period then 0 eur
rule can_checkout: yes
unless final_fee > 10 eur then no
```
---
**Data: constraint definitions, not placeholders**
`data` declares variables. Constraints define validity. Type-only `data` (no value) is input slot. `lemma show` lists static reachable data. Evaluator needs come from rule `missing_data` on `run` (not `show`). Use real domain values. Never `"TODO"` or dummy placeholders.
**Example D — typed data (coffee order)**
```lemma
spec coffee_order
data money: measure
-> decimals 2
-> unit eur 1.00
-> unit gbp 1.17
-> unit usd 0.84
-> minimum 0 eur
data product: text
-> option "espresso"
-> option "latte"
-> option "cappuccino"
-> option "mocha"
data size: text
-> option "small"
-> option "medium"
-> option "large"
data age: number
-> maximum 100
-> minimum 0
data number_of_cups: number
-> maximum 10
data has_loyalty_card: boolean
```
- `age`, `number_of_cups`: input slots (type-only + bounds)
- `money`: custom measure type with units, decimals, minimum
- `product`, `size`: text enumeration via `-> option` (prefer over veto for static sets)
**Example E — data patterns**
Input slot:
```lemma
spec intake
data customer_age: number -> minimum 0 -> maximum 120
```
Fixed policy constant:
```lemma
spec fiscal_policy
data tax_rate: 21%
```
Text enumeration:
```lemma
spec membership
data status: text
-> option "active"
-> option "inactive"
```
Typed alias:
```lemma
spec accounts
data money: measure -> unit eur 1.00
data wallet: money -> minimum 0 eur
```
With help text:
```lemma
spec payroll
data pay_period: text
-> option "month"
-> option "week"
-> help "How often you are paid."
```
Constraints chain: `-> minimum`, `-> maximum`, `-> option`, `-> unit`, `-> decimals`, `-> suggest`, `-> help`, etc. Details in Reference.
---
**Standard library — `uses lemma units`**
Lemma embeds SI bases, derived compounds (force, pressure, energy, power, frequency, electrical), imperial, area/volume, and information (`bit`/`byte`) in `repo lemma` / `spec units`. Import: `uses lemma units`. Reference types: `units.mass`, `units.duration`, `units.length`, `units.force`. Unit names: **singular only** (`8 hour`). Length uses American `meter`. Durations (`hour`, `day`, `week`) require `units.duration`. No Celsius/Fahrenheit (kelvin only).
```lemma
spec logistics
"""
Physical shipment constraints using SI units from the standard library.
"""
uses lemma units
data package_weight: 12 kilogram
data shift_length: 8 hour
data route_distance: 45 kilometer
rule weight_grams: package_weight as gram
rule shift_hours: shift_length as hour
rule distance_km: route_distance as kilometer
rule is_heavy: package_weight > 20 kilogram
rule is_long_shift: shift_length >= 8 hour
```
Prefer `units.mass`, `units.duration`, `units.length` over redefining units. Convert in family: `as <unit>`. Strip unit: `amount as eur as number`. Cross-family relabel: `5 eur as kg` -> `5 kg`.
**Ranges — half-open intervals**
Ranges: lower bound inclusive, upper bound exclusive (`lo...hi`). Test with `in`. Width: `(lo...hi) as <unit> as number` (or `(lo...hi) as <unit>`). Bare `as number` on date/measure ranges fails. Typedefs: `number range`, `date range`, `measure range`, `ratio range`. Month/year intervals: `uses lemma units` and inline literals (`18 year...67 year`) or `units.calendar range`.
```lemma
spec eligibility
uses lemma units
data employee_age: 42 year
data performance_score: 75
data package_weight: 45 kilogram
data hire_date: 2024-01-15
data review_date: 2024-06-30
data discount_rate: 15%
data eligible_band: units.calendar range
-> suggest 18 year...67 year
data score_band: number range
-> suggest 0...100
rule is_working_age: employee_age in eligible_band
rule is_top_score: performance_score in 90...100
rule is_heavy: package_weight in 30 kilogram...80 kilogram
rule in_discount_band: discount_rate in 0%...50%
rule in_q2: hire_date in 2024-04-01...2024-07-01
rule review_days: (hire_date...review_date) as day
rule span_years: (1990-05-20...2024-06-15) as year
```
Upper bound exclusive: `67 year` is NOT inside `18 year...67 year` (returns false). Declare range slots on `data` for reuse, inline `value in lo...hi` for one-off.
Range over custom measure type (no SI import needed):
```lemma
spec freight
data weight: measure
-> unit gram 1
-> unit kilogram 1000
data load_band: weight range
-> suggest 30 kilogram...80 kilogram
rule inside_band: 45 kilogram in load_band
rule band_width: (30 kilogram...80 kilogram) as kilogram
```
**Derived measures — compound units**
Build compound units with `/`, `*`, `^`. Name derived unit, then give compound expression. Prior measure types must declare referenced base units (`eur`, `hour`, `employee`, `results`). Import `uses lemma units` if using time (`eur/hour`).
```lemma
spec contractor
uses lemma units
data money: measure
-> unit eur 1.00
data headcount: measure
-> unit employee 1
data outcome: measure
-> unit results 1
data wage_rate: measure
-> unit eur_per_second eur/second
-> unit eur_per_hour eur/hour
data productivity: measure
-> unit result_per_employee results/eur/hour/employee
data premium_per_head: measure
-> unit eur_hour_per_employee eur_per_hour/employee
data time_worked: 120 hour
data wage: wage_rate -> suggest 85 eur_per_hour
data yield_rate: productivity -> suggest 3 result_per_employee
rule total: wage * time_worked
rule is_high_yield: yield_rate >= 2 result_per_employee
```
Layer compound units: `eur_per_hour` builds on `eur` and `hour`. Dimensional checks run at plan time.
**Date predicates relative to `now`**
`now` is evaluation/effective instant. Import `uses lemma units` for duration windows.
| Form | Meaning |
|------|---------|
| `date in past` / `in future` | Before / after `now` |
| `date in past N day` / `in future N day` | In last / next N duration units |
| `past N day` / `future N day` | Relative date-range window |
| `date in calendar year\|month\|week` | Current calendar period |
| `date in past\|future calendar year\|month\|week` | Adjacent calendar period |
| `date not in calendar year\|month\|week` | Not current calendar period |
```lemma
spec recency
uses lemma units
data event_date: date
rule recent: event_date in past 7 day
rule this_year: event_date in calendar year
```
---
**Rules and unless: last matching clause wins**
Write default expression, then: `unless <condition> then <result>`. Evaluated in source order. If multiple match, **bottommost wins**. Order general first, specific overrides last.
Use **snake_case** rule names. Boolean rules as predicates: `is_eligible`, `can_ship`. Decompose logic into pipeline of named rules. No opaque single expressions.
**Example F — overlapping unless (last wins)**
```lemma
spec vip_discount
data qty: number
data is_vip: boolean
rule discount: 0%
unless qty >= 10 then 10%
unless qty >= 50 then 20%
unless is_vip then 25%
```
VIP customer ordering 75 items gets **25%** (not 20%). Both `qty >= 50` and `is_vip` match; bottommost wins.
**Example G — progressive unless chain**
```lemma
spec rules_and_unless
data is_premium: yes
data base_price: number -> minimum 0
data customer_age: number -> minimum 0 -> maximum 120
data qty: number -> minimum 0
rule total_before_discount:
base_price * qty
rule discount_percentage:
0%
unless qty >= 10 then 10%
unless qty >= 20 then 15%
unless is_premium then 20%
rule discount_amount:
total_before_discount * discount_percentage
rule total_after_discount:
total_before_discount - discount_amount
rule shipping_cost:
15
unless total_after_discount >= 100 then 10
unless total_after_discount >= 200 then 0
rule final_total:
total_after_discount + shipping_cost
```
Tiered discounts, derived rules referencing prior rules, unless on computed values.
**Example H — decomposed shipping pipeline**
```lemma
spec shipping_policy
uses lemma units
data destination_country: text
-> option "NL"
-> option "BE"
-> option "DE"
-> option "FR"
-> suggest "NL"
data customer_tier: text
-> option "standard"
-> option "silver"
-> option "gold"
-> option "platinum"
-> suggest "gold"
data destination_region: text
data is_expedited: boolean
data is_hazardous: boolean
data is_po_box: boolean
data item_weight: units.mass
data order_total: number -> minimum 0
rule base_shipping_rate: 35
unless destination_country is "NL" then 22
unless destination_country is "BE" then 25
unless destination_country is "DE" then 28
unless destination_country is "FR" then 30
rule weight_surcharge: 0
unless item_weight > 5 kilogram then 7.5
unless item_weight > 20 kilogram
then veto "Item too heavy for standard shipping"
rule po_box_fee: 0
unless is_po_box then 5
rule expedited_fee: 0
unless is_expedited then 25
unless is_expedited and item_weight > 10 kilogram then 45
rule hazardous_fee: 0
unless is_hazardous then 50
unless is_hazardous and destination_country is not "NL"
then veto "Cannot ship hazardous materials internationally"
rule customer_discount: 0%
unless customer_tier is "silver" then 10%
unless customer_tier is "gold" then 20%
unless customer_tier is "platinum" then 30%
rule free_shipping_eligible:
order_total >= 100 and destination_country is "NL"
rule shipping_before_discount:
base_shipping_rate + weight_surcharge + po_box_fee
+ expedited_fee + hazardous_fee
rule shipping_discount_amount:
shipping_before_discount * customer_discount
rule final_shipping:
shipping_before_discount - shipping_discount_amount
unless free_shipping_eligible then 0
rule ships_to_location: true
unless is_po_box and is_hazardous
then veto "Cannot ship hazardous materials to PO boxes"
unless destination_region is "Svalbard"
then veto "Shipping not available to Svalbard"
rule Summary: "Standard shipping"
unless free_shipping_eligible
then "Free shipping (order over €100)"
unless is_expedited then "Expedited shipping"
```
Each fee is distinct rule. `item_weight` uses `units.mass`. Veto clauses placed last in `weight_surcharge` and `ships_to_location`. Eligibility separated from amount rules.
---
**Veto: impossible to answer, not `false`**
Veto is like Rust's `Err` — rule has **no value** and propagates to dependents. Use veto when answer is impossible, not when business answer is false.
| Situation | Use |
|-----------|-----|
| Invalid/out-of-domain input | `unless ... then veto "reason"` |
| Unmapped choice / no rule applies | default `veto` + unless arm per choice |
| Normal business "no" | `false` or `no` |
| Test veto without propagating | `x is veto` (returns boolean) |
Place veto unless clauses **last** to override other branches.
**Example I — enumeration with default veto**
```lemma
spec choice_mapping
data choice_field: number
rule selected: veto
unless choice_field is 1 then true
unless choice_field is 2 then false
unless choice_field is 3 then true
```
Default `veto` if unlisted. Each `unless` maps known choice. `false` is valid answer for choice 2.
**Example J — veto lookup + propagation**
```lemma
spec coffee_pricing
data money: measure
-> unit eur 1.00
-> decimals 2
data product: text
data size: text
rule base_price: veto "Unknown type of coffee"
unless product is "espresso" then 2.5 eur
unless product is "latte" then 3.5 eur
unless product is "cappuccino" then 3.5 eur
unless product is "mocha" then 4 eur
rule size_multiplier: veto "Unknown size of coffee"
unless size is "small" then 80%
unless size is "medium" then 100%
unless size is "large" then 120%
rule price_per_cup: base_price * size_multiplier
```
If `base_price` vetoes, `price_per_cup` vetoes automatically (propagates).
**Example K — veto vs boolean**
WRONG — veto for business decision:
```lemma-skip
rule can_checkout: veto
unless fee <= 10 eur then true
```
RIGHT — veto for invalid input, boolean for business logic:
```lemma
spec checkout_policy
data money: measure
-> unit eur 1.00
-> decimals 2
data customer_age: number
data fee: money
rule age_validation:
true
unless customer_age < 18 then veto "Customer must be 18 or older"
unless customer_age > 120 then veto "Invalid age"
rule can_checkout: yes
unless customer_age < 18 then no
unless fee > 10 eur then no
```
**Example L — veto propagation with unless fallback**
```lemma
spec scoring
data score: number
data use_default: boolean
rule validated_score: score
unless score < 0 then veto "Invalid score"
rule result: validated_score
unless use_default then 50
```
If `validated_score` vetoes but `use_default` is true, `result` is 50. Unless branch avoids needing vetoed value.
**Workflow checklist**
1. **Scope**: one spec per coherent policy; compose with `uses`; skip `repo` unless needed.
2. **Inputs**: every user-supplied fact is `data` with constraints.
3. **Outputs**: every answerable question is a `rule`.
4. **Factor**: intermediate calculations as named rules.
5. **Unless**: default first, general to specific, vetoes last.
6. **No inline comments**: no `#`, `//`. Commentary `"""..."""` only as first block after `spec`.
7. **Validate**: verify reachable data with `show`, missing inputs via `run`, and correct override order.
8. **Advanced**: use `uses lemma units`, ranges, compound units when needed.
---
**Anti-patterns**
Inline comments (WRONG — `#` fails to parse):
```lemma-skip
data customer_age: number -> minimum 0 -> maximum 120 # input slot
data tax_rate: 21% # fixed policy constant
rule discount: 0% # default: no discount
unless qty >= 10 then 10% # bulk rate
```
Commentary after `uses` (WRONG — fails to parse):
```lemma-skip
spec wholesale_nuts_pricing
uses lemma units
"""
B2B pricing calculation for a wholesale nuts supplier.
Handles product base pricing, organic premiums, and freight shipping.
"""
data base_price: number
```
Commentary after `data` (WRONG — fails to parse):
```lemma-skip
spec pricing
data qty: number
"""
This is not valid commentary placement.
"""
rule discount: 0%
```
Commentary before `uses` (RIGHT):
```lemma
spec wholesale_nuts_pricing
"""
B2B pricing for a wholesale nuts supplier.
Handles base pricing, organic premiums, volume discounts, and freight.
"""
uses lemma units
data base_price: number
```
Commentary immediately after spec (RIGHT — no `uses`):
```lemma
spec pricing
"""
Customer age is an input; tax_rate is a fixed policy constant.
"""
data customer_age: number -> minimum 0 -> maximum 120
data tax_rate: 21%
data qty: number
rule discount: 0%
unless qty >= 10 then 10%
```
Mega-rule (WRONG):
```lemma-skip
rule final_total:
base_price * qty
- base_price * qty * (0% unless qty >= 10 then 10% unless is_premium then 20%)
+ (15 unless base_price * qty >= 100 then 10 unless base_price * qty >= 200 then 0)
```
Decomposed pipeline (RIGHT):
```lemma
spec order_pricing
data base_price: number -> minimum 0
data qty: number -> minimum 0
rule subtotal: base_price * qty
rule discount_percentage: 0% unless qty >= 10 then 10%
rule discount_amount: subtotal * discount_percentage
rule total_after_discount: subtotal - discount_amount
rule shipping_cost: 15 unless total_after_discount >= 100 then 10
rule final_total: total_after_discount + shipping_cost
```
Hardcoded input (WRONG):
```lemma-skip
rule discount: 10 * 0.1
```
Input as data (RIGHT):
```lemma
spec bulk_discount
data qty: number -> minimum 0
rule discount: qty * 0.1
```
Wrong unless order (WRONG — VIP gets 20%, not 25%):
```lemma-skip
rule discount: 0%
unless is_vip then 25%
unless qty >= 50 then 20%
```
Correct order (RIGHT):
```lemma
spec tiered_discount
data qty: number
data is_vip: boolean
rule discount: 0%
unless qty >= 50 then 20%
unless is_vip then 25%
```
Placeholder (WRONG):
```lemma-skip
data customer_name: "TODO"
```
Type-only input (RIGHT):
```lemma
spec customer_record
data customer_name: text
```
Error vs Veto: `5 and "text"` is planning Error (invalid Lemma). `unless age > 120 then veto "Invalid age"` is runtime Veto (valid spec, domain no-value). Do not confuse.
Unnecessary repo (WRONG):
```lemma-skip
repo default
spec pricing
data qty: number
rule discount: 0%
unless qty >= 10 then 10%
```
Single-file spec (RIGHT):
```lemma
spec pricing
data qty: number
rule discount: 0%
unless qty >= 10 then 10%
```
---
## Docs
- [Learn guide](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/readme.md): specs, data, rules, unless, veto
- [Reference](https://github.com/lemma/lemma/blob/main/cli/documentation/reference/readme.md): operators, types, units, ranges, `uses`
- [Veto](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/types_and_units.md#veto): propagation, `is veto`
- [Composing specs](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/composing_specs.md): `uses`, temporal versions, pins
- [CLI](https://github.com/lemma/lemma/blob/main/cli/documentation/reference/cli.md): `run`, `show`, `format`
- [Registry](https://github.com/lemma/lemma/blob/main/cli/documentation/reference/registry.md): LemmaBase `@user/repo` imports
- [LemmaBase search](https://lemmabase.com/search?q=): registry search
- [Examples (docs)](https://github.com/lemma/lemma/tree/main/cli/documentation/examples): `.lemma` files
- [Examples (tests)](https://github.com/lemma/lemma/tree/main/cli/tests/integrations/examples): progressives
## Examples by pattern
- [Coffee order](https://github.com/lemma/lemma/blob/main/cli/documentation/examples/01_coffee_order.lemma): constraints, unless, veto enum
- [Library fees](https://github.com/lemma/lemma/blob/main/cli/documentation/examples/02_library_fees.lemma): unless chains, bool vs veto
- [Rules and unless](https://github.com/lemma/lemma/blob/main/cli/tests/integrations/examples/02_rules_and_unless.lemma): progressive unless
- [Spec references](https://github.com/lemma/lemma/blob/main/cli/tests/integrations/examples/03_spec_references.lemma): compound units, `uses lemma units`
- [Unit conversions](https://github.com/lemma/lemma/blob/main/cli/tests/integrations/examples/04_unit_conversions.lemma): duration, `as` conversion
- [Spec composition](https://github.com/lemma/lemma/blob/main/cli/tests/integrations/examples/11_spec_composition.lemma): hierarchy
- [Registry references](https://github.com/lemma/lemma/blob/main/cli/tests/integrations/examples/12_registry_references.lemma): registry import
- [Shipping policy](https://github.com/lemma/lemma/blob/main/cli/tests/integrations/examples/07_shipping_policy.lemma): decomposed rules
## Numeric precision (summary)
Exact rationals (ℚ) for `+`, `−`, `×`, `÷`, comparisons, conversions. `^`, `sqrt`, trig, `log` use ~28-significant-digit Decimal. `floor`/`ceil`/`round` on measures use Decimal.
**Boundary number contract:** WASM, HTTP JSON, Elixir NIF accept integers as numbers, reject float/decimal numbers. Pass decimals as strings. Example: `{ "quantity": 42, "rate": "0.075" }`. On Maven (JDK 21+), pass `BigDecimal` or decimal strings via `RunRequest.data(...)`; output is `BigDecimal`.
## Optional
- [LemmaBase](https://lemmabase.com): registry
- [Numeric precision](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/precision.md): exact rational arithmetic, boundary contract
- [JavaScript / TypeScript (WASM)](https://github.com/lemma/lemma/blob/main/cli/documentation/tools/javascript.md): browser and Node
- [Maven (Java / Kotlin)](https://github.com/lemma/lemma/blob/main/cli/documentation/tools/maven.md): Central JNI package
- [README](https://github.com/lemma/lemma/blob/main/README.md): install, quick start