core-logic

Benjamin 2025-01-30T19:13:17.453589Z

Hi, in the reasoned schemer, they define (Chapter 6 page 105) (defrel (nevero) (nevero)) and then are able to use it as goal. 1. what actually does 'defrel' do, is there an equivalent? 2. I naively said (defn nevero [s] (nevero s)) , but it makes a stack overflow in the case

(run 1 [q]
  (conde
   [nevero]
   [l/succeed])) 

Benjamin 2025-02-04T08:17:39.448939Z

cool thanks @peterhfmnn . I am also using the pattern matching. I find it very condensend and even easier to read once one is used to it!

2025-01-30T19:18:48.447199Z

defrel is defined in the 'Connecting the wires' appendix. It's been awhile since I've looked at core.logic itself, so I'm not sure if there's an exact equivalent there.

2025-01-30T19:19:01.633589Z

But basically you're looking for some way to make this lazy, to break the recursion.

2025-01-30T19:25:46.796869Z

Glancing over the core.logic source though, It seems possible that the definition of defrel could be pretty close. Something like (fn [s] (fn [] (nevero)))

Benjamin 2025-01-30T19:26:11.150519Z

ah,

(defn nevero [s]
  (conde [(nevero s)]))
this works. ah another fn πŸ˜„

2025-01-30T19:26:33.809169Z

That makes sense, I believe conde synthesizes the same structures for you.

Benjamin 2025-01-30T19:27:07.970759Z

ah

Benjamin 2025-01-30T19:27:42.313739Z

ah it makes a stack graph of fn or something

2025-01-30T19:29:30.957479Z

You can macroexpand your own usage to see what it turns into

peterh 2025-01-31T23:03:21.034249Z

When I was working through the book, I first defined a macro similar to the footnote on page 19:

(defmacro defrel [name params & goals]
  `(defn ~name ~params
     (fn [s#]
       (fn []
         ((l/and* ~(vec goals)) s#)))))
The l/and* part is just for convenience. I guess the final returned fn is what makes it lazy to prevent infinite recursion. The all macro in core.logic seems to do the same thing, but with bind, which I don’t quite understand since I am not familiar with protocols in Clojure. So I guess we can also write it like this:
(defmacro defrel [name params & goals]
  `(defn ~name ~params
     (fn [s#]
       (l/bind* s# ~@goals))))

peterh 2025-01-31T23:28:56.557979Z

Besides that, I sometimes prefer core.logics defne macro that enables more concise definitions using pattern matching (see https://github.com/clojure/core.logic/wiki/A-Core.logic-Primer#pattern-matching). It was fun to rewrite definitions from the book using defne instead:

(defrel teacupo [t]
  (or* [(l/== 'tea t) (l/== 'cup t)]))

(l/defne teacupo [t]
  (['tea])
  (['cup]))

(defne cdro [p d] ([[_ . d] _]))
(defne caro [p a] ([[a . _] _]))
(defne conso [a d p] ([_ _ [a . d]]))

(defne nevero []
  ([] (nevero)))

(defne alwayso []
  ([] s#)
  ([] (alwayso)))