Skip to content

Instantly share code, notes, and snippets.

@cgrand
Last active December 10, 2021 16:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cgrand/18b7b0fdceb0b5a20b285df3b8bd9b07 to your computer and use it in GitHub Desktop.
Save cgrand/18b7b0fdceb0b5a20b285df3b8bd9b07 to your computer and use it in GitHub Desktop.
Putting expressions in datomic-flavored datalog :where clauses. (This is a snippet I had laying around and that I'd like to develop further.)
(ns tensegritics.expressive.datalog
"Putting expressions in datomic-flavored datalog :where clauses.")
;; I (cgrand) believes that datalog literally lacks expressivity because
;; it's not expression-based but clause-based and thus requires a lot of
;; gratuitous naming.
;; Here is an attempt to compiles expressions into clauses.
(defn- grounded? [[e a v]] (and (keyword? a) (not (symbol? v))))
(defn- unravel [x]
(cond
(not (coll? x))
[[x]]
(map? x)
(let [id (or (:db/id x) (gensym '?eid))
x (dissoc x :db/id)]
[[id]
(let [[front back]
(reduce-kv
(fn [[front back] a v]
(let [mk-clause
(if (and (keyword? a) (.startsWith (name a) "_"))
(let [a (keyword (namespace a) (subs (name a) 1))]
(fn [v] [v a id]))
(fn [v] [id a v]))
[vals clauses] (unravel v)]
[(into front (map mk-clause) vals)
(into back clauses)]))
[[] []]
x)]
(-> []
(into (filter grounded?) front)
(into (remove grounded?) front)
(into back)))])
(vector? x)
(let [x (map unravel x)]
[(mapcat first x) (mapcat second x)]) :else
(throw (ex-info "Can't unravel this expression"))))
(defn where
"Takes an expr (generally a map) and flatten it into a coll of clauses."
[expr] (second (unravel expr)))
(comment
; this:
(where '#:game
{:teams #:team
{:players #:player{:name ?pid}
:final-score ?ts}
:date ?d})
; returns:
[[?eid7207 :game/teams ?eid7208]
[?eid7207 :game/date ?d]
[?eid7208 :team/players ?eid7209]
[?eid7208 :team/final-score ?ts]
[?eid7209 :player/name ?pid]]
; or this one:
(where '#:artist
{:name ?artist-name
:release/_artists #:release {:name ?release-name}})
; returns:
[[?eid7213 :artist/name ?artist-name]
[?eid7214 :release/artists ?eid7213]
[?eid7214 :release/name ?release-name]])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment