# Conj in a rule fails on the transactor

**URL:** <https://forum.datomic.com/t/conj-in-a-rule-fails-on-the-transactor/413>\
**Category:** Datomic Applications\
**Created:** [April 24, 2018, 1:24am UTC](https://forum.datomic.com/t/conj-in-a-rule-fails-on-the-transactor/413 "2018-04-24T01:24:12Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![benfle](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.datomic.com/benfle/32/164_2.png) [@benfle](https://forum.datomic.com/u/benfle)\
**Post date:** [April 24, 2018, 1:24am UTC](https://forum.datomic.com/t/conj-in-a-rule-fails-on-the-transactor/413/1 "2018-04-24T01:24:12Z")

</div>

Hi everyone,

I have a hard time understanding why the following rule sometimes fail with a ClassCastException on the `conj`. Apparently `?parent-path` is a Java array and not a IPersistentCollection.

This only happens in the transactor and not with `d/with`.

We would like to refer to nodes in a tree using their path from the root. I was hoping that a recursive rule like this would work:

```
(def node-path
  '[;; node without a parent
    [(node-path ?n ?p)
     [?n :node/name ?name]
     [(missing? $ ?n :node/parent)]
     [(vector ?name) ?p]]
    ;; node with a parent
    [(node-path ?n ?p)
     [?n :node/name ?name]
     [?n :node/parent ?parent]
     (node-path ?parent ?parent-path)
     [(conj ?parent-path ?name) ?p]]])

```

I can send a minimal schema and query to reproduce my use-case if needed.

My expectation was that a vector would always be built at the bottom of the recursion. `?p` would be bound to the result of `(vector ?name)`. And then we `conj` this vector until we reach the leaf. Was my expectation correct? If not, where is it wrong?

Thanks a lot for your help.

---

<div class="post-metadata">

**Author:** ![benfle](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.datomic.com/benfle/32/164_2.png) [@benfle](https://forum.datomic.com/u/benfle)\
**Post date:** [April 25, 2018, 12:58pm UTC](https://forum.datomic.com/t/conj-in-a-rule-fails-on-the-transactor/413/2 "2018-04-25T12:58:36Z")

</div>

To repro on the lastest Datomic free transactor.

```
(ns sandbox
  (:require [datomic.api :as d]))

(def db-uri "datomic:free://localhost:4334/sandbox")

(def schema
  [#:db{:ident :node/name
        :valueType :db.type/string
        :cardinality :db.cardinality/one}
   #:db{:ident :node/parent
        :valueType :db.type/ref
        :cardinality :db.cardinality/one}
   #:db{:ident :node/path->eid
        :fn #db/fn
        {:lang "clojure"
         :params [db path]
         :requires [[datomic.api :as d]]
         :code (ffirst
                (d/q '[:find ?e
                       :in $ % ?path
                       :where (node-path ?e ?path)]
                     db
                     '[[(node-path ?n ?p)
                        [?n :node/name ?name]
                        [(missing? $ ?n :node/parent)]
                        [(vector ?name) ?p]]
                       [(node-path ?n ?p)
                        [?n :node/name ?name]
                        [?n :node/parent ?parent]
                        (node-path ?parent ?parent-path)
                        [(conj ?parent-path ?name) ?p]]]
                     path))}}
   #:db{:ident :node/ensure-path
        :fn #db/fn
        {:lang "clojure"
         :params [db path parent?]
         :requires [[datomic.api :as d]]
         :code (if-let [eid (d/invoke db :node/path->eid db path)]
                 (when parent?
                   eid)
                 (let [name (last path)
                       parent-path (butlast path)]
                   [(cond-> {:db/id (d/tempid :db.part/user)
                             :node/name name}
                      (seq parent-path)
                      (assoc :node/parent
                             (d/invoke db :node/ensure-path
                                       db parent-path true)))]))}}])

(defn connect
  []
  (d/create-database db-uri)
  (let [conn (d/connect db-uri)]
    @(d/transact conn schema)
    conn))

(def test-data
  [["1"
    ["1A"]
    ["1B"
     ["1B1"]
     ["1B2"]]]
   ["2"]])

(defn plex-tx
  ([data]
   (plex-tx data nil))
  ([data parent]
   (mapcat (fn [[name & children]]
             (let [tempid (d/tempid :db.part/user)
                   node (cond-> {:db/id tempid
                                 :node/name name}
                          parent (assoc :node/parent parent))]
               (into [node] (plex-tx children tempid))))
           data)))

(comment

  (def conn (connect))
  @(d/transact conn (plex-tx test-data))

  (def db (d/db conn))
  (d/invoke db :node/path->eid db ["1" "1B" "1B1"])

  @(d/transact conn [[:node/ensure-path ["1" "1B" "1B3"] false]])
  @(d/transact conn [[:node/ensure-path ["1" "1B"] false]])

  ;; This tx throws
  @(d/transact conn [[:node/ensure-path ["1"] false]])

  )
```

---

<div class="post-metadata">

**Author:** ![dustingetz](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.datomic.com/dustingetz/32/56_2.png) [@dustingetz](https://forum.datomic.com/u/dustingetz)\
**Post date:** [December 21, 2018, 3:59pm UTC](https://forum.datomic.com/t/conj-in-a-rule-fails-on-the-transactor/413/3 "2018-12-21T15:59:41Z")

</div>

Bump!

---

<div class="post-metadata">

**Author:** ![marshall](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.datomic.com/marshall/32/48_2.png) [@marshall](https://forum.datomic.com/u/marshall)\
**Post date:** [January 14, 2019, 4:53pm UTC](https://forum.datomic.com/t/conj-in-a-rule-fails-on-the-transactor/413/4 "2019-01-14T16:53:39Z")

</div>

This is a consequence of a couple factors:

- array lists and vectors compare equal
- you can’t force something to be a vector on the wire between peer and transactor
- if you need a vector, you have to make it a vector after the wire

The encoding between peer and transactor doesn’t preserve Clojure vectors, which is why your example works using `with` (no wire) but doesn’t with a remote transactor (the vector becomes an array list).

When you pass in `?path` in your query, it is bound to `?p` in your rule, where the call to `vector` evaluates true, when the passed in path is an array list or a vector.

You would need to use an intermediate variable that you force into a new vector if you need the bottom of the rule to return a vector. I have provided a very small reproduction below (using an explicit array list):

```auto
(def uri "datomic:mem://testdb")
(d/create-database uri)
(def conn (d/connect uri))

(def a-list (java.util.ArrayList.))
(.add a-list 2)
(.add a-list 3)
a-list
;; [2 3]

(= a-list (vector 2 3))
;; true

(d/q '[:find ?a .
       :in $ ?a-list
       :where
       [(vector ?a-list) ?a]]
     (d/db conn) a-list)

;; [[2 3]]

(type (first *1))

;; java.util.ArrayList

(d/q '[:find ?a .
       :in $ ?a-list
       :where
       [(vector ?b) ?a]
       [(into [] ?a-list) ?b]]
     (d/db conn) a-list)

;; [[2 3]]

(type (first *1))

;; clojure.lang.PersistentVector

```
