I've never used Racket's macro system before so forgive my ignorance. I'm trying to dynamically define rules in Racket's Datalog system, basically like this:
(datalog rules (! (:- c a b)))
If I put something like this into the code directly, all is well. However, I want to dynamically generate these rules. With regular functions I would just do something like this:
(datalog rules (! (apply :- conclusion premises)))
But datalog
, !
and :-
are all macros, making this impossible.
My solution was to write this macro:
(define-syntax (make-rule stx)
(syntax-case stx ()
((_ premises conclusion)
#`(datalog rules (! (:- ,conclusion ,@(map (lambda (x) `,x) premises)))))))
Which is then called like this:
(make-rule '(a b) 'c)
However while this does run, querying the Datalog database renders nothing when both a
and b
are true.
I tried double quoting the syntax object so I could see just what was getting output, and it's this:
'(datalog rules (! (:- c a b)))
So... Exactly what should and does work if I just type it directly in my code! What is going on here? Is this some sort of hygenic macro thing? I really have no idea.
Thanks!
Edit: Here is a complete runnable example of the problem.
#lang racket
(require datalog)
(define rules (make-theory))
(define-syntax (make-rule stx)
(syntax-case stx ()
((_ premises conclusion)
#`(datalog rules (! (:- ,conclusion ,@(map (lambda (x) `,x) premises)))))))
(make-rule '(a b) 'c)
(datalog rules (? c)) ;;Does not work.
(datalog rules (! (:- c a b)))
(datalog rules (! a) (! b))
(datalog rules (? c)) ;;Works.
syntax-case
orsyntax-parse
forms to break hygiene. – law-of-fiveswith-syntax
to introduce bindings that aren't secretly relabeled by the macro system. – law-of-fives