datomic

Ask questions on the official Q&A site at https://ask.datomic.com!
cmdrdats 2020-09-16T03:22:10.461400Z

The key here is to understand what you're wanting this for. 9/10, you're actually just wanting to pull the field information out into ?foo-bar For that, I would recommend using the pull syntax, ie.

cmdrdats 2020-09-16T03:22:58.461600Z

(d/q 
  '[:find (pull ?foo-id [:foo/id :foo/bar])
    :where [?foo-id :foo/id "123"]] db)

joshkh 2020-09-16T15:20:04.473400Z

i found myself in the middle of a fun (:man-shrugging:) debate today, the topic being: > can a function still be pure if it calls d/pull? some people say yes, d/pull operates on db as a value. other people say no, d/pull is a side effect that fetches data over the network who's right, and who has to buy the next round at the pub?

joshkh 2020-09-17T07:01:32.481900Z

the example function inc-attr at the top of this thread is actually a transactor function taken from the Datomic documentation. and according to the same documentation, they must be pure. so perhaps inc-attr is pure (in this context) because transactor functions run "inside" of Datomic?

favila 2020-09-17T07:25:36.482100Z

I think “pure” is used loosely here to mean “I may execute this function multiple times while holding a db lock at the head of a queue of other transactions, and you are ok with the consequences of whatever you do in there”

joshkh 2020-09-17T11:32:47.485300Z

agreed!

joshkh 2020-09-17T11:33:48.485500Z

thanks for sharing your thoughts, it's always insightful to pick other peoples' brains.

joshkh 2020-09-16T15:23:15.473600Z

example function taken from the Cloud documentation:

(defn inc-attr
  "Transaction function that increments the value of entity's card-1
attr by amount, treating a missing value as 0."
  [db entity attr amount]
  (let [m (d/pull db {:eid entity :selector [:db/id attr]})]
    [[:db/add (:db/id m) attr (+ (or (attr m) 0) amount)]]))

benoit 2020-09-16T15:28:28.474100Z

I think it is more important to worry about what your function returns rather than what it actually does. Does your "pure" function always return the same result for the same input?

wotbrew 2020-09-16T15:28:44.474300Z

The difference between a memory load of an immutable value and a network load is mostly going to be performance and possibility of exception. Well I can write a 'pure' function that takes a long time to compute and I can write a 'pure' function that might throw e.g out-of-memory.

wotbrew 2020-09-16T15:30:30.474500Z

Excision may well remove the value-ness of your as-of db though.

joshkh 2020-09-16T15:32:53.474700Z

yes, i agree with both of your points. Datomic being immutable, you will always be returned the same output for a given input, except for the case of excision (although we don't worry about that in Cloud).

favila 2020-09-16T15:51:06.475Z

You also can’t just look at the function to decide purity, you have to look at its arguments and return values

favila 2020-09-16T15:51:37.475200Z

d/pull is absolutely pure if db is an in-memory db, for example

favila 2020-09-16T15:52:05.475500Z

but it’s absolutely not pure if it’s an in-memory db that randomly generates results when read

favila 2020-09-16T15:57:07.475700Z

I think more than purity I usually want to know things like: does this function take/return values or objects (lazyness being an important grey area--look at d/entity return values, or lazy-seqs that perform IO); does it return the same thing on repeated calls with the same arguments (“same” depending on whether they are values or objects); could it ever possibly perform io or idle-blocking

benoit 2020-09-16T17:10:31.476400Z

Yes, same is not an obvious concept 🙂 But I think the important part to get across here is that the notion of pure function matters for the user of the function and not its implementation. You can have pure functions that manage state internally (e.g. any memoized function).

favila 2020-09-16T17:30:05.476600Z

“pure” can mean same return for same arguments, or no side effects (I think this excludes memoization and IO), or both. Because it can mean any of these things, I think it’s better to be more specific. We know the properties of d/pull, so I think the original question is really an argument about what “pure” should mean.

favila 2020-09-16T17:32:35.476800Z

which I guess is a fine argument for the pub 🙂

favila 2020-09-16T17:33:01.477Z

(just keep sharp objects away)

1😁
2020-09-16T19:41:29.478100Z

So you can specify that a rule requires bindings by enclosing the argument in brackets, but from experimenting I noticed that that doesn’t take clause ordering into account, e.g. this is fine, even though the rule is invoked in a clause preceding the binding of ?e:

(d/q '{:find [?v]
       :in   [$ %]
       :where [(my-rule? ?e)
               [?e :attr ?v]]}
     db
     '[[(my-rule? [?e])
        [?e :attr 12]]])
Why is that? And how should I go about making sure that consumers of this rule don’t accidentally use it in ways that binds large parts of the database?

favila 2020-09-16T20:06:51.478200Z

That looks like a bug to me

favila 2020-09-16T20:07:24.478400Z

does that only happen if it’s the very first clause?

2020-09-16T20:15:21.478600Z

It also doesn’t complain like this:

(d/q '{:find [?v]
       :in   [$ %]
       :where [[?other-e :attr ?v]
               (my-rule? ?e)
               [?e :attr ?v]]}
      db
      '[[(my-rule? [?e])
         [?e :attr 12]]])

2020-09-16T20:15:40.478800Z

Lets see what it does on a more contemporary version of datomic

2020-09-16T20:20:12.479Z

(same behaviour on 1.0.6202, both queries don’t complain about missing bindings)

favila 2020-09-16T20:40:53.479300Z

yeah, I tried a bunch of things. I can only get this to fail:

(d/q '{:find  [?v]
       :in    [$ %]
       :where [(my-rule ?e ?v)]}
     [[1 :attr 2]
      [2 :attr 12]]
     '[[(my-rule [?e] ?v)
        [?e :attr ?v]
        [(ground 12) ?v]]])
Execution error (Exceptions$IllegalArgumentExceptionInfo) at datomic.error/arg (error.clj:79).
:db.error/insufficient-binding [?e] not bound in clause: (my-rule ?e ?v)

favila 2020-09-16T20:41:40.479500Z

maybe, there is some clause reordering? I wouldn’t know how to predict the performance of this

favila 2020-09-16T20:43:07.479800Z

if it isn’t somehow delaying rule evaluation until after everything’s bound that can be, I would expect your examples to throw

2020-09-16T20:48:29.480Z

Hmm interesting. Yeah, if it is just performant, you will not hear me complain. I just got pretty fixated on getting my clause orders right, right, so I was surprised this was allowed

marshall 2020-09-16T21:50:48.480200Z

Rules will be pushed down until required bindings are satisfied

marshall 2020-09-16T21:51:11.480400Z

Youll get an error if it cant ever satisfy them

marshall 2020-09-16T21:54:09.480600Z

That is documented for or and not clauses but not for rules, i will double check and also add documentation

2020-09-16T21:58:02.480800Z

Ah thank you! Then I learned something today and it was worth to ask

Nassin 2020-09-16T22:14:46.481100Z

anything that needs to leave the process in impure, networks failing is common