datascript

yuhan 2025-09-04T10:19:20.121909Z

hey all, I'm following along the Learn Datalog Today tutorials, and ran into some really unexpected behavior on chapter 6 where they use transformation functions in a join query. Here's a shrunken down version of the scenario:

(d/q '[:find ?n1 ?n2 
       :where
       [?p1 :name ?n1]
       [?p2 :name ?n2]
       [?p1 :born ?b1]
       [?p2 :born ?b2]
       [(clojure.core/first ?b1) ?day]
       [(clojure.core/first ?b2) ?day]
       [(clojure.core/second ?b1) ?month]
       [(clojure.core/second ?b2) ?month]
       [(< ?p1 ?p2)]]
  (d/db-with (d/empty-db)
    [{:name "A" :born [1 1 1980]}
     {:name "B" :born [1 1 1981]}
     {:name "C" :born [1 2 1982]}
     {:name "D" :born [2 2 1983]}
     {:name "E" :born [5 6 1984]}]))
;; => #{["A" "C"] ["B" "C"] ["A" "B"]}
I'd expect the only match to be ["A" "B"] , can anyone help explain why AC and BC are also being returned? Note that it does not match the CD pair (which share a month) so the semantics aren't even consistent with the clauses being combined disjunctively

yuhan 2025-09-04T10:22:52.851619Z

Here's a link to the original page (exercise 3, birthday paradox) https://learn-some.com/chapter/6 - the reference solution is logically the same (but using interop syntax on the insts which Datascript doesn't seem to support)

yuhan 2025-09-04T10:26:43.258799Z

Even stranger, if I reorder the clauses so that ?month comes first, it produces a different 'wrong' result:

(d/q '[:find ?n1 ?n2
       :where
       [?p1 :name ?n1]
       [?p2 :name ?n2]
       [?p1 :born ?b1]
       [?p2 :born ?b2]
       [(clojure.core/second ?b1) ?month]
       [(clojure.core/second ?b2) ?month]
       [(clojure.core/first ?b1) ?day]
       [(clojure.core/first ?b2) ?day]
       [(< ?p1 ?p2)]]
  db)
;; => #{["C" "D"] ["A" "B"]}

Niki 2025-09-04T11:57:12.650559Z

This is in Datascript, right? Might be a bug. What’s Datomic’s output?

yuhan 2025-09-04T12:00:27.927259Z

I don't have a copy of Datomic to test with, but it should output [A B] according to the tutorial

Niki 2025-09-04T12:01:24.387559Z

Maybe we don’t do unification on function call assignment, I don’t remember. Open an issue

👍 1
yuhan 2025-09-04T12:03:43.903939Z

oh funny enough it works if you put it in a rule

(d/q '[:find ?n1, ?n2
       :in $ %
       :where
       [?p1 :name ?n1]
       [?p2 :name ?n2]
       (birthday ?p1 ?day ?month)
       (birthday ?p2 ?day ?month)
       [(< ?p1 ?p2)]]
  db
  '[[(birthday ?p ?d ?m)
     [?p :born ?b]
     [(clojure.core/first ?b) ?d]
     [(clojure.core/second ?b) ?m]]])
;; => #{["A" "B"]}

Niki 2025-09-04T12:13:06.578469Z

Yeah, out parameters on rules are implemented differently