> Thanks, Greg. I can see now. Sorry that it took several posts in order to 
> explain it to me.
>
> Very interesting and clever idea. So there is a kind of distribution of 
> complement information across different branches of conde under forall. I 
> didn't think about it under this angle.
>

Don't thank me yet.  I need to issue some corrections.  I found a gaping 
hole in the approach I described.

Your example is a simpler version of an example from my first message, 
> which for reference was:
>
> (fresh (v w y z) (forall (x) (conde ((== 5 x) (== x w) (symbolo y)) ((=/= 
> 5 x) (== x v) (numbero z)))))
> ==>*
> (fresh (v w y z) (=/= 5 v) (== 5 w) (symbolo y) (numbero z))
>

I was sloppy here.  This example should actually fail because the (== x v) 
isn't satisfiable when x must be everything other than 5.  This is a better 
example:

(fresh (v w y z)
  (forall (x)
    (conde ((== 5 x) (== x w) (symbolo y))
                ((=/= 5 x) (=/= x v) (numbero z)))))  ;; (== x v) changed 
to (=/= x v)
==>*
(fresh (v w y z) (== 5 v) (== 5 w) (symbolo y) (numbero z))

This also points out a shortcoming of my description of the bounded forall 
operation.
 

> I'll step through yours since it's less noisy.  The way it works is, as 
> answers bubble up, the quantified variable is transferred to a new fresh 
> variable, and any constraints it had, are complemented and fed back into 
> the original forall as a more constrained bound.  Once the bound becomes 
> Bottom (or F for fail), the process can stop with success.
>

This operational explanation needs to be fixed.  Transferring to a fresh 
variable is only sound when the quantified variable is unified with a 
single value (without nested quantified variables).

The problem exposed in the above example is that in (forall-bounded (x) 
((=/= 5 x)) ...), the (=/= 5 x) behaves as a *conjunction* of all 
unifications with something other than 5 (in the normal situation with a 
fresh x, =/= instead behaves like a disjunction of these unifications).


Here is a trace of the working example, which undoes forall via double 
negation, then uses term rewritings that only involve not and fresh, making 
it simpler to understand:

(fresh (v w y z)
  (forall (x)
    (conde ((== 5 x) (== x w) (symbolo y))
                ((=/= 5 x) (=/= x v) (numbero z)))))
==>
(fresh (v w y z)
  (not (not (forall (x)
    (conde ((== 5 x) (== x w) (symbolo y))
                ((=/= 5 x) (=/= x v) (numbero z)))))))
==>
(fresh (v w y z)
  (not (fresh (x)
            (not (conde ((== 5 x) (== x w) (symbolo y))
                               ((=/= 5 x) (=/= x v) (numbero z)))))))
==>
(fresh (v w y z)
  (not (fresh (x)
            (conde ((=/= 5 x) (=/= x w) (not-symbolo y))))
            (conde ((== 5 x) (== x v) (not-numbero z)))))))
==>  ;; We enumerate all pairs of these conde clauses.
(fresh (v w y z)
  (not (fresh (x)  ;; We push fresh downward into the conde clauses.
           ;; As answers bubble up, we stream a conjunction of their 
negations.
           ;; This is because we're working with (not (conde ...)).
           (conde
             ((=/= 5 x) (== 5 x))
             ;; ==> F
             ;; (not F)
             ;; ==> S
             ((=/= 5 x) (== x v))
             ;; ==> (fresh (x) (=/= 5 x) (=/= 5 v))
             ;; v doesn't depend on x, so shrink the scope of fresh.
             ;; ==> (and (fresh (x) (=/= 5 x)) (=/= 5 v))
             ;; (not (and (fresh (x) (=/= 5 x)) (=/= 5 v)))
             ;; ==> (conde ((not (fresh (x) (=/= 5 x)))
                                   ((not (=/= 5 v)))
             ;; ==> (conde (F) ((== 5 v)))
             ;; ==> (== 5 v)
             ((=/= 5 x) (not-numbero z))
             ;; The pattern is the same as above so let's make this shorter:
             ;; ==>* (and (fresh (x) (=/= 5 x)) (not-numbero z))
             ;; (not (and (fresh (x) (=/= 5 x)) (not-numbero z)))
             ;; ==>* (numbero z)
             ((=/= x w) (== 5 x))
             ;; ==>* (and (fresh (x) (== 5 x)) (=/= 5 w))
             ;; (not (and (fresh (x) (== 5 x)) (=/= 5 w)))
             ;; ==>* (== 5 w)
             ((=/= x w) (== x v))
             ;; ==> (fresh (x) (=/= x w) (== x v))
             ;; ==> (fresh (x) (=/= v w) (== x v))
             ;; ==> (and (=/= v w) (fresh (x) (== x v)))
             ;; (not ...)
             ;; ==>* (== v w)
             ((=/= x w) (not-numbero z))
             ;; Shorter still, now that we've seen enough patterns:
             ;; (not ...)
             ;; ==>* (numbero z)
             ((not-symbolo y) (== 5 x))
             ;; (not ...)
             ;; ==>* (symbolo y)
             ((not-symbolo y) (== x v))
             ;; (not ...)
             ;; ==>* (symbolo y)
             ((not-symbolo y) (not-numbero z))
             ;; (not ...)
             ;; ==>* (conde ((symbolo y)) ((numbero z)))
))))
==>*
(fresh (v w y z)
  S
  (== 5 v)
  (numbero z)
  (== 5 w)
  (== v w)
  (numbero z)
  (symbolo y)
  (symbolo y)
  (conde ((symbolo y)) ((numbero z)))
==>
;; This time, we get the right result.
(fresh (v w y z)
  (== 5 v)
  (numbero z)
  (== 5 w)
  (symbolo y))


Since soundness is clearer in this case, maybe we can operationalize this 
rewriting process instead.  It seems simpler than figuring out proper 
treatment for forall-bounded.

-- 
You received this message because you are subscribed to the Google Groups 
"minikanren" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/minikanren.
For more options, visit https://groups.google.com/d/optout.

Reply via email to