Skip to main content
This page states what rslc reads as RSL 0.4.1, form by form. Each excerpt quotes a checked example rater. Names lists every section, type and annotation key, and Functions explains every function. The language tour teaches the same forms in reading order. Two synthetic raters from the carrier “Example Mutual” supply the excerpts: auto and auto-extras. Examples shows both whole. Save and check them with these commands:

Find a form

Each row names something a rater or a guideline states, its shortest form and the section that defines it.

Notation

Each grammar block uses this notation:
A comma separates the items of every list. Between two items of a section, a record, an inputs block or a step, the comma is optional. rslc fmt writes one comma between items and leaves the last item bare.

Files and versions

An RSL program is one entry file plus the files it imports. Three kinds of entry file exist:
  • Sections stand in any order, and a name resolves wherever its declaration stands in the program. rslc fmt keeps the order you write.
  • A file may repeat a section. The compiler reads the repeats as one section.
  • Spaces and line ends only separate tokens.
  • A comment starts with # and runs to the end of its line.
  • use, Roles and Acts belong to a guideline. See Guidelines.

The spec block

A value is a literal or a bare word, so state: AZ and state: "AZ" both pass. Write product for the name a customer reads. The older field name gets E-RATER-METADATA-INVALID.

Versions

rslc reads the RSL 0.4 line: rsl: "0.4" and its patch rsl: "0.4.1". Any other version, or a spec that lacks rsl, gets one E-RSL-VERSION message and stops the check. Versions explains how a patch relates to its line.

import

  • The string is a path relative to the importing file, or a standard-library path that starts with std/.
  • An import reads every section of the named file into the program. Sections with the same name merge.
  • A scenario imports the rater it tests. A large rater imports files of types, constants or tables. See Split a rater into files.
The standard library ships inside the compiler:

package

A file whose first line is a package line is a shared-facts package: questions that several raters ask in the same words. It holds Types, Tables and records made of inputs only. See Shared facts.

Lexical elements

  • Case is syntax. Fields, constants, enumeration members, functions and steps start in lower case. Types, records, tables and sections start in upper case.
  • Keywords such as if, match, step and where are identifiers that the grammar reads by position.
  • A number is an exact decimal. Write the digits on both sides of the point: 0.5. Exponents and digit separators are outside the language.
  • A minus sign directly before digits belongs to the number. Write x - 1 with spaces: the compiler reads x -1 as two values and reports a parse error.
  • A date is a calendar day from 0001-01-01 to 9999-12-31.
  • A string appears in annotations, in a Code comparison, in otherwise cells, in import and use, and after nl.
The operators and punctuation:

Sources and citations

A source has exactly the two fields artifact and kind; anything else gets E-SOURCE-SCHEMA. The string in artifact(…) is the identifier of the stored document. The compiler checks each region’s form and leaves the document closed. A citation is the annotation [cite: source:region]. The region follows the kind of the source: reason states why a number that the filing leaves unstated is in the rater:
A citation that names an undeclared source, or a region in the wrong form, gets E-CITATION. Evidence belongs in citations: a comment that carries a page reference gets E-PROVENANCE-COMMENT. Citations explains what a citation establishes.

Types

A field states its type after its name. Types gives a type a name. The dimension forms and enumerations exist only as named types in Types.

Scalars

Premium, Rate, Factor and Pct are numbers with a stated meaning. Words are enumerations or Code types: RSL has left Text behind.

Numbers and precision

Every number is an exact rational: 1 / 3 stays one third until a round narrows it. Exact numbers gives the reasons. A numeric input states its precision in metadata after the type, and its range in a refinement:
  • Metadata follows the whole type: Option(Num) {precision: int}.
  • A numeric input needs both a precision and a range where self in [a, b], else the check reports E-INPUT-DOMAIN.
  • Either end of a range may be _ for an open side: [0, _].
  • A request value off the precision grid or outside the range is refused before rating. See Refusals.

Enumerations

  • @Coverage.collision names a member in an expression.
  • A member written frame = "F" has the value F: a request and a table cell hold the value. A plain member’s value is its name.
  • A member takes a label, the words a person reads.
  • Combustible = Construction - { fire_resistive } declares a narrowed enumeration: the members of Construction minus the listed ones. Its members read @Combustible.frame.
  • An enumeration of more than 256 members belongs in a CSV file: E-DATA-AS-CODE.
Name = Enum reads the members from enums/Name.csv beside the file:
A CSV-backed enumeration may key a table. A map or a fold needs an enumeration declared in the file: E-MAP-DATA-SIZED. The annotation [listed] on the type lists its members in the input contract.

Bands and value lists

A band type splits the number line into bands. A value-list type holds exactly the listed numbers. Both type a table dimension. A value-list type also types an input, and a request with an unlisted value is refused.
  • A band runs from its cut to the next cut. With a plain last cut, as in IntBands(0, 10, 20), the bands are 0 to 9 and 10 to 19, and 20 is outside.
  • An open bottom is the first item and repeats its number in the next item. An open top is the last item. Cuts ascend.
  • A sentinel bound such as 999999 gets E-BAND-SENTINEL: write an open band.
  • A dimension lists at most 65,536 items.
A computed key of a band lookup must sit on the band’s grid: round it first, or declare key: unrounded. The check reports E-KEY-HOLE otherwise.

Codes

A Code type holds text that matches a pattern, such as a postal code or a license number:
  • The pattern is a regular expression over printable ASCII that the whole value must match.
  • A pattern holds literals, classes such as [A-Z0-9], groups, |, ? and counts such as {5} or {1,3}. * and + follow one literal or one class.
  • A pattern outside this set gets E-CODE-PATTERN.
  • Two codes compare with = and != only. A code compares with a string literal: self.zip = "85001".
  • A Code types an input and a table dimension. A column that holds codes of another table has the type Key(Table).

Option

Option(T) is a value that is present, Some(value), or absent, None.
  • A request leaves an optional field out, or sends null, for None.
  • A calculation reads an Option through match, through a rule clause, or with = None. Arithmetic on an Option is E-TYPE.
  • Some(x) makes a present value in a calculation of type Option(T).
  • A refinement on an optional field tests the present value.
  • A field with a filed default is an Option whose None arm holds the default.

List, Ref and Key

self.principal above is an Option of a driver: a match binds the driver and reads its fields. A plain Ref(Driver) field reads through the reference: self.principal.age. A lookup keyed by a Key(Table) value always finds its row, so the table works with its filed rows alone.

Unions

  • Each arm is a record that holds inputs and rules only.
  • A request sends the arm’s name as tag, beside the arm’s fields: {"tag": "Installments", "count": 6}.
  • A calculation reads a union with a match that names every arm. _ is unavailable here: E-UNION-USE.

Maps

A map holds one value for each member of an enumeration. A calculation declares one with an index, and a table column declares one with Type[Enumeration]:
  • c is the binder: inside the body it holds one member of Coverage.
  • A read names its key: self.base_rate[c] or self.base_rate[@Coverage.collision].
  • The key type is an enumeration declared in the file, or a refined one such as PhysicalDamage.
  • A read whose key may fall outside the map’s key type gets E-MAP-DOMAIN.
  • A fold visits every member: sum(Coverage as c, self.coverage_premium[c]).

Refinements

A refinement narrows a type to the values that pass a Bool expression. self is the value.
  • A field of a refined type inherits the rule. annual_miles: Miles refuses 30,000 with the code MILEAGE.
  • A value states one range, on its type or on its field: E-REFINEMENT.
  • A bound has one spelling, self in [a, b]. self >= a gets E-REFINE-CANONICAL.
  • c in PhysicalDamage tests membership of a refined enumeration in an expression.
  • Rules covers refinements on fields, lists and records, and their codes.

Constants

  • A constant is a fixed value with a citation, or with [reason: "…"].
  • A calculation reads a constant by its bare name. Constants.policy_fee is the long form.

Tables and lookups

Declaration

  • Every column and every dimension is read by a calculation, or the check reports E-TABLE-UNUSED and its relatives in the same topic.
  • An otherwise row that each lookup already covers gets E-OTHERWISE-DEAD.
  • An otherwise cell holds the text of a CSV cell: a number, true, false, or a string such as "remainder" for an enumeration value.

CSV files

The file tables/DriverClass.csv of the auto rater starts:
Read a filed table works through the layouts a filing uses.

lookup!

  • The arguments are the table and one key for each dimension, in the declared order. The suffix names one column, and [member] names one value of a map column.
  • The subscript of a map column is a binder or one member written @Coverage.collision. The binder belongs to the map calculation that holds the lookup, as c does above, or to a fold such as sum(Coverage as c, …).
  • Any other subscript, such as an input in .rate[self.coverage], gets E-MAP-DOMAIN.
  • To read the member that an input names, read the column into a map calculation as above, then index that map: self.base_rate[self.coverage]. Use a filed rate table shows both steps.
  • A number key selects its band or its listed value. An enumeration, Bool or Code key selects its exact row.
  • The check proves that every call finds a row. A key that may leave the dimension gets E-KEY-RANGE, and a reachable key that lacks a row gets E-LOOKUP-GAP.
  • Close a gap with a narrower key type, a filed row, an open band or an otherwise row.
  • The ! is part of the name. lookup(…) gets E-LOOKUP-SYNTAX.

interpolate!

  • The last dimension is a value-list type that holds the filed points. The last key is the value to read at.
  • At a filed point the result is that row’s value. Between two points p1 and p2 with values v1 and v2, the result is round(v1 + round(ratio * (v2 - v1))), where ratio = round((x - p1) / (p2 - p1)).
  • ratio, step and result state the three roundings in that order, each as (scale, mode). ratio and step may be exact.
  • With 12,000 miles the example reads 1.048: two fifths of the way from 1.000 to 1.120.
  • The key must lie inside the filed points, else the check reports E-INTERP-RANGE. A band dimension or an otherwise row gets E-INTERP-KEY.
Interpolate between rows shows a clamp around the read.

Geographies and locate

A geography maps a point to one cell of a grid. Each row of the cells table is one cell, with its key and one point inside it.
  • Every field of the chosen method is required.
  • locate(Geography, latitude, longitude) gives Some(key) for a point inside a filed cell and None elsewhere.
  • A Some key is a filed key, so the lookup that follows always finds its row.

Records

A record is one thing a request describes: a policy, a driver, a vehicle. It holds inputs, steps of calculations, and rules.

Inputs

  • The clauses follow the order of the grammar. keyed by stands before the metadata and the annotations.
  • An input takes a type and rules, and a calculation takes = and a value.
  • Every input is read by a rating outcome or by a rule, or the check reports E-UNREAD-INPUT.

Follow-up inputs

  • when makes an input a follow-up: the request gives it where the condition holds. Left out under a true condition, it is refused with INPUT-FOLLOW-UP.
  • Where the condition fails, the rater ignores a given value.
  • A read of a follow-up sits under its condition, as in the rule not self.prior_insurance or self.prior_lapse_days <= 30. Any other read gets E-FOLLOW-UP-READ.
  • A Bool follow-up follows the same rule: condition and answer counts an unasked question as false, and not condition or answer counts it as true.
  • The condition reads inputs of the same record. A list follow-up gets E-FOLLOW-UP-TYPE.

Lists and keys

  • keyed by number names an input of the item record. The key is distinct across the list, and a repeat is refused with INPUT-KEY.
  • A keyed list is a map from key to record, so one request rates the same way however its list is ordered. find, argmax, argmin, topn and rank break ties by the key, and they need a keyed list: E-SELECT-TIE.
  • index reads a position, so it takes a plain list. On a keyed list it gets E-KEYED-ORDER, and so does = between two lists of keyed records.
  • A Ref(Record) field names one item of a keyed list by its key. An unknown key is refused with INPUT-REF.
  • len(self) bounds the length and unique(self as d, d.license) states a second distinct field.
  • Rate a list works through folds over a list.

Steps and calculations

  • A step groups calculations under a name. A step takes in stage, cite, label, note and skip.
  • A calculation has a name, a type and one expression.
  • name[c: Enumeration] declares a map: one value for each member.
  • Calculations stand in any order. A cycle gets E-CYCLE.
  • Every calculation reaches a rating outcome, or the check reports E-ORPHAN-COMPUTED.
  • A child’s calculation that reads only its parent belongs on the parent: E-HOIST.
  • A refinement on a calculation’s type, where self in [a, b], is a claim that the check proves: E-COMPUTED-RANGE.
  • [skip: "id":"reason"] on a step marks a step of the manual that the rater leaves out.
The three rule clauses end a calculation’s body. They need rsl: "0.4.1".
A clause belongs at the end of a calculation. To use one inside a larger expression, name the value as its own calculation. A calculation whose value is still prose holds nl "text". The strict check fails while a hole remains, and rslc check --allow-incomplete reports the rest.

Rules

A rule is a refinement on an input, on a list or on a record. self is the value that the rule tests.
  • A rule that lacks a code is an input rule: the refusal is InvalidInputs with a code such as INPUT-RANGE.
  • cite is optional on every rule. A coded business rule and a plain range such as where self in [0, 6] both pass the check uncited.
  • A refusal of an uncited rule holds "cite": null.
  • A cite on a rule names a declared source and a region of that source’s kind, or the check reports E-CITATION.
  • A rule takes code, message and cite only. reason on a rule gets E-ANNOTATION.
  • code and message follow the rule’s expression. On the input’s own brackets they get E-ANNOTATION.
  • On an input, write an equality inside parentheses: where (count(self as d, d.named_insured) = 1). A bare = there ends the declaration.
  • A rule reads its own value and what lies below it. A rule that relates a child to its parent stands on the parent: E-REFINE-UPWARD.
  • N% of x is an exact share, for rules only: self.coverage_c <= 70% of self.coverage_a.
  • A rule may use dates, arithmetic, if, match, folds over its own lists, constants and lookup!.
  • Rating starts after every rule passes. A calculation always gives a value. Refusals lists each refusal code and the order of the checks.

Expressions

Precedence

From the loosest binding to the tightest:
  • = tests equality and != tests the opposite. == is a parse error.
  • not binds tighter than a comparison: not a = b reads as (not a) = b, a type error. Write not (a = b) or a != b.
  • not self.prior_insurance or self.prior_lapse_days <= 30 reads as (not …) or (… <= 30).
  • Both sides of a comparison have the same type. < and its relatives order numbers and dates.
  • Every division proves a divisor other than zero, or the check reports E-DIV-ZERO.

if and match

  • Both branches of an if, and every arm of a match, have the type of the calculation.
  • A match covers every case, or the check reports E-MATCH or E-OPTION-MATCH.
  • Inside an arm, the check knows the matched case: lookup!(DeductibleFactor, self.deductible, c) above passes because c is a PhysicalDamage member in those two arms.
  • An if that sorts a number into classes belongs in a band table: E-IF-AS-TABLE.

Field access, self and binders

  • list as d binds each item of a list to d for the rest of a call. Enumeration as c binds each member.
  • list as d by d.key adds the order of a stepwise fold.
  • A read of a record that stands outside the path to the root gets E-ANCESTOR-READ.

Membership

Dates

Dates compare with =, !=, <, <=, >, >= and in [a, b]. Arithmetic on dates goes through functions:
Work with dates derives ages and terms.

Functions

A call is name(arguments). Only lookup! and interpolate! take !. Functions gives the rules of each one. Numbers:
Folds and lists:
A fold that walks a list once for each item of the same list gets E-QUADRATIC when the walk reads the same value each time.

Rounding

  • Arithmetic is exact, and a value changes precision only where the file says round, root, pow with four arguments, round_each or interpolate!.
  • scale is a whole-number literal: the count of decimal places. A negative scale rounds to tens, hundreds and beyond: round(x, -3, half_up).
  • The mode is required.
  • Every numeric rating outcome passes through a round in a calculation that cites the rounding rule.
round(1.25, 1, half_up) is 1.3 and round(1.25, 1, half_even) is 1.2. Round a premium compares the modes.

Questions and choices

An input’s annotations say how a person is asked for it. They change the input contract and leave every premium as it is. Names lists each key with the contract field it writes, and Plan the next question shows a host that asks them.
A wrong placeholder, format or shape gets E-QUESTION.

from and filled_by

These two inputs come from the package of the next section:
  • x: T from expression suggests an answer. The expression reads inputs and tables of the same record, and the person accepts the suggestion or answers over it.
  • [filled_by: "function(input).field"] names a host function that fills the input, so the question is skipped.
  • Rating reads the answer and ignores the from expression. An input that only a from reads stays optional, and a calculation that reads it gets E-FROM.
  • x: T refines base places a question directly after base, an input of the same record.

Shared facts

A shared-facts package declares questions once for many raters. This package and the rater that binds it check together:
household/facts.rsl
household/rater.rsl
household/tables/ZipFactor.csv
household/tables/DeductibleFactor.csv
A rater imports the package and gets each package record’s inputs in its own record of the same name:
  • A rater binds a shared fact by its name alone. A second declaration gets E-FACT-REDECLARED, and an unknown name gets E-FACT-BINDING.
  • A shared fact that the rater leaves unread is optional in its requests.
  • A package holds Types, Tables and records of inputs: E-FACT-PACKAGE.
The package above uses the keys that serve a questionnaire: A host that shows the questions reads these keys as well. The input contract shows each one in a checked rater. In a rater that imports a package, each choice states its shape: E-CHOICE-SHAPE.

Outcomes

  • RatingOutcomes lists the calculations a program returns, each under a public name. The block holds one outcome or more.
  • A target is a calculation of type Premium, Num, Rate, Factor, Pct, Bool or an enumeration. One outcome or more is a Premium.
  • Each numeric outcome is rounded in a calculation that cites the rule.
  • A rater evaluates what its outcomes read. Results describes what a host returns.
  • A wrong target gets E-RATING-OUTCOME.

Stages, labels, notes and concepts

These forms describe the rating manual that the rater implements. They leave every premium as it is, and the check holds them to the program.
A concept wraps the type of a calculation, an input or a table column. The rating@2 vocabulary holds these concepts:
  • A concept must fit the value’s type and formula, or the check reports a message of the rating manual topic.
  • Once a rater uses labels or concepts, the check prints hints for values that lack them: H-LABEL-MISSING and H-CONCEPT-MISSING. A hint leaves the check passing.
  • In an RSL 0.4.1 file, arithmetic reads named values, constants and inputs. An inline lookup!, if, match or round inside arithmetic gets E-CHAIN: name it as its own calculation.

Scenarios

A scenario file imports a rater, builds one request from named instances and states what to evaluate:
The scenario’s request passes the same rules as a rating request. The file stands beside the rater that it imports. Test a rater covers expected premiums and refusals.

Guidelines

A guideline is an RSL file that uses a rater and adds who answers and who signs off. This excerpt comes from the shared example staffing:
  • A guideline reads a used rater through its inputs and its outcomes only: E-USE.
  • Every role that Acts, by or refer names is declared in Roles: E-ROLE.
  • A referral’s name is distinct: E-REFER.
  • In an input, by or per quote follows the type and its metadata, before refines, from and when.

Terms of a quote

This line comes from the rater of staffing. per quote marks an input as a term. Each quote of one submission may hold its own answer to a term. Every other input is a fact, and all quotes of the submission share one answer to it.
  • Only the submission log reads the mark. A rater rates the same with the mark and with the mark removed.
  • A rater and a guideline each mark their own inputs. A term of a used rater is a term in each guideline that uses it.
  • The mark covers its own input. On a record or an Option, it covers whether the record is present. A field of that record is a term only when the field carries the mark too.
  • An input takes by or per quote, one of the two. An input with both gets E-ROLE, because a role’s answer is a judgment that every quote shares.
rslc submission open holds the marks to three more rules, and each breach stops it with E-ROLE: Quote options describes how the log answers and quotes a term. Raters and guidelines explains the split, and The submission log records answers, sign-offs, quotes and binds.

What the check proves

rslc check accepts a file only when every rule on this page holds, every lookup finds a row, every division has a divisor other than zero and every request that passes the input rules gets a value for each outcome. Each message has a code, and Check messages explains each code with an example and its fix. What rslc checks and guarantees states what a passing check establishes.

Removed forms

rslc still reads these older spellings so that it reports the current one. Each row gets an error; Messages for older forms explains the codes.