모든 코드가 결정 규칙이 되는 것은 아닙니다. 이 가이드는 어떤 판단을 규칙으로 떼어 낼지 고르는 기준에서 시작해, 환불 승인과 할인율이라는 두 예제로 규칙을 작성하고 시험하는 과정을 안내합니다. 여기서 다루는 규칙은 모두 리스펙스 바우치가 쓰는 검사 가능한 부분집합 안에 있으므로, 같은 입력에는 언제나 같은 답이 나옵니다.
어떤 판단을 규칙으로 떼어 낼까
규칙으로 떼어 낼 판단은 세 가지 질문으로 고릅니다.
- 나중에 누군가에게 설명해야 할 수 있는 판단인가. 환불 거절, 할인율, 승인 한도처럼 문의나 감사가 따라오는 판단이 좋은 후보입니다.
- 규칙이 읽을 입력을 빠짐없이 나열할 수 있는가. 배송 경과일과 개봉 여부처럼 입력이 몇 개의 값으로 정리되면 규칙이 됩니다. 현재 시각이나 실시간 재고처럼 규칙이 스스로 조회해야 하는 값은 호스트가 미리 읽어서 인자로 넘겨야 합니다.
- 화면이나 저장소를 건드리지 않고 답만 내는가. 규칙은 판단만 하고, 그 판단으로 무엇을 할지는 호스트 애플리케이션의 몫입니다.
셋 다 그렇다면 좋은 후보입니다. 하나라도 아니라면 그 부분은 앱에 남겨 두고, 판단만 더 작게 잘라 보세요.
규칙 함수의 모양
규칙은 입력을 인자로 받아 답을 반환하는 함수 하나입니다. 몇 가지 습관이 규칙을 읽기 쉽게 만듭니다.
- 예·아니오를 답하는 규칙에는
refund-auto-approved?처럼 물음표로 끝나는 이름을 붙입니다. - 값을 답하는 규칙에는
discount-rate처럼 답의 이름을 붙입니다. - 입력은 전부 인자로 받습니다. 규칙 밖의 값을 읽지 않고, 규칙 안에서 아무것도 바꾸지 않습니다.
예제 1. 환불 자동 승인
배송 후 14일 이내이고 미개봉이면 자동 환불을 승인합니다.
(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 개봉 상품
시험 호출을 규칙 바로 아래에 두면 규칙의 경계가 그대로 문서가 됩니다. 14일째는 승인인가 하는 질문에 코드가 바로 답합니다.
예제 2. 할인율
여러 갈래로 나뉘는 판단에는 cond가 어울립니다. 회원 등급과 장바구니 금액으로 할인율을 정한다고 합시다. 등급은 숫자로 나타내서 2는 골드, 1은 실버, 0은 일반입니다.
(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 골드 회원, 10만 원 이상
(discount-rate 2 50000) ; ⇒ 10
(discount-rate 0 120000) ; ⇒ 5
(discount-rate 0 30000) ; ⇒ 0
cond는 위에서부터 차례로 조건을 검사하고 처음 참이 되는 가지의 값을 답으로 삼습니다. 가지의 순서가 곧 우선순위이므로, 가장 구체적인 조건을 위에 둡니다.
규칙 시험하기
두 가지 방법이 있고, 어느 쪽이든 같은 레퍼런스 인터프리터가 평가합니다.
플레이그라운드에 규칙과 시험 호출을 붙여 넣으면 결과가 바로 나옵니다. 설치가 필요 없어서 규칙의 모양을 잡는 단계에 알맞습니다.
파일로 관리하려면 rules.lspx 같은 파일에 저장하고 CLI로 실행합니다.
npm install -g lispex
lispex run rules.lspx시험 호출마다 기대값을 주석으로 함께 적어 두면, 나중에 규칙을 고칠 때 무엇이 달라졌는지 눈으로 따라갈 수 있습니다.
검사 가능한 부분집합 안에 머무르기
위 예제가 쓴 것은 define, and, cond, 비교 연산자뿐입니다. 이 정도면 이미 부분집합 안입니다. 규칙이 자라도 두 가지만 지키면 됩니다.
- 매크로와 리더 확장은 쓰지 않습니다. 검사 가능한 부분집합에서는 즉시 오류입니다. 허용되는 폼의 전체 목록은 구문에 있습니다.
- 입력은 호스트가 넘겨준 인자만 씁니다. 규칙이 시각, 난수, 외부 데이터를 스스로 가져오면 같은 입력에 같은 답이라는 약속이 깨집니다.
다음은 영수증입니다
이 가이드에서 작성한 규칙은 그대로 리스펙스 바우치의 재료입니다. 규칙이 실제 결정에 사용된다면 네이티브 바이너리로 영수증을 생성해 결정 기록과 함께 보관하세요. 영수증은 어떤 규칙이 어떤 데이터에 적용되어 어떤 결과가 나왔는지를 보여 줍니다. 다만 누가 그 결과에 따라 행동했는지, 언제 적용했는지, 배포된 규칙이 검토한 규칙과 같은지는 영수증만으로 확인할 수 없습니다. 이런 부분에는 리스펙스 바깥의 통제 수단이 필요합니다. lispex verify와 lispex replay는 리스펙스 바우치 문서에서 설명합니다.