Search code examples
clojureclojure-core.logic

pseudo-relation in core.logic


In core.logic of function, I see the below definition for clojure.core.logic/everyg.

A pseudo-relation that takes a coll and ensures that the goal g succeeds on every element of the collection.

What exactly a pseudo-relation means in this context?


Solution

  • Here's a good explanation of relational (and non-relational): What does non-relational mean in practice for core.logic? Another bit of background is everyg was renamed from everyo in this commit because the o suffix should only used for relations.

    One property of relational goals is that they can contribute answers when all/some/none of their inputs are non-ground. conso is relational and so we can ask what are the values that satisfy the relation where l is a prepended to d, where all variables are fresh:

    (run* [a d l] (conso a d l))
    => ([_0 _1 (_0 . _1)])
    

    This tells us a (_0) is fresh, d (_1) is fresh, and l ((_0 . _1)) is a prepended to d. While none of these values are ground, we can still see the relationship between them in the answer.

    The same is not true for everyg:

    (run* [g coll] (everyg g coll)) ;; Don't know how to create ISeq from: clojure.core.logic.LVar
    

    everyg can't tell us what the relationship between g and coll is, nor can it tell us what g or coll might be if we supply either ground value:

    (run* [coll] (everyg succeed coll)) ;; throws
    (let [coll (repeatedly 3 lvar)]     ;; throws too
      (run* [g]
        (everyg g coll)))
    

    While everyg itself isn't a relation, you can pass it a goal g and coll of fresh and/or ground variables:

    (let [coll (cons 1 (repeatedly 2 lvar))] ;; 1 prepended to two fresh logic vars
      (run* [q]
        (== q coll)
        (everyg #(fd/in % (fd/domain 0 1)) coll)))
    => ((1 0 0) (1 1 0) (1 0 1) (1 1 1))
    

    This example will give the same answers regardless of the position of the everyg goal, which is another property of relational goals.

    And we can pass the conso relation to everyg with all fresh logic variables and still see the relationship in the answer, exactly as it was above with the conso example:

    (run* [a d q]
      (everyg #(conso a d %) [q]))
    => ([_0 _1 (_0 . _1)])
    

    So with some caveats, everyg can be used relationally which is why I think it's considered pseudo-relational rather than non-relational.