clojure

seancorfield 2026-05-20T14:07:51.991489Z

Before I create Asks for either of these -- am I the only person who has written either of these fns/macros? • interleave-all -- like partition-all is to partition, this stops when the longest sequence is exhausted, not when the shortest sequence is exhausted • condp-> & condp->> -- like cond-> and cond->> but they thread the expression through the predicates as well as through the expressions You can see the implementations here https://github.com/worldsingles/commons/blob/master/src/ws/clojure/extensions.clj and they're the only two things we use from this library at this point, so I'd like to retire the library. Having that fn and those two macros in core would be my preference (of course) but if no one has ever felt the need, that would be pointless.

seancorfield 2026-05-21T13:41:58.788909Z

I think that's rather non-idiomatic for Clojure. I can imagine it more in Common Lisp, I guess...

➕ 1
hrtmt brng 2026-05-21T19:10:23.225639Z

Maybe not very idiomatic. But so many nested if-constructs are also not nice.

Alex Miller (Clojure team) 2026-05-21T19:15:01.552399Z

when I have code like this, I try to drive conditionality into the steps and turn the main code back into a simple pipeline

Max 2026-05-21T19:18:13.717579Z

I've also seen a handy macro floating around a few times for flattening this sort of thing: https://clojurians.slack.com/archives/C03S1KBA2/p1718287615736349?thread_ts=1718286671.849899&cid=C03S1KBA2

😅 1
seancorfield 2026-05-20T14:09:34.439779Z

For context, I think we have just two uses of interleave-all and ten of condp-> (and none of condp->> -- that was added purely for symmetry in the library) -- in 150K lines of Clojure.

Alex Miller (Clojure team) 2026-05-20T14:10:26.671569Z

you've already asked for https://ask.clojure.org/index.php/12125/add-interleave-all-like-partition-all-is-to-partition

😄 1
Alex Miller (Clojure team) 2026-05-20T14:11:05.614689Z

and there is an ask for condp-> https://ask.clojure.org/index.php/2365/adding-condp-and-condp-macros-to-core-library

seancorfield 2026-05-20T14:12:31.781029Z

Oh... and interleave-all got 14 upvotes and has a Jira. And interesting that it wasn't me who requested condp-> / condp->>! Thanks, @alexmiller

djanus 2026-05-20T14:13:02.408619Z

I have this version of interleave-all in one of my codebases (now that I look at it, it looks buggy...):

(defn interleave-all
  [x y]
  (into (vec (interleave x y)) (drop (count y) x)))

seancorfield 2026-05-20T14:14:45.659589Z

@dj942 We have to do this across an arbitrary number of sequences, hence the generalized c1 c2 & colls arity.

seancorfield 2026-05-20T14:15:58.590779Z

The condp-> in that old Ask isn't quite the same, as it just invokes the pred on the expression rather than threading, so I'll add a note about that (and maybe add a comment to the Jira too).

2026-05-20T14:20:16.334009Z

https://github.com/weavejester/medley/blob/822981871facb27630dcba03cce2924a34989963/src/medley/core.cljc#L347-L364 exists in medley, which i take as a good sign for its general usage

👍 1
2026-05-20T14:27:14.170399Z

and plumatic/plumbing and taoensso/encore. github gives me roughly 225 uses of interleave-all in a call-position

👍🏻 1
2026-05-20T16:26:27.458009Z

I do recall pulling interleave-all into several projects over the years. Copy and pasted it from medley. Just added an upvote on Ask.

Max 2026-05-21T02:38:54.044569Z

I've occasionally written condp-> for one-offs but I've always wanted the predicate behavior to be opt-in per condition, kind of like how condp uses :>> to opt into calling a fn on the predicate result. Most of my condp->s have a majority of threaded predicates mixed in with a minority of constant predicates, and it's annoying to have to wrap those in ((constantly (the-actual-predicate ...))). I haven't spent enough hammock time on it to have a good idea of how it'd actually work though.

hrtmt brng 2026-05-21T03:59:08.468319Z

What about an extension to let instead of condp ? https://ask.clojure.org/index.php/14999/controlled-bailout-jira-issue-2213

emccue 2026-05-20T15:07:46.438259Z

Finally made a workable enough prototype of the query interface i've always wanted

emccue 2026-05-20T15:08:34.285529Z

So this:

(jsonquery/execute! (user/db)
                    {:select [:name
                              :version
                              :target_platform
                              :mandated
                              :synthetic
                              [:requires {:select [:module :version :static :transitive :mandated :synthetic]
                                          :from :repository.module_requires
                                          :join-on [:id :module_id]}]
                              [:exports {:select [:package :to :mandated :synthetic]
                                         :from :repository.module_exports
                                         :join-on [:id :module_id]}]
                              [:uses {:select [:service]
                                      :from :repository.module_uses
                                      :join-on [:id :module_id]}]
                              [:provides {:select [:service :with]
                                          :from :repository.module_provides
                                          :join-on [:id :module_id]}]
                              [:packages {:select [:package]
                                          :from :repository.module_package
                                          :join-on [:id :module_id]}]
                              [:hashes {:select [:module :algorithm :hash]
                                        :from :repository.module_hash
                                        :join-on [:id :module_id]}]]
                     :from :repository.module
                     :order-by [[:repository.module.name :asc]
                                [:repository.module.version :desc]]
                     :limit 5})

emccue 2026-05-20T15:08:39.947889Z

Will turn into a query like this

emccue 2026-05-20T15:09:07.249729Z

{:select [[[:json_build_object
            [:raw "'name'"]
            :repository.module.name
            [:raw "'version'"]
            :repository.module.version
            [:raw "'target_platform'"]
            :repository.module.target_platform
            [:raw "'mandated'"]
            :repository.module.mandated
            [:raw "'synthetic'"]
            :repository.module.synthetic
            [:raw "'requires'"]
            [[:array
              {:select [[[:json_build_object
                          [:raw "'module'"]
                          :repository.module_requires.module
                          [:raw "'version'"]
                          :repository.module_requires.version
                          [:raw "'static'"]
                          :repository.module_requires.static
                          [:raw "'transitive'"]
                          :repository.module_requires.transitive
                          [:raw "'mandated'"]
                          :repository.module_requires.mandated
                          [:raw "'synthetic'"]
                          :repository.module_requires.synthetic]]],
               :from :repository.module_requires,
               :where [[:= :repository.module.id :repository.module_requires.module_id]]}]]
            [:raw "'exports'"]
            [[:array
              {:select [[[:json_build_object
                          [:raw "'package'"]
                          :repository.module_exports.package
                          [:raw "'to'"]
                          :
                          [:raw "'mandated'"]
                          :repository.module_exports.mandated
                          [:raw "'synthetic'"]
                          :repository.module_exports.synthetic]]],
               :from :repository.module_exports,
               :where [[:= :repository.module.id :repository.module_exports.module_id]]}]]
            [:raw "'uses'"]
            [[:array
              {:select [[[:json_build_object [:raw "'service'"] :repository.module_uses.service]]],
               :from :repository.module_uses,
               :where [[:= :repository.module.id :repository.module_uses.module_id]]}]]
            [:raw "'provides'"]
            [[:array
              {:select [[[:json_build_object
                          [:raw "'service'"]
                          :repository.module_provides.service
                          [:raw "'with'"]
                          :repository.module_provides.with]]],
               :from :repository.module_provides,
               :where [[:= :repository.module.id :repository.module_provides.module_id]]}]]
            [:raw "'packages'"]
            [[:array
              {:select [[[:json_build_object [:raw "'package'"] :repository.module_package.package]]],
               :from :repository.module_package,
               :where [[:= :repository.module.id :repository.module_package.module_id]]}]]
            [:raw "'hashes'"]
            [[:array
              {:select [[[:json_build_object
                          [:raw "'module'"]
                          :repository.module_hash.module
                          [:raw "'algorithm'"]
                          :repository.module_hash.algorithm
                          [:raw "'hash'"]
                          :repository.module_hash.hash]]],
               :from :repository.module_hash,
               :where [[:= :repository.module.id :repository.module_hash.module_id]]}]]]]],
 :from :repository.module,
 :limit 5,
 :order-by [[:repository.module.name :asc] [:repository.module.version :desc]]}

emccue 2026-05-20T15:10:07.219699Z

SELECT
  JSON_BUILD_OBJECT(
    'name',
    "repository"."module"."name",
    'version',
    "repository"."module"."version",
    'target_platform',
    "repository"."module"."target_platform",
    'mandated',
    "repository"."module"."mandated",
    'synthetic',
    "repository"."module"."synthetic",
    'requires',
    (
      ARRAY(
        SELECT
          JSON_BUILD_OBJECT(
            'module', "repository"."module_requires"."module",
            'version', "repository"."module_requires"."version",
            'static', "repository"."module_requires"."static",
            'transitive', "repository"."module_requires"."transitive",
            'mandated', "repository"."module_requires"."mandated",
            'synthetic', "repository"."module_requires"."synthetic"
          )
        FROM
          "repository"."module_requires"
        WHERE
          (
            "repository"."module"."id" = "repository"."module_requires"."module_id"
          )
      )
    ),
    'exports',
    (
      ARRAY(
        SELECT
          JSON_BUILD_OBJECT(
            'package', "repository"."module_exports"."package",
            'to', "repository"."module_exports"."to",
            'mandated', "repository"."module_exports"."mandated",
            'synthetic', "repository"."module_exports"."synthetic"
          )
        FROM
          "repository"."module_exports"
        WHERE
          (
            "repository"."module"."id" = "repository"."module_exports"."module_id"
          )
      )
    ),
    'uses',
    (
      ARRAY(
        SELECT
          JSON_BUILD_OBJECT(
            'service', "repository"."module_uses"."service"
          )
        FROM
          "repository"."module_uses"
        WHERE
          (
            "repository"."module"."id" = "repository"."module_uses"."module_id"
          )
      )
    ),
    'provides',
    (
      ARRAY(
        SELECT
          JSON_BUILD_OBJECT(
            'service', "repository"."module_provides"."service",
            'with', "repository"."module_provides"."with"
          )
        FROM
          "repository"."module_provides"
        WHERE
          (
            "repository"."module"."id" = "repository"."module_provides"."module_id"
          )
      )
    ),
    'packages',
    (
      ARRAY(
        SELECT
          JSON_BUILD_OBJECT(
            'package', "repository"."module_package"."package"
          )
        FROM
          "repository"."module_package"
        WHERE
          (
            "repository"."module"."id" = "repository"."module_package"."module_id"
          )
      )
    ),
    'hashes',
    (
      ARRAY(
        SELECT
          JSON_BUILD_OBJECT(
            'module', "repository"."module_hash"."module",
            'algorithm', "repository"."module_hash"."algorithm",
            'hash', "repository"."module_hash"."hash"
          )
        FROM
          "repository"."module_hash"
        WHERE
          (
            "repository"."module"."id" = "repository"."module_hash"."module_id"
          )
      )
    )
  )
FROM
  "repository"."module"
ORDER BY
  "repository"."module"."name" ASC,
  "repository"."module"."version" DESC
LIMIT
  ? 5

emccue 2026-05-20T15:10:15.499999Z

which in turns gives results like this

emccue 2026-05-20T15:10:34.556019Z

[{:mandated false,
  :name "be.yildizgames.common.properties",
  :target_platform "universal",
  :exports [{:package "be.yildizgames.common.properties", :to nil, :mandated false, :synthetic false}],
  :requires [{:module "java.base", :version nil, :static false, :transitive false, :mandated true, :synthetic false}],
  :uses [],
  :packages [{:package "be.yildizgames.common.properties"}],
  :provides [],
  :version "1.0.0",
  :synthetic false,
  :hashes []}
 {:mandated false,
  :name "com.azure.digitaltwins.core",
  :target_platform "universal",
  :exports [{:package "com.azure.digitaltwins.core", :to nil, :mandated false, :synthetic false}
            {:package "com.azure.digitaltwins.core.models", :to nil, :mandated false, :synthetic false}],
  :requires [{:module "com.azure.core",
              :version "1.55.4",
              :static false,
              :transitive true,
              :mandated false,
              :synthetic false}
             {:module "java.base", :version nil, :static false, :transitive false, :mandated true, :synthetic false}],
  :uses [],
  :packages [{:package "com.azure.digitaltwins.core"}
             {:package "com.azure.digitaltwins.core.implementation"}
             {:package "com.azure.digitaltwins.core.implementation.converters"}
             {:package "com.azure.digitaltwins.core.implementation.models"}
             {:package "com.azure.digitaltwins.core.implementation.serializer"}
             {:package "com.azure.digitaltwins.core.models"}],
  :provides [],
  :version "1.5.0",
  :synthetic false,
  :hashes []}
 {:mandated false,
  :name "com.azure.resourcemanager.hybridkubernetes",
  :target_platform "universal",
  :exports [{:package "com.azure.resourcemanager.hybridkubernetes", :to nil, :mandated false, :synthetic false}
            {:package "com.azure.resourcemanager.hybridkubernetes.fluent", :to nil, :mandated false, :synthetic false}
            {:package "com.azure.resourcemanager.hybridkubernetes.fluent.models",
             :to nil,
             :mandated false,
             :synthetic false}
            {:package "com.azure.resourcemanager.hybridkubernetes.models", :to nil, :mandated false, :synthetic false}],
  :requires [{:module "com.azure.core.management",
              :version "1.17.0",
              :static false,
              :transitive true,
              :mandated false,
              :synthetic false}
             {:module "java.base", :version nil, :static false, :transitive false, :mandated true, :synthetic false}],
  :uses [],
  :packages [{:package "com.azure.resourcemanager.hybridkubernetes"}
             {:package "com.azure.resourcemanager.hybridkubernetes.fluent"}
             {:package "com.azure.resourcemanager.hybridkubernetes.fluent.models"}
             {:package "com.azure.resourcemanager.hybridkubernetes.implementation"}
             {:package "com.azure.resourcemanager.hybridkubernetes.models"}],
  :provides [],
  :version "1.1.0-beta.1",
  :synthetic false,
  :hashes []}
 {:mandated false,
  :name "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes",
  :target_platform "universal",
  :exports [{:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes",
             :to nil,
             :mandated false,
             :synthetic false}
            {:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes.fluent",
             :to nil,
             :mandated false,
             :synthetic false}
            {:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes.fluent.models",
             :to nil,
             :mandated false,
             :synthetic false}
            {:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes.models",
             :to nil,
             :mandated false,
             :synthetic false}],
  :requires [{:module "com.azure.core.management",
              :version "1.18.0",
              :static false,
              :transitive true,
              :mandated false,
              :synthetic false}
             {:module "java.base", :version nil, :static false, :transitive false, :mandated true, :synthetic false}],
  :uses [],
  :packages [{:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes"}
             {:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes.fluent"}
             {:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes.fluent.models"}
             {:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes.implementation"}
             {:package "com.azure.resourcemanager.kubernetesconfiguration.privatelinkscopes.models"}],
  :provides [],
  :version "1.0.0-beta.1",
  :synthetic false,
  :hashes []}
 {:mandated false,
  :name "com.azure.resourcemanager.powerbidedicated",
  :target_platform "universal",
  :exports [{:package "com.azure.resourcemanager.powerbidedicated", :to nil, :mandated false, :synthetic false}
            {:package "com.azure.resourcemanager.powerbidedicated.fluent", :to nil, :mandated false, :synthetic false}
            {:package "com.azure.resourcemanager.powerbidedicated.fluent.models",
             :to nil,
             :mandated false,
             :synthetic false}
            {:package "com.azure.resourcemanager.powerbidedicated.models", :to nil, :mandated false, :synthetic false}],
  :requires [{:module "com.azure.core.management",
              :version "1.15.6",
              :static false,
              :transitive true,
              :mandated false,
              :synthetic false}
             {:module "java.base", :version nil, :static false, :transitive false, :mandated true, :synthetic false}],
  :uses [],
  :packages [{:package "com.azure.resourcemanager.powerbidedicated"}
             {:package "com.azure.resourcemanager.powerbidedicated.fluent"}
             {:package "com.azure.resourcemanager.powerbidedicated.fluent.models"}
             {:package "com.azure.resourcemanager.powerbidedicated.implementation"}
             {:package "com.azure.resourcemanager.powerbidedicated.models"}],
  :provides [],
  :version "1.0.0",
  :synthetic false,
  :hashes []}

emccue 2026-05-20T15:10:42.365629Z

i know, huge chunks of text

emccue 2026-05-20T15:10:54.692309Z

but: notice how I got all the data I wanted from the db in a single round trip

emccue 2026-05-20T15:11:14.035369Z

no N+1 issues in sight

emccue 2026-05-20T15:11:37.009859Z

(ns dev.mccue.jsonquery
  (:require [cheshire.core :as cheshire]
            [honey.sql :as sql]
            [next.jdbc :as jdbc])
  (:refer-clojure :exclude [format]))

(defn- ->honeysql-helper
  [query parent-table]
  (let [table (:from query)
        conditions (filterv some?
                            [(when parent-table
                               (when-not (:join-on query)
                                 (throw (IllegalArgumentException. (str
                                                                     "Missing :join-on for "
                                                                     parent-table " and " table))))
                               [:=
                                (keyword (str (name parent-table)
                                              "."
                                              (name (first (:join-on query)))))
                                (keyword (str (name table)
                                              "."
                                              (name (second (:join-on query)))))])
                             (:where query)])]

    (cond->
      {:select [[(vec
                  (cons :json_build_object
                        (mapcat
                          (fn [selection]
                            (if (keyword? selection)
                              [[:raw (str "'" (name selection) "'")]
                               (keyword (str (name table) "." (name selection)))]
                              (if (:single (meta selection))
                                [[:raw (str "'" (name (first selection)) "'")]
                                 (->honeysql-helper (second selection) table)]
                                [[:raw (str "'" (name (first selection)) "'")]
                                 [[:array (->honeysql-helper (second selection) table)]]])))
                          (:select query))))]]
       :from table}
      (seq conditions) (assoc :where conditions)
      (:limit query) (assoc :limit (:limit query))
      (:order-by query) (assoc :order-by (:order-by query)))))

(defn ->honeysql
  [query]
  (->honeysql-helper query nil))

(defn format
  [query]
  (sql/format query {:quoted true}))

(defn execute!
  [db query]
  (mapv (comp #(cheshire/parse-string % keyword) str :json_build_object)
        (jdbc/execute!
          db
          (format
            (->honeysql query)))))

2026-05-20T15:20:50.515779Z

that's really cool!