2 The jen Reference
2.1 Semantics
clauses is an association list where each pair represents a clause that may be randomly selected. The key is the try thunk, a nullary procedure that’s applied when the clause is selected; and the value is the weight thunk, also a nullary procedure, which is applied to determine the clause’s likelihood of being selected (see weight?).
Applying a rule-struct value is the same as applying rule-evaluate to it.
procedure
(rule-evaluate 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 selected according to (current-clause-selector) and evaluated until one of them succeeds, i.e., doesn’t backtrack. (See backtrack and exn:backtrack.) If a clause succeeds, rule-evaluate 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.
struct
(struct exn:backtrack exn ())
Although usually unnecessary (see rule-evaluate), it’s possible to catch a backtrack manually by installing an exn:backtrack? handler. For example:
(define-rule start) (with-handlers ((exn:backtrack? (const "It backtracked!"))) (start))
"It backtracked!"
procedure
(make-rule-parameter [initial-value]) → parameter?
initial-value : any/c = #f
This is useful for tagging rules with additional state that needs to remain consistent with respect to clauses being tried but failing.
parameter
(current-clause-selector clause-selector) → void? clause-selector : clause-selector/c
Note that the total weight and the individual weights of the clauses passed as arguments may have been multiplied by a shared factor to guaranteed that they’re natural numbers. The relationship that a clause’s likelihood of being selected equals its weight divided by the total weight is preserved.
Clause selectors may feel free to ignore their second argument; it’s provided for when extra introspection is desired, but a "fair" selector most likely has no use for it.
The default clause selector implements a "fair" random selection; see default-clause-selector for specifics.
Beware that a rule that is well behaved with the default clause selector may not terminate if a different one were to be used. For example:
(define-rule very-small (~> "very " (very-small)) (~> "small"))
This will not terminate if the selector always produces 0.
value
(->i ([total-weight natural?] [_ (listof (cons/c (-> any/c) natural?))]) [_ (total-weight) (and/c natural? (</c total-weight))])
2.2 Syntax
(require jen/syntax) | package: jen-lib |
syntax
(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.
syntax
(define-rule id rest ...)
syntax
(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.
syntax
(define-simple-rule id rest ...)
syntax
(~> expr ... maybe-combiner)
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.
2.3 Preconditions
(require jen/preconditions) | package: jen-lib |
syntax
(once)
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).
2.4 Sequences
(require jen/sequential) | package: jen-lib |
syntax
(cycle expr ...+)
syntax
(cycle/repeat-last expr ...+)