datomic

enn 2025-09-04T19:31:17.485089Z

I'm realizing that I might have some out-of-date ideas about tempids. I see in https://docs.datomic.com/transactions/partitions.html#making-temporary-ids that the style of tempid returned by d/tempid is obsolete, and there is now a new mechanism for partition assignment. I also see that in the docs around transaction data (https://docs.datomic.com/transactions/transaction-data-reference.html#tempids), user-provided tempids are expected to always be strings. Are there no remaining reasons to use d/tempid in new code? That makes sense. But I'm a little surprised that the datomic.db.DbId tempids are treated differently than string tempids by most of the Datomic API (`d/pull`, d/datoms, d/q, d/entity, etc.). Passing the old datomic.db.DbId tempids gets you an empty result, while passing a string throws. I guess the reasoning here is that passing tempids to functions that read from a database is undefined and so there is no need for consistency. But I find it a pretty common occurrence when implementing upsert operations to have an identifier that may be either a tempid or an entity id, and it feels a little strange to wrap everything in (when-not (string? eid) ...). Am I missing a better approach here?

ghadi 2025-09-04T20:07:18.444089Z

> missing a better approach here? I'm missing the problem statement > I find it a pretty common occurrence when implementing upsert operations to have an identifier that may be either a tempid or an entity id ok, so what's the problem? you are sending in a tempid or an entity id to the function that produces transaction data, right?

enn 2025-09-04T20:27:16.632169Z

Yes. The problem statement is: unless I'm certain a given ID will never be a string tempid, I have to put a guard around anywhere I pass it to the Datomic API to avoid an exception. It would be nice if the Datomic API treated anything that is not resolvable (e.g.: string tempid, datomic.db.DbId tempid, non-resolvable integer entity ID, non-resolvable lookup ref) equivalently. It's surprising behavior to me that these functions throw with some tempids and not others.

ghadi 2025-09-04T20:29:31.492669Z

none of those functions work with tempids

ghadi 2025-09-04T20:29:44.303639Z

only transact has anything about tempids

ghadi 2025-09-04T20:31:02.089159Z

what exactly is an example of something you are doing with prior to calling an "upsert" with d/transact?

enn 2025-09-04T21:22:07.697649Z

> none of those functions work with tempids Maybe not by design, but in practice, they return the correct answer if you try to use them with a d/tempid-generated ID to get some attribute value from a db (the correct answer being that there is no such value). One example is a helper tx function for resetting a cardinality-many attr to match a provided input set. To implement this, you need to get the attribute's current value to compare with the input set to determine which values to add and retract. So you end up with code like:

(let [desired-vals ...
      current-vals (:track/artists (d/entity db eid))
      to-add       (set/difference desired-vals current-vals)
      to-retract   (set/difference current-vals desired-vals]
  ...)
If eid is the result of calling d/tempid, current-vals will be nil, which is correct--there are no extant values that you need to retract because this is a new entity. If eid is a string tempid, d/entity throws this exception: Execution error (Exceptions$IllegalArgumentExceptionInfo) at datomic.error/arg (error.clj:79). :db.error/not-a-keyword Cannot interpret as a keyword: foo, no leading : .

enn 2025-09-04T21:25:59.447429Z

I see this forum post regarding the same behavior: https://forum.datomic.com/t/datomic-db-dbid-tempids-vs-string-tempids-as-arguments-to-queries/2185.

ghadi 2025-09-04T21:27:45.963619Z

sorry I didn't read past > not by design

ghadi 2025-09-04T21:29:35.717329Z

this is all UB

ghadi 2025-09-04T21:30:08.256239Z

the string error is trying to coerce a string to a keyword so it can find an eid from an ident kw

ghadi 2025-09-04T21:31:18.469059Z

the other d/tempid ret returning nil through d/entity is going through I have no idea what path, but this is all wrong

ghadi 2025-09-04T21:32:16.729379Z

I want to correct something I said earlier: tempids are only valid within a single call to d/transact, and calling d/resolve-tempid on stuff returning from that call to transact

ghadi 2025-09-04T19:55:15.309609Z

> That makes sense. But I'm a little surprised that the datomic.db.DbId tempids are treated differently than string tempids by most of the Datomic API (`d/pull`, d/datoms, d/q, d/entity, etc.). Passing the old datomic.db.DbId tempids gets you an empty result, while passing a string throws. tempids have no meaning outside of a single invocation of transact

ghadi 2025-09-04T19:56:47.640489Z

I am surprised by the strong verbiage in the docs @enn