3

Having a little trouble understanding the core terms of the MicroKanren DSL. Section 4 says:

Terms of the language are defined by the unify operator. Here, terms of the language consist of variables, objects deemed identical under eqv?, and pairs of the foregoing.

But they never describe what the "pairs" actually mean. Are the pairs supposed to represent equality of two subterms, like so:

type 'a ukanren = KVar of int | KVal of 'a | KEq of 'a kanren * 'a kanren

So a term like:

(call/fresh (λ (a) (≡ a 7)))

Generates a pair for (≡ a 7)?

Edit: upon further thought, I don't think this is it. The mentions of "pair" in the paper seem to come much later, with extensions and refinements to the basic system, which would mean the pairs have no meaning in the terms for the basic intro. Is this correct?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
naasking
  • 2,514
  • 1
  • 27
  • 32

2 Answers2

3

In this context, "pair" just means a cons pair, such as (5 . 6) or (foo . #t). An example unification between two pairs is:

(call/fresh
  (λ (a)
    (call/fresh
      (λ (b)
        (≡ (cons a b) (cons 5 6))))))

which associates a with 5 and b with 6.

William E. Byrd
  • 3,117
  • 1
  • 10
  • 6
  • 1
    Right, but the *meaning* of unifying cons here is more subtle. All kinds of object types are represented by such pairs in Scheme, so I think this code really *means*, "unify all components with each other element-wise and recursively". – naasking Mar 06 '15 at 13:02
  • Correct. Let's consider the unification of `(cons x y)` with `(cons 3 4)` with respect to some substitution `s1`. Assuming unification proceeds left-to-right, first `x` will be unified with 3 in substitution `s1`, returning a new substitution `s2`. If this unification fails, `s2` will be `#f`, which will cause the entire unification to fail. Otherwise, `y` and 4 will then be unified in substitution `s2`, producing a new substitution `s3` that will be the result of the pair's unification. – William E. Byrd Mar 07 '15 at 18:28
  • Right, so if I were to implement uKanren in another language, then this step of unification would be different. In uKanren.NET I traverse anything that implements IEnumerable streams and unify each stream element-wise. I believe this is the desired semantics. Ideally, this would also apply to any records or other composite objects that may contain logic variables as fields, but this quickly gets complicated for statically typed languages. – naasking Mar 08 '15 at 17:39
  • Exactly. Clojure's core.logic has an extensible unifier that I believe does just this, using Clojure's protocols. And last night Michael Ballantyne and I added support for Racket records to miniKanren, which meant being able to recursively perform element-wise unification on the record fields. – William E. Byrd Mar 08 '15 at 22:48
1
  1. Sorry for the confusion and difficulty!! And thank you for the question!

You can think of the (typical) Kanren term language as having a single binary functor tag cons/2, and an infinite quantity of constants (the exact makeup changes from embedding to embedding).

Under the assumption that cons/2 is the only (n < 0)-ary functor tag, every compound term will have been built with it. If you look up standard presentations of unification (e.g. Martelli-Montanari) you'll usually see a step f(t0,...,tn) g(s0,...,sm) => fail if f =/= g or n =/= m. We handle clash-failure in unification when comparing ground atomic terms; for us, compound terms must necessarily be of the same arity and tag.

pair? recognizes conses. In Racket, in fact, they provide a cons? alias, to make this clearer!

Once again, thank you!

Jason Hemann
  • 327
  • 1
  • 12