Песочница

Пишем решающие правила

Когда выносить решение из приложения в небольшое правило на Lispex и как это делать. Пишем и тестируем правила одобрения возврата и ставки скидки.

Не всякий код является решающим правилом. Это руководство начинается с того, как выбрать, что выносить в правило, а затем показывает написание и тестирование двух примеров, одобрения возврата и ставки скидки. Все правила здесь остаются внутри проверяемого подмножества, с которым работает Lispex Vouch, поэтому один и тот же вход всегда даёт один и тот же ответ.

Что выносить в правило

Кандидатов отбирают три вопроса.

  • Попросят ли вас позже объяснить это решение? Отказы в возврате, ставки скидок и лимиты одобрения притягивают обращения в поддержку и аудиты, поэтому они хорошие кандидаты.
  • Можно ли перечислить все входные данные правила? Если решение сводится к нескольким значениям, например дням с момента доставки и признаку вскрытия, оно годится в правило. Значения, которые правилу пришлось бы запрашивать самому, например текущее время или остатки на складе, хост должен прочитать заранее и передать аргументами.
  • Даёт ли оно ответ, не трогая экран и хранилище? Правило только судит. Что делать с этим суждением, решает хост-приложение.

Три «да» дают хорошего кандидата. Если хотя бы одно «нет», оставьте эту часть в приложении и вырежьте суждение поменьше.

Форма функции правила

Правило состоит из одной функции, которая получает входные данные аргументами и возвращает ответ. Несколько привычек делают правила читаемыми.

  • Правилу с ответом «да или нет» дайте имя с вопросительным знаком на конце, как refund-auto-approved?.
  • Правило, возвращающее значение, назовите по ответу, как discount-rate.
  • Все входные данные принимайте аргументами. Ничего не читайте снаружи правила и ничего не меняйте внутри.

Пример 1. Одобрение возврата

Возврат одобряется автоматически в течение четырнадцати дней с доставки, если заказ не вскрыт.

LISPEX
(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  граничный день ещё одобряется
(refund-auto-approved? 15 #f)  ; ⇒ #f  на день позже
(refund-auto-approved? 3 #t)   ; ⇒ #f  вскрытый заказ

Держите тестовые вызовы прямо под правилом, и его границы станут документацией. Одобряется ли четырнадцатый день? Код отвечает сразу.

Пример 2. Ставка скидки

Для решений с несколькими ветками подходит cond. Пусть ставка скидки зависит от уровня участника и суммы корзины. Уровни записаны числами, 2 для золотого, 1 для серебряного, 0 для обычного.

LISPEX
(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  золотой уровень и крупная корзина
(discount-rate 2 50000)   ; ⇒ 10
(discount-rate 0 120000)  ; ⇒ 5
(discount-rate 0 30000)   ; ⇒ 0

cond проверяет ветки сверху вниз и отвечает первой, которая выполнилась. Порядок и есть приоритет, поэтому самое конкретное условие ставьте первым.

Как тестировать правило

Способов два, и оба вычисляет один и тот же эталонный интерпретатор.

Вставьте правило и тестовые вызовы в Песочницу, и результаты появятся сразу. Ставить ничего не нужно, что удобно на этапе поиска формы.

Чтобы держать правила в файлах, сохраните их, например, в rules.lspx и запустите CLI.

BASH
npm install -g lispex
lispex run rules.lspx

Если рядом с каждым тестовым вызовом записать ожидаемое значение комментарием, позже будет видно, что именно сдвинуло изменение правила.

Как остаться в проверяемом подмножестве

Примеры выше используют только define, and, cond и операторы сравнения, а этого уже достаточно, чтобы быть внутри подмножества. Когда правило растёт, держат его там две вещи.

  • Никаких макросов и расширений ридера. В проверяемом подмножестве они дают немедленную ошибку. Полный список допустимых форм есть в разделе «Синтаксис».
  • Используйте только аргументы, которые передал хост. Правило, которое само тянет время, случайные числа или внешние данные, ломает обещание «один вход, один ответ».

Дальше квитанции

Правила из этого руководства и есть материал для Lispex Vouch. Если правило влияет на реальное решение, сгенерируйте квитанцию нативным бинарником и храните её вместе с записью о решении. Квитанция показывает, какое правило было применено к каким данным и с каким результатом. Она не устанавливает, кто действовал на основании результата, когда это произошло и совпадает ли развёрнутое правило с проверенным. Эти вопросы требуют контроля за пределами Lispex. Команды lispex verify и lispex replay описаны на странице Lispex Vouch.