> ## Documentation Index
> Fetch the complete documentation index at: https://docs.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# RSL at a glance

> Every part of an RSL rater on one page: blocks, types, inputs and rules, calculations, tables, rounding and citations.

[Install Relativities and download the synthetic examples](/relativities/setup) before running this page.

This page shows how to read and write each part of a rater. It uses the dwelling fire rater from
[Write your first rater](/relativities/rsl/tutorial/first-rater), plus two small raters for the forms that
rater skips.

## The parts of a rater file

A rater file holds a fixed set of blocks, in this order. Each block matches a part of the rating
manual.

| Block | Holds | In the manual |
| - | - | - |
| `spec` | the carrier, state, product and effective date | the cover page |
| `Sources` | the filed documents that the rater cites | the filing itself |
| `Types` | the names and value sets that the manual uses | the definitions |
| `Constants` | single filed numbers, such as a policy fee | a fee or a factor in the text |
| `Tables` | the filed rate tables | the rate pages |
| `Records` | the facts of a policy, its rules and its calculations | the rating steps |
| `RatingOutcomes` | the values that the rater returns, such as the premium | the final premium |

The `spec` block also holds the language version line. Copy it as the tutorial shows. A line that starts with `#` is a comment.

## Name the values the manual uses

The `Types` block names the sets of values that the manual uses:

```rsl theme={null}
Types = {
    Peril = {
        fire,
        wind,
        theft
    },
    ProtectionClass = IntBands(1, 4, 7, 10..),
    Money = Num {precision: 0.01}
}
```

* **An enum** lists named choices, such as the perils. A request sends a member by its name.
* **A band type** groups numbers into ranges. `IntBands(1, 4, 7, 10..)` makes the bands 1 to 3, 4 to 6, 7 to 9, and 10 and up.
* **A number type** gives a precision. `Money` holds whole cents.

An enum member can have a different name in a request. `fire_resistive = "fire-resistive"` makes
a request send `"fire-resistive"`:

```rsl theme={null}
    Construction = {
        frame,
        masonry,
        fire_resistive = "fire-resistive"
    },
```

`Premium`, `Factor` and `Rate` are built-in number types. Use them for premiums, multipliers and
rates.

## Declare the inputs

The **inputs** are the facts that a request sends. Each input has a type and, for a number, a
range. This rater shows each kind of input:

```rsl theme={null}
        inputs {
            term_months: Num {precision: int} where self in [6, 12] and self in {6, 12},
            effective: Date where self in [2026-01-01, 2027-12-31],
            coverage_a: Num {precision: 1000} where self in [50000, 2000000],
            construction: Construction,
            sprinklered: Bool,
            prior_claims: Option(ClaimCount),
            buildings: List(Building) keyed by number
                where len(self) in [1, 5] [code: "BUILDING-COUNT", message: "A policy covers one to five buildings.", cite: manual:p5]
        },
```

| Type | A request sends |
| - | - |
| `Num {precision: int}` | a whole number |
| `Num {precision: 1000}` | a multiple of 1,000 |
| `Date` | a date as a `"YYYY-MM-DD"` string |
| `Bool` | `true` or `false` |
| An enum | the name of a member, as a string |
| `Option(T)` | a value of type `T`, or `null` when it is unknown |
| `List(R)` | a list of records of type `R` |
| A record | a JSON object with that record's inputs |

`keyed by number` makes each building's `number` unique in its list. Each `Option` input needs a
`match` with a `None` arm and a `Some` arm before a calculation uses it.

## Add rules to the inputs

A **rule** is a `where` clause. The rater checks every rule before it calculates, and refuses a
request that breaks one.

| Rule | Accepts |
| - | - |
| `where self in [a, b]` | `a` to `b`, both included |
| `where self in (a, b]` | above `a`, up to `b` |
| `where self in [a, _]` | `a` and above |
| `where self in {6, 12}` | only the values in the set |
| `where len(self) in [1, 5]` | a list with one to five elements |

Each number input needs a range. The range tells the check which values to prove the
calculations for.

A rule from the filing gets a code, a message and a citation in brackets. The refusal carries all
three. A rule that reads several inputs goes after the record's closing brace:

```rsl theme={null}
    } where self.coverage_a in [_, 1000000] or self.sprinklered [code: "SPRINKLER-REQUIRED", message: "A dwelling over 1,000,000 needs sprinklers.", cite: manual:p5],
```

The sample download includes this rater in `relativities-examples/rsl/inputs`. Rate one good
request and one that breaks two rules:

```python theme={null}
import json

import rslc

rater = rslc.PreparedRater("relativities-examples/rsl/inputs")
policy = {
    "Policy": {
        "term_months": 12,
        "effective": "2026-03-01",
        "coverage_a": 300000,
        "construction": "fire-resistive",
        "sprinklered": False,
        "prior_claims": 1,
        "buildings": [{"number": 1}],
    }
}
print(rater.rate(json.dumps(policy)))
# {"ok":true,"outputs":{"Policy":{"buildings":[{}],"premium":862}}}
policy["Policy"].update(coverage_a=1200000, construction="fire_resistive", prior_claims=None)
print(rater.rate(json.dumps(policy)))
# {"ok":false,"refusal":{"error":"BusinessRejection","truncated":false,"violations":[{"cite":null,"code":"INPUT-ENUM","constraint":"construction: Enum(Construction)","message":"construction must be one of: frame, masonry, fire-resistive","path":"Policy.construction","value":"fire_resistive"},{"cite":"manual:p5","code":"SPRINKLER-REQUIRED","constraint":"Policy where self.coverage_a in [_, 1000000] or self.sprinklered","message":"A dwelling over 1,000,000 needs sprinklers.","path":"Policy","value":{"coverage_a":1200000,"sprinklered":false}}]}}
```

The second request breaks two rules, and the refusal lists both. [Refusals](/relativities/rsl/reference/refusals)
describes each part of a refusal.

## Write the calculations

A `step` block holds calculations. Each calculation has a name, a type, often a citation, and a
formula after `=`:

```rsl theme={null}
        step premium {
            peril_premium[p: Peril]: Premium [cite: manual:p8] =
                round(self.dwelling.coverage_a / 1000 * lookup!(BaseRate, self.dwelling.protection_class).rate[p], 2, half_up),
            item_charge: Premium [cite: manual:p6] = sum(self.items as i, i.charge),
            premium: Premium [cite: manual:p9] =
                round(sum(Peril as p, self.peril_premium[p]) + self.item_charge + expense_fee, 0, half_up)
        }
```

* `self.dwelling.coverage_a` reads an input of the record, or of a record inside it.
* `+`, `-`, `*` and `/` do arithmetic on exact decimals.
* `sum(self.items as i, i.charge)` adds a value over each item of a list. `product`, `min`, `max`, `avg` and `count` work the same way.
* `sum(Peril as p, ...)` adds a value over each member of an enum.
* The order of the calculations is free. The rater works out the order from what each formula reads.

### Choose a value

`if` picks between two values. `match` picks one value for each member of an enum, or for each
case of an `Option`:

```rsl theme={null}
            construction_factor: Factor [cite: manual:p7] =
                match self.construction {
                    @Construction.frame => 1.2,
                    @Construction.masonry => 1,
                    @Construction.fire_resistive => 0.9
                },
            claim_charge: Premium [cite: manual:p8] =
                match self.prior_claims {
                    None => 0,
                    Some(n) => 25 * n
                },
```

`@Construction.frame` names a member of the `Construction` enum. A `match` lists every case.

### Keep one value per peril

`peril_premium[p: Peril]` is a **map**: one value for each member of `Peril`. Read one value with
`self.peril_premium[p]`. A rater returns a map as a JSON object keyed by member.

A map can cover only some members. This rater gives a deductible credit to fire and wind only:

```rsl theme={null}
    PropertyPeril = Peril where self in {@Peril.fire, @Peril.wind},
```

```rsl theme={null}
            deductible_credit[p: PropertyPeril]: Factor [cite: manual:p5] =
                match p {
                    @Peril.fire => if self.deductible >= 2500 then 0.9 else 1,
                    @Peril.wind => if self.deductible >= 2500 then 0.85 else 1
                },
            peril_premium[p: Peril]: Premium [cite: manual:p8] =
                round(self.coverage_a / 1000 * lookup!(BaseRate, self.protection_class).rate[p] * (if p in PropertyPeril then self.deductible_credit[p] else 1), 2, half_up),
```

`if p in PropertyPeril` reads the credit only for fire and wind. The sample download includes this rater
in `relativities-examples/rsl/maps`:

```python theme={null}
rater = rslc.PreparedRater("relativities-examples/rsl/maps")
policy = {"Policy": {"coverage_a": 250000, "protection_class": 5, "deductible": 2500}}
print(rater.rate(json.dumps(policy), detailed=True))
# {"ok":true,"outputs":{"Policy":{"premium":868,"deductible_credit":{"fire":0.9,"wind":0.85},"peril_premium":{"fire":585,"wind":170,"theft":112.5}}}}
```

## Read a table

A table declares its key columns after `dims` and its output columns after `->`. Its rows live in
a CSV file named after the table:

```rsl theme={null}
    BaseRate [cite: manual:p3] :
        table dims (protection: ProtectionClass) -> {
            rate: Rate[Peril]
        }
        columns: {
            rate[fire]: "fire",
            rate[wind]: "wind",
            rate[theft]: "theft"
        }
```

`lookup!(BaseRate, self.dwelling.protection_class)` finds the row for a key. `.rate[p]` reads one
peril's rate from it. [Read a filed table](/relativities/rsl/how-to/read-a-filed-table) covers tables with
more keys and rows the filing leaves out.

## Round where the manual rounds

`round(x, 0, half_up)` rounds to whole dollars, with halves going up. The second number counts
decimal places, and the third names the rounding mode. RSL rounds only where a `round` says so.
[Round a premium](/relativities/rsl/how-to/round-a-premium) lists the modes.

## Cite the filing

Each filed number cites the place in the filing that prints it. `[cite: manual:p3]` points to
page 3 of the `manual` source. Tables, constants and rounding calculations each need one.
[Citations](/relativities/rsl/explanation/citations) explains the rules.

## Name the outcome

`RatingOutcomes` names the values that the rater returns. At least one of them is a `Premium`
calculation that rounds:

```rsl theme={null}
RatingOutcomes = {
    premium: Policy.premium
}
```

## Related

* [What rslc checks and guarantees](/relativities/rsl/explanation/checks-and-guarantees) explains what the check proves about these forms.
* [Check messages](/relativities/rsl/reference/check-messages) lists each message that `rslc check` prints.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.