Not every piece of code is a decision rule. This guide starts with how to choose what belongs in one, then walks through writing and testing two examples, a refund approval and a discount rate. Every rule here stays inside the checked subset used by Lispex Vouch, so the same input always gives the same answer.
What belongs in a rule
Three questions pick the candidates.
- Might someone ask you to explain this judgement later? Refund refusals, discount rates, and approval limits attract support tickets and audits, which makes them good candidates.
- Can you list every input the rule reads? If the judgement comes down to a few values, like days since delivery and whether the package was opened, it can be a rule. Values the rule would have to look up itself, like the current time or live inventory, must be read by the host first and passed in as arguments.
- Does it produce an answer without touching a screen or a store? A rule only judges. What to do with the judgement stays in the host application.
Three yeses make a good candidate. If one answer is no, leave that part in the app and cut the judgement smaller.
The shape of a rule function
A rule is one function that takes its inputs as arguments and returns the answer. A few habits keep rules readable.
- Name a yes-or-no rule with a trailing question mark, like
refund-auto-approved?. - Name a value-producing rule after its answer, like
discount-rate. - Take every input as an argument. Read nothing outside the rule and change nothing inside it.
Example 1. Refund approval
A refund is approved automatically within fourteen days of delivery, unopened.
(define (refund-auto-approved? days-since-delivery opened?)
(and (<= days-since-delivery 14)
(not opened?)))
(refund-auto-approved? 9 #f) ; ⇒ #t
(refund-auto-approved? 14 #f) ; ⇒ #t the boundary day still approves
(refund-auto-approved? 15 #f) ; ⇒ #f one day late
(refund-auto-approved? 3 #t) ; ⇒ #f opened package
Keep the test calls right under the rule and its edges become documentation. Is day fourteen approved? The code answers directly.
Example 2. Discount rate
cond suits judgements that branch several ways. Say the discount rate depends on membership level and cart total. Levels are numbers, 2 for gold, 1 for silver, 0 for regular.
(define (discount-rate member-level cart-total)
(cond ((and (= member-level 2) (>= cart-total 100000)) 15)
((= member-level 2) 10)
((>= cart-total 100000) 5)
(else 0)))
(discount-rate 2 120000) ; ⇒ 15 gold, large cart
(discount-rate 2 50000) ; ⇒ 10
(discount-rate 0 120000) ; ⇒ 5
(discount-rate 0 30000) ; ⇒ 0
cond checks its clauses from the top and answers with the first one that holds. Order is priority, so the most specific condition goes first.
Testing a rule
There are two ways, and the same reference interpreter evaluates both.
Paste the rule and its test calls into the Playground and the results appear immediately. There is nothing to install, which suits the shaping stage.
To keep rules in files, save them as something like rules.lspx and run the CLI.
npm install -g lispex
lispex run rules.lspxWriting the expected value next to each test call as a comment lets you follow, by eye, exactly what a later change to the rule moves.
Staying inside the checked subset
The examples above use define, and, cond, and comparison operators, which already keeps them inside the subset. As rules grow, two things keep them there.
- No macros and no reader extensions. In the checked subset they are immediate errors. The full list of allowed forms is in Syntax.
- Use only the arguments the host passed in. A rule that fetches the time, random numbers, or external data on its own breaks the promise that the same input gives the same answer.
Receipts come next
The rules written in this guide are exactly what Lispex Vouch works with. When a rule drives a real decision, generate a receipt with the native binary and store it alongside the decision record. The receipt shows which rule ran against which datum and what it produced. It does not establish who acted on the result, when they did so, or whether the deployed rule matches the one you audited. Those questions require controls outside Lispex. The lispex verify and lispex replay commands are described on the Lispex Vouch page.