datascript

Derek Vance 2023-07-20T17:55:07.622419Z

Hi there, Is there a way to call Clojure functions inside queries in ClojureScript? When I run the following query using a Clojure REPL (notice the use of clojure.set/subset? in a query clause), it returns correctly:

(d/q
   '[:find [?tuple ...]
     :in $ ?odd-numbers
     :where
     [_ :tuple ?tuple]
     [(set ?tuple) ?tuple-set]
     [(clojure.set/subset? ?tuple-set ?odd-numbers)]]
   [[1 :tuple [1 2 3]]
    [2 :tuple [1 3 5]]
    [3 :tuple [2 4 6]]
    [4 :tuple [5 7 9]]
    [5 :tuple [7 8 9]]]
   #{1 3 5 7 9})
;; => [[1 3 5] [5 7 9]]
But from a ClojureScript file using a Calva REPL started with shadow-cljs, I get an error "unknown predicate 'clojure.set/subset? ...":
(d/q
   '[:find [?tuple ...]
     :in $ ?odd-numbers
     :where
     [_ :tuple ?tuple]
     [(set ?tuple) ?tuple-set]
     [(clojure.set/subset? ?tuple-set ?odd-numbers)]]
   [[1 :tuple [1 2 3]]
    [2 :tuple [1 3 5]]
    [3 :tuple [2 4 6]]
    [4 :tuple [5 7 9]]
    [5 :tuple [7 8 9]]]
   #{1 3 5 7 9})
  ;; => :repl/exception!
  ; Execution error (ExceptionInfo) at (<cljs repl>:1).
  ; Unknown predicate 'clojure.set/subset? in [(clojure.set/subset? ?tuple-set ?odd-numbers)]
I appreciate any help in understanding. Thanks so much!

Niki 2023-07-21T09:03:35.959689Z

There’s no way to resolve functions at runtime in ClojureScript. So you have to pass your fn as source, like this:

(d/q '[:find ?e
       :in $ ?adult
       :where 
       [?e :age ?a]
       [(?adult ?a)]]
  db
  #(> % 18))

Niki 2023-07-21T09:04:55.105009Z

We hard-coded some of the most common clojure.core fns but not all of them. These can be called directly: https://github.com/tonsky/datascript/blob/e484f4ebc20caa1530aa6eaa7feb9f75b3dc0ebd/src/datascript/built_ins.cljc#L80-L99

Derek Vance 2023-07-21T21:13:15.811609Z

Thank you so much!