Assignment 12: Continuations and logic programming

In lecture you learned how a logic programming language can be implemented using succeess and failure continuations. Now you will put your knowledge to work by implementing a new language construct for microkanren, a simplified version of miniKanren. microkanren has only three operators: run, conde, and ==. To introduce new logical variables, you must let-bind variables craeated by the var function (see the examples in ednoc-tests.scm).

Your task is define a new operator, ednoc, which is the dual of the conde operator we all know and love. Syntactically, ednoc is identical to conde:

(run* (q)
  (ednoc
    [(== q 5) (== q 6) (== q 7)]
    [(== q 5) (== q 6) (== #f #f)]
    [(== q 5) (== q 6) (== #f #f)]))
However, the meaning of ednoc differs from that of conde. For conde, each clause is idependent, and in order for a clause to succeed, all of the goals within that clause must succeed. For ednoc, the goals within a single clause are independent, and for ednoc to succeed, one goal within each ednoc clause must succeed. For example,
(run* (q)
  (ednoc
    [(== q 5) (== q 6)]
    [(== q 6) (== q 5)]))
returns the list (5 6). However,
(run* (q)
  (ednoc
    [(== q 5)]
    [(== q 6)]))
returns (), since q cannot be both 5 and 6 at the same time. The file ednoc-tests.scm contains more examples of ednoc.

Your definition of ednoc must handle one or more clauses, and one or more goals per clause.

You should closely examine the definition of conde in microkanren.scm. Your definition of ednoc will be similar in many ways. However, you may not use conde in your definition of ednoc.

Your code must pass the tests in ednoc-tests.scm.

Submit to Vincent a file named ednoc.scm, containing only your definition of ednoc.