core-logic

Ben Sless 2023-07-25T08:16:06.818209Z

What exactly is the purpose of the Choice type? I'm trying to map it to the microKanren paper or minikanren implementation, what am I missing?

Ben Sless 2023-07-27T08:53:53.547049Z

uk does interleave, see how mplus flips streams 1 and 2

Ben Sless 2023-07-27T15:45:26.453889Z

I just got it (I think) Choice is a TAKEN choice, not a possibility to choose. It's the type for head and rest of stream

paulocuneo 2023-07-27T22:54:40.738909Z

yes, agree is a (lazy) stream of substitutions, although the API return substitutions by lazy implementation its holding on to goals that are yet to be executed

paulocuneo 2023-07-26T00:20:07.088649Z

not a proper answer but my guess is because clojure cons is not a pair, can't hold a thunk as tail. also lazyseq uses synchronized methods to unfold, so maybe choice is more light weight. https://github.com/jasonhemann/microKanren/blob/master/microKanren.scm#L45 https://github.com/michaelballantyne/faster-minikanren/blob/master/mk.scm#L287C27-L287C27 https://github.com/clojure/core.logic/blob/master/src/main/clojure/clojure/core/logic.clj#L1092 🙂 I'm fascinated that you could also implement minikanren just with lazy-seq

;; naive implementation, todo remove nils
(defn mplus
  ([g1 g2]
   (fn [s]
     (interleave-all (g1 s) (g2 s)))))

(defn mbind [g1 & g2]
  (fn [s]
    (mapcat g2 (g1 s))))

Ben Sless 2023-07-26T06:47:04.503019Z

AFAIK the lack of improper lists is the reason for LCons. So what does Choice do?

paulocuneo 2023-07-26T22:58:53.625189Z

Yes but LCons is on the unification side of things. LCons is for unifying list-ish things. Choice builds a structure for holding the disjunctions of goals(predicates/relations) during the search

(ns 
  (:require [clojure.core.logic :refer :all]
            [clojure.core.logic.protocols :refer [mplus bind]]))

(run* [q]
  (conde
   [(== q 1)]
   [(== q 2)]
   [(== q 3)]))

(run* [q]
  (fn [a]
    (mplus* (bind a (== q 1))
            (bind a (== q 2))
            (bind a (== q 3)))))

(run* [q]
  (fn [a]
    (mplus (bind a (== q 1))
           (mplus (bind a (== q 2))
                  (bind a (== q 3))))))

;; leaving the interleaving aside
(run* [q]
  (fn [a]
    (->Choice ((== q 1) a)
              (fn []
                (->Choice  ((== q 2) a)
                           (fn [] ((== q 3) a)))))))
Choice holds a head choice for substitution and a tail of substitution choices.

paulocuneo 2023-07-26T23:04:42.563019Z

from Byrd's dissertation https://scholarworks.iu.edu/dspace/bitstream/handle/2022/8777/Byrd_indiana_0093A_10344.pdf page 51 a choice represents a stream of values. So I think is correct to say that choice is a stream of substitutions. By the way I'm not sure I fully understand the dissertation either, maybe some parts.😂

Ben Sless 2023-07-27T04:27:14.951269Z

What's messing with me is although you're right I don't think the rhs of Choice should be a goal

Ben Sless 2023-07-27T04:27:20.820939Z

(extend-type Object
  IMPlus
  (mplus [this f]
    (Choice. this f)))

Ben Sless 2023-07-27T04:27:55.635929Z

(defn to-stream [aseq]
  (let [aseq (drop-while nil? aseq)]
    (when (seq aseq)
      (choice (first aseq)
              (fn [] (to-stream (next aseq)))))))

Ben Sless 2023-07-27T04:28:55.485249Z

It behaves like a lazier lazy seq

Ben Sless 2023-07-27T04:30:02.768379Z

Maybe @dnolen remembers why he wrote it over a decade ago

Ben Sless 2023-07-27T05:06:28.262909Z

okay I should have referred to the mk dissertation and not uk Looks like choice is just a stream constructor

Ben Sless 2023-07-27T05:54:12.885909Z

In mk it's just cons, in core.logic the important bit is implementing take*

2023-07-27T06:56:04.270079Z

I forget, but uk might not do interleaved search, meaning it does depth first search, where mk and core.logic does bfs

Ben Sless 2023-07-31T07:07:47.117319Z

Choice is what's returned by msplit

👍 1