clauses is a hash where each mapping represents a clause that may be randomly chosen. The key is the try thunk, a nullary procedure that’s applied when the clause is chosen; and the value is the weight thunk, also a nullary procedure, one that’s applied to determine the clause’s likelihood of being chosen.
(evaluate-rule a-rule-struct [ #:default default-value]) → any/c a-rule-struct : rule-struct? default-value : any/c = an opaque value
The rule’s clauses’ weight thunks are all evaluated, then try thunks are evaluated at random, weighted by their corresponding computed weight, until one of them succeeds, i.e., doesn’t backtrack. (See backtrack and exn:backtrack.) If a clause succeeds, evaluate-rule returns the result of its try thunk. If no clause succeeds, then the entire rule backtracks.
The probability of a clause being chosen is its computed weight divided by the total of all clauses’ computed weights.
If a #:default argument is provided, it will be the return value in case the rule would have otherwise backtracked. This is useful for keeping the backtrack signal from bubbling to the top when a rule is being evaluated outside of any other rule. (See exn:backtrack for how to deal with it in general.)
If message is provided, it will be used to provide a more descriptive error message for debugging purposes.
(define-rule start) (with-handlers ((exn:backtrack? (const "It backtracked!"))) (start))
This is useful for tagging rules with additional state that needs to remain consistent with respect to clauses being tried but failing.
(rule clause ...)
clause = proc-expr maybe-weight maybe-weight =
| #:weight weight-expr
For each clause, proc-expr is evaluated immediately to obtain the clause’s try thunk, while weight-expr (by default, 1) becomes the body of the weight thunk and thus is evaluated only when the rule is.
(define-rule id rest ...)
(simple-rule expr ...)
This is useful when each of your clauses only needs to be a single expression without complex logic, allowing you to omit ~> or other procedure-producing forms.
(define-simple-rule id rest ...)
(~> expr ... maybe-combiner)
| #:combiner combiner-expr
The void?-filtering behavior is particularly useful alongside jen/preconditions, which contains procedures and forms that affect clause evaluation and return (void). If this turns out to be undesirable for a given use case, it’s still possible to use thunk with define-rule.
The upshot of this is that the clause containing the call to this macro can be allowed to be committed to at most n times before it always fails.
Underlyingly, each call site of this macro uses its own rule parameter (see make-rule-parameter) as a counter of how many times it’s been reached (and its clause committed to).
(cycle expr ...+)