A lightweight Lisp for decision rules
(Lispex)
( parens are punctuation, recursion is rhyme )
Do not bury complex refund policies or eligibility rules inside application code. Lispex lets you write decision logic as short, independent programs and get back answers as transparent data. Run it in your browser, inside a Node.js project, or on your local machine.
Here is a refund policy, all of it.
A request arrives as a list of named fields. Every answer, refusals included, comes back as a clean list you can print, store, and compare against any other answer.
(define (review request)
(let ((days (cdr (assq 'days request)))
(opened (cdr (assq 'opened request)))
(cents (cdr (assq 'cents request))))
(cond ((>= days 15) '(deny outside-window))
(opened '(deny opened-item))
((> cents 50000) '(escalate manual-review))
(else '(allow within-policy)))))
(map review
'(((days . 14) (opened . #f) (cents . 12900))
((days . 14) (opened . #f) (cents . 82000))
((days . 15) (opened . #f) (cents . 12900))))
; => ((allow within-policy) (escalate manual-review) (deny outside-window))Four strict constraints that keep Lispex predictable
Every if form requires an else arm. Built-in procedures form a closed list of 205 names. Higher-order procedures like map and filter accept exactly one list at a time. Nobody can define macros, so no file can quietly alter what a form means.
The value of these limits is clear. A rule you open a year later reads exactly as written, and when a program fails, it fails in the exact same way every time.
See the complete syntax at a glanceRuns in three places, powered by a single reference implementation.
The Playground runs inside your browser and your code never leaves it. The npm package runs Lispex inside a Node.js project. The downloadable program runs locally on your machine with the widest set of commands.
All three environments share the same single reference implementation written in Rust. The browser and npm reach it through WebAssembly, while the downloadable program runs it directly. What differs is the command scope and memory or recursion limits, and every runtime clearly reports its execution limits.
Hand someone a decision they can independently verify.
The downloadable program can digitally sign a record of a run. The executed rule, the input data, and the resulting answer are sealed together in a single signed record.
Whoever receives the record does not need to trust your claims or system blindly. They supply their own copy of the rule and input data, run it again on their own machine, and verify whether today’s answer matches the signed record. The verification key is chosen by the recipient, never taken from the record itself.
Passing verification is evidence of past execution, not automatic permission. Whether to issue a refund remains a decision your application makes.
This image holds an entire rule in its pixels.

(let ((input '((days . 14) (opened . #f))))
(let ((days (cdr (car input)))
(opened (cdr (car (cdr input)))))
(if (< days 15)
(if opened "deny" "allow")
"deny")))The pixels carry the exact source code below byte for byte and nothing else. You can recover the code on your machine or right here in the Playground. The format is fixed down to the byte, so screenshots or copies resized by other programs will not work.
Carrying bytes intact is the only thing it does. It is not a signature and it is not a guarantee of trust.