rslc compiler.
The RSL pages quote these example raters. Each one passes rslc check. All carriers, rates and rules in them are synthetic.
rslc examples lists them, and rslc examples get NAME saves one to a folder.
minimal
The smallest rater that passes a strict check: one input, one cited constant and one rounded premium.
minimal/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Staffing Liability",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Constants = {
rate_per_1000 [cite: manual:p3] = 2
}
Records = {
Policy = {
inputs {
payroll: Num {precision: int} where self in [0, 100000000]
},
step rating {
premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
dwelling
The tutorial’s dwelling-fire rater: a rate table with protection-class bands, a rate for each peril, a list of scheduled items and a coded input rule.
dwelling/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Dwelling Fire (tutorial)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
Peril = {
fire,
wind,
theft
},
ProtectionClass = IntBands(1, 4, 7, 10..),
Money = Num {precision: 0.01}
}
Constants = {
expense_fee [cite: manual:p9] = 35
}
Tables = {
BaseRate [cite: manual:p3] :
table dims (protection: ProtectionClass) -> {
rate: Rate[Peril]
}
columns: {
rate[fire]: "fire",
rate[wind]: "wind",
rate[theft]: "theft"
}
}
Records = {
Policy = {
inputs {
dwelling: Dwelling,
items: List(Item)
where len(self) in [0, 10] [code: "TOO-MANY-ITEMS", message: "A policy schedules at most ten items.", cite: manual:p6]
},
step premium {
base_rate[p: Peril]: Rate =
lookup!(BaseRate, self.dwelling.protection_class).rate[p],
peril_premium[p: Peril]: Premium [cite: manual:p8] =
round(self.dwelling.coverage_a / 1000 * self.base_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)
}
},
Dwelling = {
inputs {
coverage_a: Money where self in [50000, 2000000],
protection_class: Num {precision: int}
where self in [1, 10] [code: "PROTECTION-CLASS", message: "Protection class must be 1 to 10.", cite: manual:p4]
}
},
Item = {
inputs {
value: Money where self in [0.01, 50000]
},
step item {
charge: Premium [cite: manual:p6] = round(self.value * 0.015, 2, half_up)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
dwelling/tables/BaseRate.csv
protection,fire,wind,theft
1,2.10,0.80,0.40
4,2.60,0.80,0.45
7,3.40,0.85,0.50
10,5.20,0.90,0.60
staffing
A staffing-liability rater and the guideline that uses it: roles, a question that a role owns, an eligibility rule, two referrals and a limit that each quote sets for itself. The two scripts play a submission from open to bind.
staffing/application.json
{
"operation": "staffing",
"years_in_operation": 2,
"policy": {
"payroll": 2500000,
"limit": 1000000
}
}
staffing/bind.script
# A two-year-old staffing firm: the manager signs off the new venture, the underwriter quotes and binds.
program "guideline.rsl"
as assistant:intake
open
answer file "application.json"
# The referral is known before the premium is.
expect referral new_venture Open
expect open policy.experience, new_venture
refused NOT_AUTHORIZED: answer policy.experience = no_claims
as uw:alice
answer policy.experience = no_claims because "Loss runs, five years"
expect outcome premium = 5000
refused NOT_QUOTABLE: quote q1
refused NOT_AUTHORIZED: answer new_venture = Approved
as manager:morgan
answer new_venture = Approved because "Experienced owners."
as uw:alice
expect stage Quotable
quote q1
expect quote q1 stands
# More payroll: the quote q1 was for other answers, the sign-off still stands.
answer policy.payroll = 3000000
expect referral new_venture Approved
expect quote q1 lapsed
refused NOT_QUOTED: bind q1
# A changed answer the referral reads makes the sign-off lapse.
answer years_in_operation = 1
expect referral new_venture Lapsed
as manager:morgan
answer new_venture = Approved because "Still acceptable."
as uw:alice
# A quote is kept: the new one takes a new name, and bind names it.
refused NOT_QUOTABLE: quote q1
quote q2
bind q2
expect stage Bound
expect quote q2 bound
expect outcome premium = 6000
staffing/guideline.rsl
spec {
rsl: "0.4",
carrier: "Example Mutual",
state: PA,
product: "Staffing Liability Underwriting Guideline",
version: "0.1.0",
effective_date: 2026-01-01
}
use ts = "rater.rsl"
Roles = { assistant < uw < manager }
Acts = { quote: @Role.uw, bind: @Role.uw }
Sources = {
guideline: Source = {
artifact: artifact("0c1f6f43-1f55-4a55-8a53-7d1b6f0e2a22"),
kind: pdf
}
}
Types = {
Operation = {
staffing,
peo
}
}
Constants = {
established_years [cite: guideline:p1] = 3,
large_premium [cite: guideline:p2] = 10000
}
Records = {
Submission = {
inputs {
operation: Operation [cite: guideline:p1],
years_in_operation: Num {precision: int} where self in [0, 300] [cite: guideline:p1],
financially_sound: Bool by @Role.uw when self.operation = @Operation.peo [cite: guideline:p1],
policy: ts.Policy,
policy.experience by @Role.uw
},
step premium [cite: guideline:p2] {
premium: Premium = ts(self.policy).premium
}
} where self.operation != @Operation.peo or (self.years_in_operation >= established_years and self.financially_sound) [code: "PEO_NOT_ESTABLISHED", message: "A PEO must be in operation for at least 3 years and be financially sound.", cite: guideline:p1]
refer new_venture when self.years_in_operation < established_years
to @Role.manager because "The firm is in operation for less than 3 years." [cite: guideline:p1]
refer large_account when self.premium >= large_premium
to @Role.manager because "The premium is $10,000 or more." [cite: guideline:p2]
}
RatingOutcomes = {
premium: Submission.premium
}
staffing/rater.rsl
spec {
rsl: "0.4",
carrier: "Example Mutual",
state: PA,
product: "Staffing Liability",
version: "0.1.0",
effective_date: 2026-01-01
}
Sources = {
rules: Source = {
artifact: artifact("5b0e7c0e-6f0a-4c56-9d39-0d1f1a1c7e11"),
kind: pdf
}
}
Types = {
Experience = {
no_claims,
claims
}
}
Constants = {
rate_per_1000 [cite: rules:p1] = 2,
claims_factor [cite: rules:p2] = 1.5,
basic_limit [cite: rules:p3] = 1000000,
higher_limit_factor [cite: rules:p3] = 1.1
}
Records = {
Policy = {
inputs {
payroll: Num {precision: int} where self in [0, 100000000] [cite: rules:p1],
experience: Experience [cite: rules:p2],
limit: Num {precision: int} per quote where self in [1000000, 2000000] [cite: rules:p3]
},
step rating [cite: rules:p1] {
experience_factor: Num = if self.experience = @Experience.no_claims then 1 else claims_factor,
limit_factor: Num = if self.limit > basic_limit then higher_limit_factor else 1,
premium: Premium [cite: rules:p1] =
round(self.payroll / 1000 * rate_per_1000 * self.experience_factor * self.limit_factor, 2, half_up)
}
} where self.payroll >= 1000 [code: "PAYROLL_BELOW_MINIMUM", message: "The payroll must be at least $1,000.", cite: rules:p1]
}
RatingOutcomes = {
premium: Policy.premium
}
staffing/stale.script
# A correction lands between the manager's read and the sign-off: a sign-off based on
# the read before it is refused, and one based on a read that holds it stands.
program "guideline.rsl"
as assistant:intake
open
answer file "application.json"
as uw:alice
answer policy.experience = no_claims because "Loss runs, five years"
# The manager reads the submission here, at entry 6: two years in operation.
as assistant:agent
answer years_in_operation = 1 because "Corrected from the website"
as manager:morgan
refused STALE: based on 6: answer new_venture = Approved because "Two years, experienced owners"
based on 7: answer new_venture = Approved because "One year, experienced owners"
expect stage Quotable
questions
A renters rater whose inputs carry their questions: question text, follow-up inputs, a named list, a choice and an input that a host fills.
questions/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Renters (questions example)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
Money = Num {precision: 0.01},
Zip = Code(pattern: "[0-9]{5}"),
County = {
maricopa,
pima,
other
},
ItemKind = {
jewelry,
bicycle,
instrument [label: "musical instrument"]
}
}
Constants = {
base_premium [cite: manual:p2] = 120,
rate_per_1000 [cite: manual:p2] = 4.5,
urban_county_factor [cite: manual:p3] = 1.1,
old_building_factor [cite: manual:p3] = 1.15,
business_charge [cite: manual:p3] = 40,
equipment_rate [cite: manual:p3] = 0.02,
visitor_charge [cite: manual:p3] = 25,
item_rate [cite: manual:p4] = 0.012,
bicycle_rate [cite: manual:p4] = 0.03
}
Records = {
Policy = {
inputs {
postal_code: Zip [ask: "What is the ZIP code of the home you rent?", format: "postal-code", group: "Home"],
zip_county: County [filled_by: "zip_lookup(postal_code).county"],
county: County from self.zip_county [ask: "Which county is the home in?", group: "Home"],
year_built: Num {precision: int} [ask: "What year was the building built?", help: "The lease or the landlord states the year.", format: "year", group: "Home"]
where self in [1800, 2100],
home_business: Bool [ask: "Do you run a business from the home?", help: "Count paid work you do at home for your own customers.", group: "Business"],
business_property: Num {precision: int} when self.home_business [ask: "What is the business equipment worth?", format: "money", unit: "USD", group: "Business"]
where self in [0, 20000],
customers_visit: Bool when self.home_business [ask: "Do customers visit the home?", group: "Business"],
contents_limit: Num {precision: 1000} [ask: "How much contents coverage do you want?", choice, shape: "limit", api_default: "30000", unit: "USD", group: "Coverage"]
where self in [20000, 100000],
items: List(Item) keyed by item_id [ask: "Which valuables do you want to schedule?", help: "Add each item worth $1,000 or more.", group: "Valuables", called: "the {kind}|item {#}", complete: "Any other valuables?"]
where len(self) in [0, 5]
},
step premium {
county_factor: Num = if self.county = @County.maricopa then urban_county_factor else 1,
age_factor: Num = if self.year_built < 1980 then old_building_factor else 1,
contents_premium: Premium [cite: manual:p2] =
round(self.contents_limit / 1000 * rate_per_1000 * self.county_factor * self.age_factor, 2, half_up),
business_premium: Premium [cite: manual:p3] = if self.home_business then business_charge else 0,
equipment_premium: Premium [cite: manual:p3] =
if self.home_business then round(self.business_property * equipment_rate, 2, half_up) else 0,
visitor_premium: Premium [cite: manual:p3] =
if self.home_business and self.customers_visit then visitor_charge else 0,
item_premium: Premium [cite: manual:p4] = sum(self.items as i, i.charge),
premium: Premium [cite: manual:p5] =
round(base_premium + self.contents_premium + self.business_premium + self.equipment_premium + self.visitor_premium + self.item_premium, 0, half_up)
}
},
Item = {
inputs {
item_id: Num {precision: int} where self in [1, 99],
kind: ItemKind [ask: "What kind of item is item {#}?"],
value: Money [ask: "What is {this} worth?", format: "money", unit: "USD"]
where self in [1000, 25000]
},
step item {
rate: Rate = if self.kind = @ItemKind.bicycle then bicycle_rate else item_rate,
charge: Premium [cite: manual:p4] = round(self.value * self.rate, 2, half_up)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
renters
A renters rater for rating with missing answers and for the planner: a banded rate table, a follow-up input, a business rule and a choice.
renters/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Renters",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
Territory = {
metro,
suburban,
rural
},
Construction = {
frame,
masonry,
fire_resistive
},
ContentsBand = IntBands(10000, 25000, 50000..),
Deductible = IntValues(250, 500, 1000)
}
Constants = {
prior_insurance_credit [cite: manual:p4] = 0.95,
dog_surcharge [cite: manual:p5] = 25
}
Tables = {
BaseRate [cite: manual:p2] :
table dims (contents: ContentsBand) -> {
rate: Rate
},
BuildingFactor [cite: manual:p3] :
table dims (territory: Territory, construction: Construction) -> {
factor: Factor
}
otherwise [cite: manual:p3]: { factor: 1.20 },
DeductibleFactor [cite: manual:p4] :
table dims (deductible: Deductible) -> {
factor: Factor
}
}
Records = {
Policy = {
inputs {
insured_now: Bool [ask: "Do you have a renters policy now?", group: "Insurance", gives: "declarations_page"],
contents: Num {precision: int} [ask: "What are your belongings worth?", help: "Count furniture, clothing and electronics.", group: "Home", found_on: "declarations_page"]
where self in [10000, 100000],
territory: Territory [ask: "Where is the home?", group: "Home"],
construction: Construction [ask: "How is the building built?", group: "Home"],
has_dog: Bool [ask: "Do you have a dog?", group: "Household"],
dog_has_bitten: Bool when self.has_dog [ask: "Has the dog bitten anyone?", group: "Household"]
where not self [code: "DOG-BITE", message: "The program declines a dog with a bite history.", cite: manual:p5],
deductible: Deductible [choice, api_default: "500", ask: "Which deductible do you want?", group: "Coverage"]
},
step rating [cite: manual:p2] {
base: Rate = lookup!(BaseRate, self.contents).rate,
building_factor: Factor = lookup!(BuildingFactor, self.territory, self.construction).factor,
deductible_factor: Factor = lookup!(DeductibleFactor, self.deductible).factor,
prior_factor: Factor = if self.insured_now then prior_insurance_credit else 1,
dog_charge: Num = if self.has_dog then dog_surcharge else 0,
premium: Premium [cite: manual:p6] =
round(self.base * self.building_factor * self.deductible_factor * self.prior_factor + self.dog_charge, 0, half_up)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
renters/tables/BaseRate.csv
contents,rate
10000,120
25000,180
50000,260
renters/tables/BuildingFactor.csv
territory,construction,factor
metro,frame,1.30
metro,masonry,1.15
metro,fire_resistive,1.00
suburban,frame,1.10
suburban,masonry,1.00
rural,frame,1.20
rural,masonry,1.20
rural,fire_resistive,1.20
renters/tables/DeductibleFactor.csv
deductible,factor
250,1.10
500,1.00
1000,0.90
auto
A personal-auto rater that the language reference quotes: lists of drivers and vehicles, options, a union, maps, bands, interpolation, dates, follow-up inputs, questions and a scenario.
auto/enums/Territory.csv
ident,value,label,aliases
phoenix,phoenix,Phoenix metro,
tucson,tucson,Tucson metro,
remainder,remainder,Remainder of state,rest;other
auto/rater.rsl
# Example Mutual personal auto: a synthetic rater for the language reference.
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Personal Auto (reference example)",
version: "1.0.0",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000001"),
kind: pdf
},
rates: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000002"),
kind: xlsx
}
}
Types = {
Coverage = {
liability [label: "Liability"],
collision [label: "Collision"],
comprehensive [label: "Comprehensive"]
},
PhysicalDamage = Coverage where self in {@Coverage.collision, @Coverage.comprehensive},
Use = {
pleasure,
commute,
business
},
Territory = Enum,
Zip = Code(pattern: "[0-9]{5}"),
License = Code(pattern: "[A-Z][0-9]{8}"),
DriverAge = IntBands(..<21, 21, 25, 65..),
VehicleValue = DecimalBands(scale: 2; ..<10000.00, 10000.00, 25000.00, 50000.00..),
LiabilityLimit = IntValues(50000, 100000, 300000),
Deductible = IntValues(250, 500, 1000),
MileagePoint = IntValues(0, 5000, 10000, 15000, 20000),
Money = Num {precision: 0.01},
Miles = Num {precision: int}
where self in [0, 20000] [code: "MILEAGE", message: "The program writes up to 20,000 miles a year.", cite: manual:p4]
}
Constants = {
policy_fee [cite: manual:p12] = 25,
paid_in_full_factor [cite: manual:p11] = 0.95,
installment_charge [cite: manual:p11] = 6,
multi_car_factor [cite: manual:p10] = 0.90,
point_surcharge [cite: manual:p8] = 0.10,
young_driver_fee [cite: manual:p7] = 40,
old_vehicle_years [cite: manual:p9] = 15,
old_vehicle_factor [cite: manual:p9] = 0.80,
new_driver_years [cite: manual:p7] = 3,
new_driver_factor [cite: manual:p7] = 1.25,
telematics_floor [cite: manual:p10] = 0.80,
mature_age [cite: manual:p10] = 55,
mature_factor [cite: manual:p10] = 0.93,
enclosed_garage_factor [cite: manual:p10] = 0.95
}
Tables = {
TerritoryByZip [cite: manual:pp14-15] :
table dims (zip: Zip) -> {
territory: Territory
}
otherwise [cite: manual:p15]: { territory: "remainder" },
BaseRate [cite: rates:'Base rates'!A2:D4] :
table dims (territory: Territory) -> {
rate: Rate[Coverage]
}
columns: {
rate[liability]: "liability",
rate[collision]: "collision",
rate[comprehensive]: "comprehensive"
},
LimitFactor [cite: manual:p5] :
table dims (limit: LiabilityLimit) -> {
factor: Factor
},
DriverClass [cite: manual:p6] :
table dims (age: DriverAge, use: Use) -> {
factor: Factor
},
MileageFactor [cite: manual:p5] :
table dims (miles: MileagePoint) -> {
factor: Factor
},
ValueFactor [cite: manual:p9] :
table dims (value: VehicleValue) -> {
factor: Factor
},
DeductibleFactor [cite: manual:p9] :
table dims (deductible: Deductible, coverage: PhysicalDamage) -> {
factor: Factor
}
}
Stages = {
classify: "Classification" [cite: manual:p4, note: "The garaging ZIP sets the territory and each driver gets a class."],
vehicle: "Vehicle premium" [cite: manual:p9, note: "Each vehicle is rated for each coverage."],
policy: "Policy premium" [cite: manual:p11, note: "The vehicle premiums are summed and the discounts, payment plan and fee apply."]
}
Records = {
Policy [label: "Policy"] = {
inputs {
effective_date: Date [ask: "When does the policy start?", group: "Policy"]
where self in [2026-01-01, 2026-12-31],
expiration_date: Date [ask: "When does the policy end?", group: "Policy"]
where self in [2026-07-01, 2027-12-31],
garage: Garage,
liability_limit: LiabilityLimit [choice, shape: "limit", api_default: "100000", group: "Coverage"],
payment: PaidInFull | Installments,
telematics: Option(Telematics),
prior_insurance: Bool [ask: "Are the cars insured today?", group: "Policy"],
prior_lapse_days: Num {precision: int} when self.prior_insurance [ask: "How many days was the household uninsured in the last year?", group: "Policy"]
where self in [0, 365],
drivers: List(Driver) keyed by number [ask: "Who drives the cars?", group: "Drivers", complete: "Any more drivers?"]
where len(self) in [1, 4] [code: "DRIVER-COUNT", message: "A policy rates one to four drivers.", cite: manual:p3]
where unique(self as d, d.license) [code: "DUPLICATE-LICENSE", message: "Each driver has a distinct license.", cite: manual:p3]
where (count(self as d, d.named_insured) = 1) [code: "NAMED-INSURED", message: "A policy has one named insured.", cite: manual:p3],
vehicles: List(Vehicle) keyed by number [ask: "Which cars do you want to insure?", group: "Cars"]
where len(self) in [1, 4] [code: "VEHICLE-COUNT", message: "A policy rates one to four vehicles.", cite: manual:p3]
},
step territory in classify [label: "Territory and base rates"] {
territory: Classification(Territory) [label: "Territory", cite: manual:p14] =
lookup!(TerritoryByZip, self.garage.zip).territory,
base_rate[c: Coverage]: BaseRate(Rate) [label: "Base rate", cite: manual:p4] =
lookup!(BaseRate, self.territory).rate[c],
limit_factor: IncreasedLimits(Factor) [label: "Liability limit factor", cite: manual:p5] =
lookup!(LimitFactor, self.liability_limit).factor,
term_months: Num = diff_months(self.expiration_date, self.effective_date),
term_factor: Relativity(Factor) [label: "Term factor", cite: manual:p4] = self.term_months / 12
},
step household in classify [label: "Household"] {
youngest_age: Num = min(self.drivers as d, d.age),
highest_points: Num =
match argmax(self.drivers as d, d.points, then [cite: manual:p8]: d.age) {
Some(d) => d.points,
None => 0
},
points_factor: Surcharge(Factor) [label: "Points surcharge", cite: manual:p8] =
1 + point_surcharge * self.highest_points,
garage_factor: Credit(Factor) [label: "Enclosed garage factor", cite: manual:p10] =
1 exception when self.garage.enclosed then enclosed_garage_factor,
mature_discount: Credit(Factor) [label: "Mature driver discount", cite: manual:p10] =
match find(self.drivers as d, d.named_insured) {
Some(d) => if d.age >= mature_age then mature_factor else 1,
None => 1
},
young_drivers: Num = count(self.drivers as d, d.age < 25),
young_driver_charge: Charge(Premium) [label: "Young driver charge", cite: manual:p7] =
young_driver_fee * self.young_drivers
},
step discounts in policy [label: "Policy discounts"] {
multi_car_discount: Credit(Factor) [label: "Multi-car discount", cite: manual:p10] =
1 exception when len(self.vehicles) >= 2 then multi_car_factor,
telematics_discount: Credit(Factor) [label: "Telematics discount", note: "A safe-driving score lowers the premium, to the filed floor.", cite: manual:p10, collect: multi_car_discount..telematics_discount, title: "Policy discounts"] =
match self.telematics {
Some(t) => max(telematics_floor, 1 - t.score / 500),
None => 1
},
payment_factor: Credit(Factor) [label: "Payment plan factor", cite: manual:p11] =
match self.payment {
PaidInFull => paid_in_full_factor,
Installments(i) => 1
},
installment_fee: Fee(Premium) [label: "Installment fee", cite: manual:p11] =
match self.payment {
PaidInFull => 0,
Installments(i) => installment_charge * i.count
}
},
step premium in policy [label: "Policy premium"] {
vehicle_premium: Subtotal(Premium) [label: "Vehicle premium"] = sum(self.vehicles as v, v.premium),
discounted_premium: Subtotal(Premium) [label: "Discounted premium"] =
self.vehicle_premium * self.term_factor * self.multi_car_discount * self.mature_discount * self.telematics_discount * self.payment_factor,
premium: PolicyPremium(Premium) [label: "Policy premium", cite: manual:p12] =
round(self.discounted_premium + self.young_driver_charge + self.installment_fee + policy_fee, 0, half_up)
}
} where not self.prior_insurance or self.prior_lapse_days <= 30 [code: "LAPSE", message: "The program accepts a lapse of 30 days or fewer.", cite: manual:p3]
where self.expiration_date = add_months(self.effective_date, 6) or self.expiration_date = add_years(self.effective_date, 1) [code: "TERM", message: "The program writes six-month and annual terms.", cite: manual:p4],
Garage = {
inputs {
zip: Zip [ask: "Where are the cars parked overnight?", help: "A five-digit ZIP code.", group: "Policy", format: "postal-code"],
enclosed: Bool [ask: "Are the cars parked in an enclosed garage?", group: "Policy"]
}
},
PaidInFull = {
},
Installments = {
inputs {
count: Num {precision: int} where self in [2, 12]
}
},
Telematics = {
inputs {
score: Num {precision: int} where self in [0, 100]
}
},
Driver [label: "Driver"] = {
inputs {
number: Num {precision: int} where self in [1, 99],
license: License,
named_insured: Bool,
birth_date: Date where self in [1900-01-01, 2100-12-31],
licensed_date: Date where self in [1900-01-01, 2100-12-31],
points: Num {precision: int}
where self in [0, 6] [code: "POINTS", message: "The program writes drivers with six points or fewer.", cite: manual:p8]
},
step class in classify [label: "Driver class"] {
age: Num = diff_years(Policy.effective_date, self.birth_date),
years_licensed: Num = diff_years(Policy.effective_date, self.licensed_date),
experience_factor: Surcharge(Factor) [label: "New driver factor", cite: manual:p7] =
1 exception when self.years_licensed < new_driver_years then new_driver_factor
}
},
Vehicle [label: "Vehicle"] = {
inputs {
number: Num {precision: int} where self in [1, 99],
model_year: Num {precision: int} where self in [1981, 2027],
value: Money where self in [500, 250000],
use: Use,
annual_miles: Miles,
deductible: Deductible [choice, shape: "deductible", api_default: "500", group: "Coverage"],
principal: Option(Ref(Driver))
},
step driver in vehicle [label: "Rated driver"] {
rated_age: Num =
match self.principal {
Some(d) => d.age,
None => Policy.youngest_age
},
experience_factor: Factor =
match self.principal {
Some(d) => d.experience_factor,
None => 1
}
},
step factors in vehicle [label: "Vehicle factors"] {
class_factor: Relativity(Factor) [label: "Driver class factor", cite: manual:p6] =
lookup!(DriverClass, self.rated_age, self.use).factor,
mileage_factor: Relativity(Factor) [label: "Mileage factor", cite: manual:p5] =
interpolate!(MileageFactor, self.annual_miles, ratio: (4, half_up), step: (4, half_up), result: (3, half_up)).factor,
value_factor: Relativity(Factor) [label: "Vehicle value factor", cite: manual:p9] =
lookup!(ValueFactor, self.value).factor,
age_factor: Credit(Factor) [label: "Vehicle age factor", cite: manual:p9] =
1 exception when year(Policy.effective_date) - self.model_year >= old_vehicle_years then old_vehicle_factor,
coverage_factor[c: Coverage]: Composite(Factor) [label: "Coverage factor"] =
match c {
@Coverage.liability => Policy.limit_factor,
@Coverage.collision => self.value_factor * self.age_factor * lookup!(DeductibleFactor, self.deductible, c).factor,
@Coverage.comprehensive => self.value_factor * Policy.garage_factor * lookup!(DeductibleFactor, self.deductible, c).factor
}
},
step premium in vehicle [label: "Vehicle premium"] {
coverage_premium[c: Coverage]: Subtotal(Premium) [label: "Coverage premium", cite: manual:p9] =
round(
Policy.base_rate[c] * self.class_factor * self.experience_factor * self.mileage_factor * Policy.points_factor * self.coverage_factor[c],
2,
half_up
),
premium: Subtotal(Premium) [label: "Vehicle premium"] = sum(Coverage as c, self.coverage_premium[c])
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
auto/scenario.rsl
import "rater.rsl"
# One household: two drivers, one car, paid in full.
Scenario = {
instances = {
Policy p = {
effective_date = 2026-03-01,
expiration_date = 2027-03-01,
liability_limit = 100000,
prior_insurance = true,
prior_lapse_days = Some(0),
payment = PaidInFull
},
Garage g = {
__parent: p,
zip = "85004",
enclosed = true
},
Driver d1 = {
__parent: p,
number = 1,
license = "D12345678",
named_insured = true,
birth_date = 1968-05-20,
licensed_date = 1986-06-01,
points = 0
},
Driver d2 = {
__parent: p,
number = 2,
license = "D87654321",
named_insured = false,
birth_date = 2006-09-12,
licensed_date = 2024-10-01,
points = 2
},
Vehicle v1 = {
__parent: p,
number = 1,
model_year = 2021,
value = 27500.00,
use = @Use.commute,
annual_miles = 12000,
deductible = 500,
principal: d2
}
}
evaluate = {
targets: [
v1.rated_age,
v1.mileage_factor,
p.premium
]
}
assert = {
p.territory = @Territory.phoenix,
v1.rated_age = 19,
p.premium = 2240
}
}
auto/tables/BaseRate.csv
territory,liability,collision,comprehensive
phoenix,410.00,290.00,120.00
tucson,360.00,250.00,105.00
remainder,300.00,210.00,95.00
auto/tables/DeductibleFactor.csv
deductible,coverage,factor
250,collision,1.15
250,comprehensive,1.10
500,collision,1.00
500,comprehensive,1.00
1000,collision,0.85
1000,comprehensive,0.90
auto/tables/DriverClass.csv
age,use,factor
..,pleasure,1.60
..,commute,1.75
..,business,1.90
21,pleasure,1.20
21,commute,1.30
21,business,1.45
25,pleasure,1.00
25,commute,1.05
25,business,1.15
65,pleasure,1.05
65,commute,1.10
65,business,1.25
auto/tables/LimitFactor.csv
limit,factor
50000,0.90
100000,1.00
300000,1.18
auto/tables/MileageFactor.csv
miles,factor
0,0.800
5000,0.900
10000,1.000
15000,1.120
20000,1.300
auto/tables/TerritoryByZip.csv
zip,territory
85001,phoenix
85004,phoenix
85701,tucson
auto/tables/ValueFactor.csv
value,factor
..,0.85
10000.00,1.00
25000.00,1.20
50000.00,1.55
auto-extras
A homeowners rater with the forms that auto leaves out: every source kind, enumeration narrowing, value lists, a geography, select and an override.
auto-extras/enums/ProtectionClass.csv
value
1
2
3
4
5
6
7
8
9
10
auto-extras/rater.rsl
# Example Mutual homeowners: the forms the personal auto example leaves out.
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Homeowners (reference extras)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000011"),
kind: pdf
},
rates: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000012"),
kind: csv
},
site: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000013"),
kind: html
},
filing: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000014"),
kind: zip
},
scan: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000015"),
kind: png
},
cells: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000016"),
kind: parquet
}
}
Types = {
Construction = {
frame = "F" [label: "Frame"],
masonry = "M" [label: "Masonry"],
fire_resistive = "FR" [label: "Fire resistive"]
},
Combustible = Construction - { fire_resistive },
CellId = Code(pattern: "C[0-9]{3}"),
Zip = Code(pattern: "[0-9]{5}"),
WindShare = DecimalValues(scale: 2; 0.01, 0.02, 0.05),
ProtectionClass = Num {precision: int, values: Csv} where self in [1, 10],
Money = Num {precision: 0.01},
Coordinate = Num {precision: 0.000001}
}
Constants = {
rate_per_1000 [cite: rates:row 2] = 3.10,
base_amount [cite: manual:p2] = 250000,
protection_load [cite: manual:p4] = 0.03,
age_load [cite: manual:p5] = 1.02,
schedule_low [cite: site:anchor "schedule-rating"] = 0.75,
schedule_high [cite: site:anchor "schedule-rating"] = 1.25,
new_business_factor [cite: filing:member "rules/new-business.pdf"] = 1.05,
claim_load [cite: scan:image] = 0.08,
winter_factor [reason: "Example Mutual applies its seasonal load by underwriting bulletin."] = 1.03
}
Tables = {
Cell [cite: cells:rows 1-4] :
table dims (cell: CellId) -> {
factor: Factor,
latitude: Coordinate,
longitude: Coordinate
},
ZipFactor [cite: rates:rows 3-6] :
table dims (zip: Zip) -> {
factor: Factor
},
WindCredit [cite: manual:p7] :
table dims (share: WindShare, shuttered: Bool) -> {
credit: Pct
},
ConstructionFactor [cite: manual:p4] :
table dims (construction: Combustible) -> {
factor: Factor
}
}
Geographies = {
WildfireGrid [cite: manual:p9] = degree_grid {
cell: 0.01,
origin_latitude: 0,
origin_longitude: 0,
cells: Cell,
latitude: latitude,
longitude: longitude,
on_line: least_key
}
}
Records = {
Policy = {
inputs {
effective_date: Date where self in [2026-01-01, 2026-12-31],
expiration_date: Date where self in [2026-01-02, 2027-12-31],
latitude: Coordinate where self in [31, 37],
longitude: Coordinate where self in [-115, -109],
zip: Key(ZipFactor),
coverage_a: Money [description: "The dwelling limit.", unit: "USD"]
where self in [50000, 2000000],
coverage_c: Money [description: "The contents limit.", unit: "USD"] where self in [0, 1500000],
construction: Construction,
protection_class: ProtectionClass,
dwelling_age: Num {precision: int} where self in [0, 200],
wind_deductible: WindShare,
shuttered: Bool,
prior_claims: Option(Num) {precision: int} where self in [0, 5],
schedule_position: Num {precision: 0.01} where self in [0, 1],
underwriter_factor: Option(Num) {precision: 0.01} where self in [0.5, 1.5]
},
step base [cite: manual:p2] {
units: Num = ceil(self.coverage_a / 1000),
size_factor: Factor [cite: manual:p2] = root(self.coverage_a / base_amount, 2, 4, half_up),
term_days: Num = diff_days(self.expiration_date, self.effective_date),
term_factor: Factor = self.term_days / 365,
base_premium: Premium = self.units * rate_per_1000 * self.size_factor * self.term_factor
},
step location [cite: manual:p9] {
wildfire_factor: Factor =
match locate(WildfireGrid, self.latitude, self.longitude) {
Some(c) => lookup!(Cell, c).factor,
None => 1
},
zip_factor: Factor = lookup!(ZipFactor, self.zip).factor,
season_factor: Factor = 1 exception when month(self.effective_date) in {12, 1, 2} then winter_factor
},
step dwelling [cite: manual:p4] {
construction_factor: Factor =
match self.construction {
@Construction.fire_resistive => 1,
@Construction.frame => lookup!(ConstructionFactor, @Combustible.frame).factor,
@Construction.masonry => lookup!(ConstructionFactor, @Combustible.masonry).factor
},
protection_factor: Factor = 1 + protection_load * self.protection_class,
age_decades: Num = floor(self.dwelling_age / 10),
age_factor: Factor = pow(age_load, self.age_decades),
wind_credit: Pct = lookup!(WindCredit, self.wind_deductible, self.shuttered).credit
},
step judgment [cite: manual:p11] {
claims_factor: Factor = 1 + claim_load * c with self.prior_claims as c otherwise new_business_factor,
schedule_factor: Factor =
select(schedule_low..schedule_high, self.schedule_position)
overridden by self.underwriter_factor
},
step premium [cite: manual:p12] {
premium: Premium [cite: manual:p12] =
round(
self.base_premium * self.wildfire_factor * self.zip_factor * self.season_factor * self.construction_factor * self.protection_factor
* self.age_factor * (1 - self.wind_credit) * self.claims_factor * self.schedule_factor,
0,
half_up
)
}
} where self.coverage_c <= 70% of self.coverage_a [code: "CONTENTS-LIMIT", message: "Contents coverage is at most 70 percent of the dwelling limit.", cite: manual:p3]
where self.expiration_date <= add_days(self.effective_date, 366) [code: "TERM", message: "A term is at most 366 days.", cite: manual:p3]
where self.expiration_date > self.effective_date [code: "TERM-ORDER", message: "The term ends after it starts.", cite: manual:p3]
}
RatingOutcomes = {
premium: Policy.premium
}
auto-extras/tables/Cell.csv
cell,factor,latitude,longitude
C001,1.40,33.455000,-112.075000
C002,1.25,33.455000,-112.065000
C003,1.10,33.465000,-112.075000
C004,1.60,34.545000,-112.465000
auto-extras/tables/ConstructionFactor.csv
construction,factor
F,1.00
M,0.90
auto-extras/tables/WindCredit.csv
share,shuttered,credit
0.01,false,0.00
0.01,true,0.02
0.02,false,0.03
0.02,true,0.05
0.05,false,0.08
0.05,true,0.10
auto-extras/tables/ZipFactor.csv
zip,factor
85001,1.10
85004,1.05
85701,0.98
86301,0.95
fn-tables
A homeowners rater that the function reference quotes for lookup! and interpolate!.
fn-tables/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Homeowners (table functions)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
Territory = {
metro,
suburban,
rural
},
Construction = {
frame,
masonry
},
HomeAge = IntBands(0, 10, 25, 50..),
Deductible = IntValues(500, 1000, 2500),
AmountPoint = IntValues(100000, 200000, 300000, 500000, 750000),
Zip = Code(pattern: "[0-9]{5}")
}
Tables = {
BaseRate [cite: manual:p2] :
table dims (territory: Territory, construction: Construction) -> {
rate: Rate
},
HomeAgeFactor [cite: manual:p3] :
table dims (age: HomeAge) -> {
factor: Factor
},
DeductibleFactor [cite: manual:p3] :
table dims (deductible: Deductible) -> {
factor: Factor
},
ZipFactor [cite: manual:p4] :
table dims (zip: Zip) -> {
factor: Factor
}
otherwise [cite: manual:p4]: { factor: 1 },
AmountFactor [cite: manual:p5] :
table dims (amount: AmountPoint) -> {
factor: Factor
},
HailFactor [cite: manual:p6] :
table dims (territory: Territory, amount: AmountPoint) -> {
factor: Factor
}
}
Records = {
Policy = {
inputs {
territory: Territory,
construction: Construction,
zip: Zip,
home_age: Num {precision: int} where self in [0, 200],
deductible: Deductible,
coverage_a: Num {precision: int} where self in [100000, 750000]
},
step base [cite: manual:p2] {
base_rate: Rate = lookup!(BaseRate, self.territory, self.construction).rate
},
step factors [cite: manual:p3] {
home_age_factor: Factor = lookup!(HomeAgeFactor, self.home_age).factor,
deductible_factor: Factor = lookup!(DeductibleFactor, self.deductible).factor,
zip_factor: Factor = lookup!(ZipFactor, self.zip).factor
},
step amount [cite: manual:p5] {
amount_factor: Factor [cite: manual:p5] =
interpolate!(AmountFactor, self.coverage_a, ratio: (4, half_up), step: (4, half_up), result: (3, half_up)).factor,
hail_factor: Factor [cite: manual:p6] =
interpolate!(HailFactor, self.territory, self.coverage_a, ratio: exact, step: exact, result: (3, half_up)).factor
},
step premium [cite: manual:p7] {
premium: Premium [cite: manual:p7] =
round(
self.base_rate * self.home_age_factor * self.deductible_factor * self.zip_factor * self.amount_factor
* self.hail_factor,
0,
half_up
)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
fn-tables/tables/AmountFactor.csv
amount,factor
100000,0.700
200000,1.000
300000,1.270
500000,1.780
750000,2.350
fn-tables/tables/BaseRate.csv
territory,construction,rate
metro,frame,820
metro,masonry,740
suburban,frame,690
suburban,masonry,620
rural,frame,910
rural,masonry,830
fn-tables/tables/DeductibleFactor.csv
deductible,factor
500,1.10
1000,1.00
2500,0.85
fn-tables/tables/HailFactor.csv
territory,amount,factor
metro,100000,1.00
metro,200000,1.02
metro,300000,1.03
metro,500000,1.05
metro,750000,1.06
suburban,100000,1.00
suburban,200000,1.03
suburban,300000,1.05
suburban,500000,1.08
suburban,750000,1.10
rural,100000,1.05
rural,200000,1.10
rural,300000,1.14
rural,500000,1.20
rural,750000,1.25
fn-tables/tables/HomeAgeFactor.csv
age,factor
0,0.90
10,1.00
25,1.12
50,1.30
fn-tables/tables/ZipFactor.csv
zip,factor
85001,1.08
85250,0.96
86001,1.15
fn-geography
A wildfire rater that the function reference quotes for locate and Some.
fn-geography/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Homeowners Wildfire (locate)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
CellId = Code(pattern: "[A-Z][0-9]{3}")
}
Constants = {
base_premium [cite: manual:p2] = 900,
unmapped_factor [cite: manual:p4] = 1.5
}
Tables = {
WildfireCell [cite: manual:p3] :
table dims (cell: CellId) -> {
factor: Factor,
latitude: Num,
longitude: Num
}
}
Geographies = {
WildfireGrid [cite: manual:p3] = degree_grid {
cell: 0.01,
origin_latitude: 0,
origin_longitude: 0,
cells: WildfireCell,
latitude: latitude,
longitude: longitude,
on_line: least_key
}
}
Records = {
Policy = {
inputs {
latitude: Num {precision: exact} where self in [-90, 90],
longitude: Num {precision: exact} where self in [-180, 180],
surveyed_cell: Option(Key(WildfireCell))
},
step territory [cite: manual:p3] {
rated_cell: Option(CellId) =
match self.surveyed_cell {
Some(c) => Some(c),
None => locate(WildfireGrid, self.latitude, self.longitude)
},
wildfire_factor: Factor =
match self.rated_cell {
Some(c) => lookup!(WildfireCell, c).factor,
None => unmapped_factor
}
},
step premium [cite: manual:p5] {
premium: Premium [cite: manual:p5] = round(base_premium * self.wildfire_factor, 2, half_up)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
fn-geography/tables/WildfireCell.csv
cell,factor,latitude,longitude
W101,1.10,33.455,-112.075
W102,1.25,33.455,-112.065
W201,0.95,33.465,-112.075
W202,1.40,33.465,-112.065
fn-numbers
A general-liability rater that the function reference quotes for root, pow, abs, floor, ceil and select.
fn-numbers/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "General Liability (number functions)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
Money = Num {precision: 0.01}
}
Constants = {
rate_per_unit [cite: manual:p2] = 3.25,
base_revenue [cite: manual:p3] = 1000000,
annual_trend [cite: manual:p3] = 1.03,
full_credibility_claims [cite: manual:p4] = 400,
mod_tolerance [cite: manual:p4] = 0.05,
claim_free_credit [cite: manual:p5] = 0.02,
schedule_low [cite: manual:p6] = 0.75,
schedule_high [cite: manual:p6] = 1.25
}
Records = {
Policy = {
inputs {
revenue: Money where self in [50000, 50000000],
trend_years: Num {precision: int} where self in [0, 5],
claim_count: Num {precision: int} where self in [0, 2000],
loss_ratio: Num {precision: 0.001} where self in [0, 5],
claim_free_months: Num {precision: int} where self in [0, 600],
risk_position: Num {precision: 0.01} where self in [0, 1]
},
step exposure [cite: manual:p2] {
exposure_units: Num = ceil(self.revenue / 1000),
size_factor: Factor [cite: manual:p3] = pow(self.revenue / base_revenue, -0.15, 4, half_up),
trend_factor: Factor = pow(annual_trend, self.trend_years)
},
step experience [cite: manual:p4] {
credibility: Factor [cite: manual:p4] =
min(root(self.claim_count / full_credibility_claims, 2, 2, half_up), 1),
experience_mod: Factor = self.credibility * self.loss_ratio + 1 - self.credibility,
mod_swing: Num = abs(self.experience_mod - 1),
applied_mod: Factor = if self.mod_swing <= mod_tolerance then 1 else self.experience_mod
},
step credits [cite: manual:p5] {
claim_free_years: Num = min(floor(self.claim_free_months / 12), 5),
claim_free_discount: Factor = 1 - claim_free_credit * self.claim_free_years,
schedule_factor: Factor = select(schedule_low..schedule_high, self.risk_position)
},
step premium [cite: manual:p7] {
premium: Premium [cite: manual:p7] =
round(
self.exposure_units * rate_per_unit * self.size_factor * self.trend_factor * self.applied_mod
* self.claim_free_discount * self.schedule_factor,
0,
half_up
)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
fn-folds
A commercial-property rater that the function reference quotes for sum, product, min and max.
fn-folds/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Commercial Property (fold functions)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
Coverage = {
building,
contents,
income
},
Protection = {
sprinkler,
alarm
},
Adjustment = {
large_limit,
old_building
},
Money = Num {precision: 0.01}
}
Constants = {
building_rate [cite: manual:p2] = 1.2,
contents_rate [cite: manual:p2] = 2.5,
income_rate [cite: manual:p2] = 0.8,
sprinkler_factor [cite: manual:p3] = 0.85,
alarm_factor [cite: manual:p3] = 0.95,
large_limit [cite: manual:p4] = 1000000,
large_limit_factor [cite: manual:p4] = 0.9,
old_building_year [cite: manual:p4] = 1970,
old_building_factor [cite: manual:p4] = 1.1,
lowest_modifier [cite: manual:p5] = 0.75,
highest_modifier [cite: manual:p5] = 1.25,
minimum_premium [cite: manual:p6] = 500
}
Records = {
Policy = {
inputs {
locations: List(Location) keyed by number
where len(self) in [1, 5] [code: "LOCATION-COUNT", message: "A policy covers one to five locations.", cite: manual:p1],
modifiers: List(Modifier) keyed by number
where len(self) in [0, 4] [code: "MODIFIER-COUNT", message: "A policy takes at most four schedule modifiers.", cite: manual:p5],
endorsements: List(Endorsement)
where len(self) in [0, 3] [code: "ENDORSEMENT-COUNT", message: "A policy takes at most three endorsements.", cite: manual:p5]
},
step rates [cite: manual:p2] {
rate[c: Coverage]: Rate =
match c {
@Coverage.building => building_rate,
@Coverage.contents => contents_rate,
@Coverage.income => income_rate
}
},
step account [cite: manual:p4] {
largest_limit: Num = max(self.locations as l, l.largest_limit),
oldest_year: Num = min(self.locations as l, l.year_built),
adjustment[k: Adjustment]: Factor =
match k {
@Adjustment.large_limit => if self.largest_limit >= large_limit then large_limit_factor else 1,
@Adjustment.old_building => if self.oldest_year < old_building_year then old_building_factor else 1
},
account_factor: Factor = product(Adjustment as k, self.adjustment[k])
},
step modification [cite: manual:p5] {
schedule_modifier: Factor [cite: manual:p5] =
product(self.modifiers as m by m.number, m.factor, round_each: (3, half_up)),
capped_modifier: Factor = max(min(self.schedule_modifier, highest_modifier), lowest_modifier),
endorsement_factor: Factor = product(self.endorsements as e, e.factor)
},
step premium [cite: manual:p6] {
location_premium: Premium [cite: manual:p6] =
sum(self.locations as l by l.number, l.premium, round_each: (0, half_up)),
modified_premium: Premium =
self.location_premium * self.account_factor * self.capped_modifier * self.endorsement_factor,
premium: Premium [cite: manual:p6] = round(max(self.modified_premium, minimum_premium), 0, half_up)
}
},
Location = {
inputs {
number: Num {precision: int} where self in [1, 99],
building_limit: Money where self in [0, 5000000],
contents_limit: Money where self in [0, 5000000],
income_limit: Money where self in [0, 5000000],
year_built: Num {precision: int} where self in [1850, 2026],
sprinklered: Bool,
alarmed: Bool
},
step exposure [cite: manual:p2] {
limit[c: Coverage]: Num =
match c {
@Coverage.building => self.building_limit,
@Coverage.contents => self.contents_limit,
@Coverage.income => self.income_limit
},
largest_limit: Num = max(Coverage as c, self.limit[c])
},
step premium [cite: manual:p3] {
coverage_premium[c: Coverage]: Premium [cite: manual:p2] =
round(self.limit[c] / 1000 * Policy.rate[c], 2, half_up),
credit[k: Protection]: Factor =
match k {
@Protection.sprinkler => if self.sprinklered then sprinkler_factor else 1,
@Protection.alarm => if self.alarmed then alarm_factor else 1
},
protection_credit: Factor = min(Protection as k, self.credit[k]),
coverage_total: Premium = sum(Coverage as c, self.coverage_premium[c]),
premium: Premium [cite: manual:p3] = round(self.coverage_total * self.protection_credit, 2, half_up)
}
},
Modifier = {
inputs {
number: Num {precision: int} where self in [1, 99],
factor: Num {precision: 0.001} where self in [0.75, 1.25]
}
},
Endorsement = {
inputs {
factor: Num {precision: 0.001} where self in [1, 1.5]
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
fn-lists
A household-auto rater that the function reference quotes for the list functions.
fn-lists/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Household Auto (list functions)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Types = {
License = Code(pattern: "[A-Z][0-9]{8}")
}
Constants = {
base_premium [cite: manual:p2] = 600,
points_load [cite: manual:p3] = 0.15,
youthful_factor [cite: manual:p3] = 1.2,
symbol_load [cite: manual:p3] = 0.02,
trailer_rate [cite: manual:p4] = 0.01,
additional_trailer_factor [cite: manual:p4] = 0.9,
multi_car_factor [cite: manual:p5] = 0.95,
claim_free_factor [cite: manual:p5] = 0.9,
mature_factor [cite: manual:p5] = 0.97,
inexperienced_factor [cite: manual:p5] = 1.1,
household_points_load [cite: manual:p5] = 0.01,
youthful_surcharge [cite: manual:p6] = 75,
filing_fee [cite: manual:p6] = 25
}
Records = {
Policy = {
inputs {
drivers: List(Driver) keyed by number
where len(self) in [1, 6] [code: "DRIVER-COUNT", message: "A policy rates one to six drivers.", cite: manual:p1]
where unique(self as d, d.license) [code: "DUPLICATE-LICENSE", message: "Each driver has a distinct license.", cite: manual:p1],
vehicles: List(Vehicle) keyed by number
where len(self) in [1, 4] [code: "VEHICLE-COUNT", message: "A policy rates one to four vehicles.", cite: manual:p1],
trailers: List(Trailer)
where len(self) in [0, 3] [code: "TRAILER-COUNT", message: "A policy rates at most three trailers.", cite: manual:p1]
},
step household [cite: manual:p5] {
rated_drivers: List(Driver) = filter(self.drivers as d, not d.excluded),
average_age: Num =
if len(self.rated_drivers) > 0
then avg(self.rated_drivers as d, d.age)
else 0,
mature_discount: Factor = if self.average_age >= 50 then mature_factor else 1,
claim_free: Bool = all(self.drivers as d, d.points = 0),
claim_free_discount: Factor = if self.claim_free then claim_free_factor else 1,
multi_car_discount: Factor = if len(self.vehicles) >= 2 then multi_car_factor else 1,
newest_driver_age: Num =
match argmin(self.drivers as d, d.years_licensed) {
Some(d) => d.age,
None => 99
},
inexperienced_surcharge: Factor = if self.newest_driver_age < 21 then inexperienced_factor else 1,
household_points: Num = sum(topn(self.drivers as d, d.points, 2) as d, d.points),
points_surcharge: Factor = 1 + household_points_load * self.household_points,
extra_vehicle_factor: Factor =
match argmax(self.drivers as d, d.points, then [cite: manual:p4]: d.class_factor) {
Some(d) => d.class_factor,
None => 1
}
},
step charges [cite: manual:p6] {
youthful_count: Num = count(self.drivers as d, d.age < 25),
youthful_charge: Premium = youthful_surcharge * self.youthful_count,
has_filing: Bool = exists(self.drivers as d, d.filing),
filing_charge: Premium = if self.has_filing then filing_fee else 0
},
step premium [cite: manual:p7] {
vehicle_premium: Premium = sum(self.vehicles as v, v.premium),
trailer_premium: Premium = sum(self.trailers as t, t.charge),
premium: Premium [cite: manual:p7] =
round(
self.vehicle_premium * self.multi_car_discount * self.claim_free_discount * self.mature_discount
* self.inexperienced_surcharge * self.points_surcharge
+ self.trailer_premium + self.youthful_charge + self.filing_charge,
0,
half_up
)
}
},
Driver = {
inputs {
number: Num {precision: int} where self in [1, 99],
license: License,
age: Num {precision: int} where self in [16, 99],
years_licensed: Num {precision: int} where self in [0, 83],
points: Num {precision: int} where self in [0, 12],
excluded: Bool,
filing: Bool
},
step class [cite: manual:p3] {
age_factor: Factor = if self.age < 25 then youthful_factor else 1,
class_factor: Factor = (1 + points_load * self.points) * self.age_factor,
rating_rank: Num = rank(Policy.drivers as d, d.class_factor, self.number, desc)
}
},
Vehicle = {
inputs {
number: Num {precision: int} where self in [1, 99],
symbol: Num {precision: int} where self in [1, 30]
},
step assignment [cite: manual:p4] {
rating_rank: Num = rank(Policy.vehicles as v, v.symbol, self.number, desc),
driver_factor: Factor =
match find(Policy.drivers as d, d.rating_rank = self.rating_rank) {
Some(d) => d.class_factor,
None => Policy.extra_vehicle_factor
}
},
step premium [cite: manual:p7] {
symbol_factor: Factor = 1 + symbol_load * self.symbol,
premium: Premium [cite: manual:p7] =
round(base_premium * self.symbol_factor * self.driver_factor, 2, half_up)
}
},
Trailer = {
inputs {
value: Num {precision: int} where self in [500, 50000]
},
step charge [cite: manual:p4] {
position_factor: Factor = if index(self) = 0 then 1 else additional_trailer_factor,
charge: Premium = round(self.value * trailer_rate * self.position_factor, 2, half_up)
}
}
}
RatingOutcomes = {
premium: Policy.premium
}
fn-dates
A motorcycle rater that the function reference quotes for the date functions.
fn-dates/rater.rsl
spec {
rsl: "0.4.1",
carrier: "Example Mutual",
state: AZ,
product: "Motorcycle (date functions)",
effective_date: 2026-01-01
}
Sources = {
manual: Source = {
artifact: artifact("00000000-0000-4000-8000-000000000000"),
kind: pdf
}
}
Constants = {
annual_premium [cite: manual:p2] = 480,
youthful_factor [cite: manual:p3] = 1.4,
senior_factor [cite: manual:p3] = 1.15,
persistency_factor [cite: manual:p4] = 0.95,
advance_quote_factor [cite: manual:p4] = 0.97,
accident_load [cite: manual:p5] = 0.2,
vehicle_age_credit [cite: manual:p5] = 0.02,
riding_season_factor [cite: manual:p6] = 1.1
}
Records = {
Policy = {
inputs {
quote_date: Date where self in [2025-01-01, 2030-12-31],
effective_date: Date where self in [2026-01-01, 2030-12-31],
expiration_date: Date where self in [2026-01-02, 2031-12-31],
date_of_birth: Date where self in [1900-01-01, 2014-12-31],
insured_since: Date where self in [1950-01-01, 2030-12-31],
model_year: Num {precision: int} where self in [1980, 2031],
accidents: List(Accident)
where len(self) in [0, 5] [code: "ACCIDENT-COUNT", message: "A policy lists at most five accidents.", cite: manual:p5]
},
step rider [cite: manual:p3] {
age: Num = diff_years(self.effective_date, self.date_of_birth),
senior: Bool = self.effective_date >= add_years(self.date_of_birth, 65),
age_factor: Factor =
if self.age < 25 then youthful_factor else if self.senior then senior_factor else 1
},
step discounts [cite: manual:p4] {
months_insured: Num = diff_months(self.effective_date, self.insured_since),
persistency_discount: Factor = if self.months_insured >= 36 then persistency_factor else 1,
advance_quote: Bool = self.quote_date <= add_days(self.effective_date, -7),
advance_quote_discount: Factor = if self.advance_quote then advance_quote_factor else 1
},
step record [cite: manual:p5] {
chargeable_accidents: Num =
count(self.accidents as a, a.occurred >= add_months(self.effective_date, -36)),
accident_surcharge: Factor = 1 + accident_load * self.chargeable_accidents,
vehicle_age: Num = min(max(year(self.effective_date) - self.model_year, 0), 10),
vehicle_age_discount: Factor = 1 - vehicle_age_credit * self.vehicle_age
},
step premium [cite: manual:p6] {
riding_season: Bool = month(self.effective_date) >= 4 and month(self.effective_date) <= 9,
season_factor: Factor = if self.riding_season then riding_season_factor else 1,
term_days: Num = diff_days(self.expiration_date, self.effective_date),
premium: Premium [cite: manual:p6] =
round(
annual_premium * self.age_factor * self.persistency_discount * self.advance_quote_discount
* self.accident_surcharge * self.vehicle_age_discount * self.season_factor * self.term_days
/ 365,
2,
half_up
)
}
} where self.expiration_date > self.effective_date [code: "TERM-ORDER", message: "The term ends after it starts.", cite: manual:p1]
where self.expiration_date <= add_years(self.effective_date, 1) [code: "TERM-LENGTH", message: "A term runs at most one year.", cite: manual:p1],
Accident = {
inputs {
occurred: Date where self in [1950-01-01, 2030-12-31]
}
}
}
RatingOutcomes = {
premium: Policy.premium
}