Skip to content

Commit 3c64bcc

Browse files
committed
Add reazon--project
1 parent 79e1935 commit 3c64bcc

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

reazon-tests.el

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,20 @@
238238
((reazon-fresh (z) (reazon-== x z)) (reazon-== y z)))
239239
(reazon-== nil z)))))
240240

241+
(ert-deftest reazon--test-project ()
242+
(reazon--should-equal '(25)
243+
(reazon-run* q
244+
(reazon-fresh (x)
245+
(reazon-== x 5)
246+
(reazon-project (x)
247+
(reazon-== q (* x x))))))
248+
(should-error
249+
(reazon-run* q
250+
(reazon-fresh (x)
251+
(reazon-project (x)
252+
(reazon-== q (* x x)))
253+
(reazon-== x 5)))))
254+
241255
(reazon-defrel reazon--test-teacup-o (x)
242256
(reazon-disj (reazon-== x 'tea) (reazon-== x 'cup)))
243257

reazon.el

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,38 @@ function: variable -> goal, e.g. (lambda (fruit) (reazon-== 'plum fruit))"
291291
(reazon-fresh ,(cdr vars)
292292
,@goals))))))
293293

294+
(defmacro reazon-project (vars &rest goals)
295+
"Run GOALS with the values associated with VARS lexically bound.
296+
This is an impure, non-relational operator, and its correct use
297+
depends on the ordering of clauses. For example,
298+
299+
(reazon-run* q
300+
(reazon-fresh (x)
301+
(reazon-== x 5)
302+
(reazon-project (x)
303+
(reazon-== q (* x x)))))
304+
305+
succeeds with a value of '(25), while
306+
307+
(reazon-run* q
308+
(reazon-fresh (x)
309+
(reazon-project (x)
310+
(reazon-== q (* x x)))
311+
(reazon-== x 5)))
312+
313+
raises an error. This is because in the second instance, the variable
314+
`x' is still fresh in the substitution, so the multiplication fails
315+
when applied to it."
316+
(declare (indent 1))
317+
(if (null vars)
318+
`(reazon-conj ,@goals)
319+
(let* ((stream (gensym))
320+
(walked-vars
321+
(mapcar (lambda (var) `(,var (reazon--walk* ,var ,stream))) vars)))
322+
`(lambda (,stream)
323+
(let ,walked-vars
324+
(funcall (reazon-conj ,@goals) ,stream))))))
325+
294326
(defmacro reazon-run (n var/list &rest goals)
295327
"Run GOALS against VAR/LIST for at most N values.
296328
If N is nil, run for as many values as possible. VAR/LIST can be

0 commit comments

Comments
 (0)