[Git][clojure-team/medley-clojure][upstream] New upstream version 1.10.0

Jérôme Charaoui (@lavamind) gitlab at salsa.debian.org
Thu Jul 30 05:21:56 BST 2026



Jérôme Charaoui pushed to branch upstream at Debian Clojure Maintainers / medley-clojure


Commits:
9f623c85 by Jérôme Charaoui at 2026-07-29T22:15:02-04:00
New upstream version 1.10.0
- - - - -


11 changed files:

- + .github/workflows/test.yml
- .gitignore
- + CONTRIBUTING.md
- + LICENSE.txt
- README.md
- − circle.yml
- + deps-clr.edn
- + deps.edn
- project.clj
- src/medley/core.cljc
- test/medley/core_test.cljc


Changes:

=====================================
.github/workflows/test.yml
=====================================
@@ -0,0 +1,40 @@
+name: Tests
+on: [push, pull_request]
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout at v6
+
+      - name: Prepare java
+        uses: actions/setup-java at v5
+        with:
+          distribution: 'zulu'
+          java-version: '8'
+
+      - name: Prepare dotnet
+        uses: xt0rted/setup-dotnet at v1.5.0
+
+      - name: Prepare Clojure CLR
+        run: |
+          dotnet tool install --global Clojure.Main --version 1.12.2
+          dotnet tool install --global Clojure.Cljr --version 0.1.0-alpha11
+
+      - name: Install clojure tools
+        uses: DeLaGuardo/setup-clojure at 13.6.0
+        with:
+          lein: 2.12.0
+
+      - name: Cache clojure dependencies
+        uses: actions/cache at v5
+        with:
+          path: ~/.m2/repository
+          key: cljdeps-${{ hashFiles('project.clj') }}
+          restore-keys: cljdeps-
+
+      - name: Run clj and cljs tests
+        run: lein test-all
+
+      - name: Run cljr tests
+        run: cljr -X:test


=====================================
.gitignore
=====================================
@@ -8,3 +8,5 @@ pom.xml.asc
 *.class
 /.lein-*
 /.nrepl-port
+/.cpcache
+/.clj-kondo


=====================================
CONTRIBUTING.md
=====================================
@@ -0,0 +1,32 @@
+# Contributing Guidelines
+
+**Do** follow [the seven rules of a great Git commit message][1].
+
+**Do** follow [the Clojure Style Guide][2].
+
+**Do** include tests for your change when appropriate.
+
+**Do** ensure that the CI checks pass.
+
+**Do** squash the commits in your PR to remove corrections
+irrelevant to the code history, once the PR has been reviewed.
+
+**Do** feel free to pester the project maintainers about the PR if it
+hasn't been responded to. Sometimes notifications can be missed.
+
+**Don't** include more than one feature or fix in a single PR.
+
+**Don't** include changes unrelated to the purpose of the PR. This
+includes changing the project version number, adding lines to the
+`.gitignore` file, or changing the indentation or formatting.
+
+**Don't** open a new PR if changes are requested. Just push to the
+same branch and the PR will be updated.
+
+**Don't** overuse vertical whitespace; avoid multiple sequential blank
+lines.
+
+**Don't** docstring private vars or functions.
+
+[1]: https://chris.beams.io/posts/git-commit/#seven-rules
+[2]: https://github.com/bbatsov/clojure-style-guide


=====================================
LICENSE.txt
=====================================
@@ -0,0 +1,204 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
+CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+     a) in the case of the initial Contributor, the initial code and
+     documentation distributed under this Agreement, and
+     b) in the case of each subsequent Contributor:
+          i) changes to the Program, and
+          ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are
+distributed by that particular Contributor. A Contribution 'originates' from a
+Contributor if it was added to the Program by such Contributor itself or
+anyone acting on such Contributor's behalf. Contributions do not include
+additions to the Program which: (i) are separate modules of software
+distributed in conjunction with the Program under their own license agreement,
+and (ii) are not derivative works of the Program.
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when
+combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+2. GRANT OF RIGHTS
+
+     a) Subject to the terms of this Agreement, each Contributor hereby grants
+     Recipient a non-exclusive, worldwide, royalty-free copyright license to
+     reproduce, prepare derivative works of, publicly display, publicly
+     perform, distribute and sublicense the Contribution of such Contributor,
+     if any, and such derivative works, in source code and object code form.
+
+     b) Subject to the terms of this Agreement, each Contributor hereby grants
+     Recipient a non-exclusive, worldwide, royalty-free patent license under
+     Licensed Patents to make, use, sell, offer to sell, import and otherwise
+     transfer the Contribution of such Contributor, if any, in source code and
+     object code form. This patent license shall apply to the combination of
+     the Contribution and the Program if, at the time the Contribution is
+     added by the Contributor, such addition of the Contribution causes such
+     combination to be covered by the Licensed Patents. The patent license
+     shall not apply to any other combinations which include the Contribution.
+     No hardware per se is licensed hereunder.
+
+     c) Recipient understands that although each Contributor grants the
+     licenses to its Contributions set forth herein, no assurances are
+     provided by any Contributor that the Program does not infringe the patent
+     or other intellectual property rights of any other entity. Each
+     Contributor disclaims any liability to Recipient for claims brought by
+     any other entity based on infringement of intellectual property rights or
+     otherwise. As a condition to exercising the rights and licenses granted
+     hereunder, each Recipient hereby assumes sole responsibility to secure
+     any other intellectual property rights needed, if any. For example, if a
+     third party patent license is required to allow Recipient to distribute
+     the Program, it is Recipient's responsibility to acquire that license
+     before distributing the Program.
+
+     d) Each Contributor represents that to its knowledge it has sufficient
+     copyright rights in its Contribution, if any, to grant the copyright
+     license set forth in this Agreement.
+
+3. REQUIREMENTS
+A Contributor may choose to distribute the Program in object code form under
+its own license agreement, provided that:
+
+     a) it complies with the terms and conditions of this Agreement; and
+
+     b) its license agreement:
+          i) effectively disclaims on behalf of all Contributors all
+          warranties and conditions, express and implied, including warranties
+          or conditions of title and non-infringement, and implied warranties
+          or conditions of merchantability and fitness for a particular
+          purpose;
+          ii) effectively excludes on behalf of all Contributors all liability
+          for damages, including direct, indirect, special, incidental and
+          consequential damages, such as lost profits;
+          iii) states that any provisions which differ from this Agreement are
+          offered by that Contributor alone and not by any other party; and
+          iv) states that source code for the Program is available from such
+          Contributor, and informs licensees how to obtain it in a reasonable
+          manner on or through a medium customarily used for software
+          exchange.
+
+When the Program is made available in source code form:
+
+     a) it must be made available under this Agreement; and
+
+     b) a copy of this Agreement must be included with each copy of the
+     Program.
+Contributors may not remove or alter any copyright notices contained within
+the Program.
+
+Each Contributor must identify itself as the originator of its Contribution,
+if any, in a manner that reasonably allows subsequent Recipients to identify
+the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+Commercial distributors of software may accept certain responsibilities with
+respect to end users, business partners and the like. While this license is
+intended to facilitate the commercial use of the Program, the Contributor who
+includes the Program in a commercial product offering should do so in a manner
+which does not create potential liability for other Contributors. Therefore,
+if a Contributor includes the Program in a commercial product offering, such
+Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+every other Contributor ("Indemnified Contributor") against any losses,
+damages and costs (collectively "Losses") arising from claims, lawsuits and
+other legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such Commercial
+Contributor in connection with its distribution of the Program in a commercial
+product offering. The obligations in this section do not apply to any claims
+or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must: a)
+promptly notify the Commercial Contributor in writing of such claim, and b)
+allow the Commercial Contributor to control, and cooperate with the Commercial
+Contributor in, the defense and any related settlement negotiations. The
+Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product
+offering, Product X. That Contributor is then a Commercial Contributor. If
+that Commercial Contributor then makes performance claims, or offers
+warranties related to Product X, those performance claims and warranties are
+such Commercial Contributor's responsibility alone. Under this section, the
+Commercial Contributor would have to defend claims against the other
+Contributors related to those performance claims and warranties, and if a
+court requires any other Contributor to pay any damages as a result, the
+Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
+Recipient is solely responsible for determining the appropriateness of using
+and distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement , including but not limited to the
+risks and costs of program errors, compliance with applicable laws, damage to
+or loss of data, programs or equipment, and unavailability or interruption of
+operations.
+
+6. DISCLAIMER OF LIABILITY
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
+LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of the
+remainder of the terms of this Agreement, and without further action by the
+parties hereto, such provision shall be reformed to the minimum extent
+necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program itself
+(excluding combinations of the Program with other software or hardware)
+infringes such Recipient's patent(s), then such Recipient's rights granted
+under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to
+comply with any of the material terms or conditions of this Agreement and does
+not cure such failure in a reasonable period of time after becoming aware of
+such noncompliance. If all Recipient's rights under this Agreement terminate,
+Recipient agrees to cease use and distribution of the Program as soon as
+reasonably practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall continue
+and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in
+order to avoid inconsistency the Agreement is copyrighted and may only be
+modified in the following manner. The Agreement Steward reserves the right to
+publish new versions (including revisions) of this Agreement from time to
+time. No one other than the Agreement Steward has the right to modify this
+Agreement. The Eclipse Foundation is the initial Agreement Steward. The
+Eclipse Foundation may assign the responsibility to serve as the Agreement
+Steward to a suitable separate entity. Each new version of the Agreement will
+be given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version of the
+Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly
+stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
+licenses to the intellectual property of any Contributor under this Agreement,
+whether expressly, by implication, estoppel or otherwise. All rights in the
+Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to this
+Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial
+in any resulting litigation.


=====================================
README.md
=====================================
@@ -1,6 +1,4 @@
-# Medley
-
-[![Build Status](https://img.shields.io/circleci/project/weavejester/medley/master.svg)](https://circleci.com/gh/weavejester/medley/tree/master)
+# Medley [![Build Status](https://github.com/weavejester/medley/actions/workflows/test.yml/badge.svg)](https://github.com/weavejester/medley/actions/workflows/test.yml)
 
 Medley is a lightweight Clojure/ClojureScript library of useful,
 *mostly* pure functions that are "missing" from clojure.core.
@@ -13,11 +11,23 @@ and limits itself to a small set of general-purpose functions.
 
 ## Installation
 
-To install, add the following to your project `:dependencies`:
+Add the following dependency to your deps.edn file:
+
+    dev.weavejester/medley {:mvn/version "1.10.0"}
+
+Or to your Leiningen project file:
+
+    [dev.weavejester/medley "1.10.0"]
 
-    [medley "1.0.0"]
+Or to your deps-clr.edn file:
 
-## Breaking Changes
+    io.github.weavejester/medley {:git/tag "1.10.0" :git/sha "FIXME"}
+
+## Documentation
+
+* [API Docs](http://weavejester.github.io/medley/medley.core.html)
+
+## Pre-1.0 Breaking Changes
 
 In 0.7.0 the minimum Clojure version was changed from 1.5.1 to 1.7.0
 to take advantage of [reader conditionals][]. The `update` function
@@ -30,13 +40,9 @@ a version prior to 0.6.0, please update your usage of `greatest` and
 
 [reader conditionals]: http://dev.clojure.org/display/design/Reader+Conditionals
 
-## Documentation
-
-* [API Docs](http://weavejester.github.io/medley/medley.core.html)
-
 ## License
 
-Copyright © 2017 James Reeves
+Copyright © 2026 James Reeves
 
 Distributed under the Eclipse Public License either version 1.0 or (at
 your option) any later version.


=====================================
circle.yml deleted
=====================================
@@ -1,7 +0,0 @@
-general:
-  branches:
-    ignore:
-      - gh-pages
-test:
-  override:
-    - lein test-all


=====================================
deps-clr.edn
=====================================
@@ -0,0 +1,6 @@
+{:paths ["src"]
+ :aliases
+ {:test {:extra-paths ["test"]
+         :extra-deps  {io.github.dmiller/test-runner {:git/tag "v0.5.1clr" :git/sha "814e06f"}}
+         :exec-fn     cognitect.test-runner.api/test
+         :exec-args   {:dirs ["test"]}}}}


=====================================
deps.edn
=====================================
@@ -0,0 +1 @@
+{:deps {org.clojure/clojure {:mvn/version "1.9.0"}}}


=====================================
project.clj
=====================================
@@ -1,12 +1,12 @@
-(defproject medley "1.0.0"
+(defproject dev.weavejester/medley "1.10.0"
   :description "A lightweight library of useful, mostly pure functions"
   :url "https://github.com/weavejester/medley"
   :license {:name "Eclipse Public License"
             :url "http://www.eclipse.org/legal/epl-v10.html"}
-  :dependencies [[org.clojure/clojure "1.7.0"]]
-  :plugins [[lein-codox "0.10.3"]
-            [lein-cljsbuild "1.1.2"]
-            [lein-doo "0.1.6"]]
+  :dependencies [[org.clojure/clojure "1.9.0"]]
+  :plugins [[lein-codox "0.10.8"]
+            [lein-cljsbuild "1.1.8"]
+            [lein-doo "0.1.11"]]
   :codox
   {:output-path "codox"
    :metadata {:doc/format :markdown}
@@ -22,12 +22,13 @@
   :doo {:paths {:rhino "lein run -m org.mozilla.javascript.tools.shell.Main"}}
   :aliases
   {"test-cljs" ["doo" "rhino" "test" "once"]
-   "test-clj"  ["with-profile" "default:+1.8:+1.9" "test"]
+   "test-clj"  ["with-profile" "default:+1.10:+1.11:+1.12" "test"]
    "test-all"  ["do" ["test-clj"] ["test-cljs"]]}
   :profiles
-  {:provided {:dependencies [[org.clojure/clojurescript "1.7.228"]]}
-   :test {:dependencies [[org.mozilla/rhino "1.7.7"]]}
-   :dev {:dependencies [[criterium "0.4.3"]]
+  {:provided {:dependencies [[org.clojure/clojurescript "1.10.439"]]}
+   :test {:dependencies [[org.mozilla/rhino "1.9.1"]]}
+   :dev {:dependencies [[criterium "0.4.6"]]
          :jvm-opts ^:replace {}}
-   :1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}
-   :1.9 {:dependencies [[org.clojure/clojure "1.9.0-alpha15"]]}})
+   :1.10 {:dependencies [[org.clojure/clojure "1.10.0"]]}
+   :1.11 {:dependencies [[org.clojure/clojure "1.11.2"]]}
+   :1.12 {:dependencies [[org.clojure/clojure "1.12.4"]]}})


=====================================
src/medley/core.cljc
=====================================
@@ -1,10 +1,12 @@
 (ns medley.core
   "A small collection of useful, mostly pure functions that might not look out
   of place in the clojure.core namespace."
-  (:refer-clojure :exclude [boolean? ex-cause ex-message uuid uuid? random-uuid]))
+  (:refer-clojure :exclude [abs boolean? ex-cause ex-message random-uuid regexp?
+                            uuid uuid?]))
 
 (defn find-first
-  "Finds the first item in a collection that matches a predicate."
+  "Finds the first item in a collection that matches a predicate. Returns a
+  transducer when no collection is provided."
   ([pred]
    (fn [rf]
      (fn
@@ -15,48 +17,96 @@
           (ensure-reduced (rf result x))
           result)))))
   ([pred coll]
-   (reduce (fn [_ x] (if (pred x) (reduced x))) nil coll)))
+   (reduce (fn [_ x] (when (pred x) (reduced x))) nil coll)))
 
 (defn dissoc-in
-  "Dissociate a value in a nested assocative structure, identified by a sequence
+  "Dissociate a value in a nested associative structure, identified by a sequence
   of keys. Any collections left empty by the operation will be dissociated from
   their containing structures."
-  [m ks]
-  (if-let [[k & ks] (seq ks)]
-    (if (seq ks)
-      (let [v (dissoc-in (get m k) ks)]
-        (if (empty? v)
-          (dissoc m k)
-          (assoc m k v)))
-      (dissoc m k))
-    m))
+  ([m ks]
+   (if-let [[k & ks] (seq ks)]
+     (if (seq ks)
+       (let [v (dissoc-in (get m k) ks)]
+         (if (empty? v)
+           (dissoc m k)
+           (assoc m k v)))
+       (dissoc m k))
+     m))
+  ([m ks & kss]
+   (if-let [[ks' & kss] (seq kss)]
+     (recur (dissoc-in m ks) ks' kss)
+     (dissoc-in m ks))))
+
+(defn- editable? [coll]
+  #?(:cljs    (satisfies? cljs.core/IEditableCollection coll)
+     :default (instance? clojure.lang.IEditableCollection coll)))
+
+(defn- assoc-some-transient! [m k v]
+  (if (nil? v) m (assoc! m k v)))
 
 (defn assoc-some
-  "Associates a key with a value in a map, if and only if the value is not nil."
+  "Associates a key k, with a value v in a map m, if and only if v is not nil."
   ([m k v]
    (if (nil? v) m (assoc m k v)))
   ([m k v & kvs]
-   (reduce (fn [m [k v]] (assoc-some m k v))
-           (assoc-some m k v)
-           (partition 2 kvs))))
-
-(defn- editable? [coll]
-  #?(:clj  (instance? clojure.lang.IEditableCollection coll)
-     :cljs (satisfies? cljs.core.IEditableCollection coll)))
+   (if (editable? m)
+     (loop [acc (assoc-some-transient! (transient (or m {})) k v)
+            kvs kvs]
+       (if (next kvs)
+         (recur (assoc-some-transient! acc (first kvs) (second kvs)) (nnext kvs))
+         (if (zero? (count acc))
+           m
+           (with-meta (persistent! acc) (meta m)))))
+     (loop [acc (assoc-some m k v)
+            kvs kvs]
+       (if (next kvs)
+         (recur (assoc-some acc (first kvs) (second kvs)) (nnext kvs))
+         acc)))))
+
+(defn update-existing
+  "Updates a value in a map given a key and a function, if and only if the key
+  exists in the map. See: `clojure.core/update`."
+  {:arglists '([m k f & args])
+   :added    "1.1.0"}
+  ([m k f]
+   (if-let [kv (find m k)] (assoc m k (f (val kv))) m))
+  ([m k f x]
+   (if-let [kv (find m k)] (assoc m k (f (val kv) x)) m))
+  ([m k f x y]
+   (if-let [kv (find m k)] (assoc m k (f (val kv) x y)) m))
+  ([m k f x y z]
+   (if-let [kv (find m k)] (assoc m k (f (val kv) x y z)) m))
+  ([m k f x y z & more]
+   (if-let [kv (find m k)] (assoc m k (apply f (val kv) x y z more)) m)))
+
+(defn update-existing-in
+  "Updates a value in a nested associative structure, if and only if the key
+  path exists. See: `clojure.core/update-in`."
+  {:added "1.3.0"}
+  [m ks f & args]
+  (let [up (fn up [m ks f args]
+             (let [[k & ks] ks]
+               (if-let [kv (find m k)]
+                 (if ks
+                   (assoc m k (up (val kv) ks f args))
+                   (assoc m k (apply f (val kv) args)))
+                 m)))]
+    (up m ks f args)))
 
 (defn- reduce-map [f coll]
-  (if (editable? coll)
-    (persistent! (reduce-kv (f assoc!) (transient (empty coll)) coll))
-    (reduce-kv (f assoc) (empty coll) coll)))
+  (let [coll' (if (record? coll) (into {} coll) coll)]
+    (if (editable? coll')
+      (persistent! (reduce-kv (f assoc!) (transient (empty coll')) coll'))
+      (reduce-kv (f assoc) (empty coll') coll'))))
 
 (defn map-entry
   "Create a map entry for a key and value pair."
   [k v]
-  #?(:clj  (clojure.lang.MapEntry. k v)
-     :cljs [k v]))
+  #?(:cljs    (cljs.core/MapEntry. k v nil)
+     :default (clojure.lang.MapEntry. k v)))
 
 (defn map-kv
-  "Maps a function over the key/value pairs of an associate collection. Expects
+  "Maps a function over the key/value pairs of an associative collection. Expects
   a function that takes two arguments, the key and value, and returns the new
   key and value as a collection of two elements."
   [f coll]
@@ -68,9 +118,33 @@
   (reduce-map (fn [xf] (fn [m k v] (xf m (f k) v))) coll))
 
 (defn map-vals
-  "Maps a function over the values of an associative collection."
+  "Maps a function over the values of one or more associative collections.
+  The function should accept number-of-colls arguments. Any keys which are not
+  shared among all collections are ignored."
+  ([f coll]
+   (reduce-map (fn [xf] (fn [m k v] (xf m k (f v)))) coll))
+  ([f c1 & colls]
+   (reduce-map
+    (fn [xf]
+      (fn [m k v]
+        (if (every? #(contains? % k) colls)
+          (xf m k (apply f v (map #(get % k) colls)))
+          m)))
+    c1)))
+
+(defn map-kv-keys
+  "Maps a function over the key/value pairs of an associative collection, using
+  the return of the function as the new key."
+  {:added "1.2.0"}
   [f coll]
-  (reduce-map (fn [xf] (fn [m k v] (xf m k (f v)))) coll))
+  (reduce-map (fn [xf] (fn [m k v] (xf m (f k v) v))) coll))
+
+(defn map-kv-vals
+  "Maps a function over the key/value pairs of an associative collection, using
+  the return of the function as the new value."
+  {:added "1.2.0"}
+  [f coll]
+  (reduce-map (fn [xf] (fn [m k v] (xf m k (f k v)))) coll))
 
 (defn filter-kv
   "Returns a new associative collection of the items in coll for which
@@ -110,21 +184,21 @@
 
 (defn queue
   "Creates an empty persistent queue, or one populated with a collection."
-  ([] #?(:clj  clojure.lang.PersistentQueue/EMPTY
-         :cljs cljs.core.PersistentQueue.EMPTY))
+  ([] #?(:cljs    cljs.core/PersistentQueue.EMPTY
+         :default clojure.lang.PersistentQueue/EMPTY))
   ([coll] (into (queue) coll)))
 
 (defn queue?
   "Returns true if x implements clojure.lang.PersistentQueue."
   [x]
-  (instance? #?(:clj  clojure.lang.PersistentQueue
-                :cljs cljs.core.PersistentQueue) x))
+  (instance? #?(:cljs    cljs.core/PersistentQueue
+                :default clojure.lang.PersistentQueue) x))
 
 (defn boolean?
   "Returns true if x is a boolean."
   [x]
-  #?(:clj  (instance? Boolean x)
-     :cljs (or (true? x) (false? x))))
+  #?(:cljs    (or (true? x) (false? x))
+     :default (instance? Boolean x)))
 
 (defn least
   "Return the least argument (as defined by the compare function) in O(n) time."
@@ -134,6 +208,26 @@
   ([a b] (if (neg? (compare a b)) a b))
   ([a b & more] (reduce least (least a b) more)))
 
+(defn least-by
+  "Return the argument for which (keyfn x) is least. Determined by the compare
+  function in O(n) time. Prefer `clojure.core/min-key` if keyfn returns numbers."
+  {:arglists '([keyfn & xs])
+   :added "1.6.0"}
+  ([_] nil)
+  ([_ x] x)
+  ([keyfn x y] (if (neg? (compare (keyfn x) (keyfn y))) x y))
+  ([keyfn x y & more]
+   (let [kx (keyfn x) ky (keyfn y)
+         [v kv] (if (neg? (compare kx ky)) [x kx] [y ky])]
+     (loop [v v kv kv more more]
+       (if more
+         (let [w (first more)
+               kw (keyfn w)]
+           (if (pos? (compare kw kv))
+             (recur v kv (next more))
+             (recur w kw (next more))))
+         v)))))
+
 (defn greatest
   "Find the greatest argument (as defined by the compare function) in O(n) time."
   {:arglists '([& xs])}
@@ -142,6 +236,57 @@
   ([a b] (if (pos? (compare a b)) a b))
   ([a b & more] (reduce greatest (greatest a b) more)))
 
+(defn greatest-by
+  "Return the argument for which (keyfn x) is greatest. Determined by the compare
+  function in O(n) time. Prefer `clojure.core/max-key` if keyfn returns numbers."
+  {:arglists '([keyfn & xs])
+   :added "1.6.0"}
+  ([_] nil)
+  ([_ x] x)
+  ([keyfn x y] (if (pos? (compare (keyfn x) (keyfn y))) x y))
+  ([keyfn x y & more]
+   (let [kx (keyfn x) ky (keyfn y)
+         [v kv] (if (pos? (compare kx ky)) [x kx] [y ky])]
+     (loop [v v kv kv more more]
+       (if more
+         (let [w (first more)
+               kw (keyfn w)]
+           (if (neg? (compare kw kv))
+             (recur v kv (next more))
+             (recur w kw (next more))))
+         v)))))
+
+(defn join
+  "Lazily concatenates a collection of collections into a flat sequence."
+  {:added "1.1.0"}
+  [colls]
+  (lazy-seq
+   (when-let [s (seq colls)]
+     (concat (first s) (join (rest s))))))
+
+(defn deep-merge
+  "Recursively merges maps together. If all the maps supplied have nested maps
+  under the same keys, these nested maps are merged. Otherwise the value is
+  overwritten, as in `clojure.core/merge`."
+  {:arglists '([& maps])
+   :added    "1.1.0"}
+  ([])
+  ([a] a)
+  ([a b]
+   (when (or a b)
+     (letfn [(merge-entry [m e]
+               (let [k  (key e)
+                     v' (val e)]
+                 (if (contains? m k)
+                   (assoc m k (let [v (get m k)]
+                                (if (and (map? v) (map? v'))
+                                  (deep-merge v v')
+                                  v')))
+                   (assoc m k v'))))]
+       (reduce merge-entry (or a {}) (seq b)))))
+  ([a b & more]
+   (reduce deep-merge (or a {}) (cons b more))))
+
 (defn mapply
   "Applies a function f to the argument list formed by concatenating
   everything but the last element of args with the last element of
@@ -151,6 +296,54 @@
   ([f m]        (apply f (apply concat m)))
   ([f a & args] (apply f a (apply concat (butlast args) (last args)))))
 
+(defn collate-by
+  "Similar to `clojure.core/group-by`, this groups values in a collection,
+  coll, based on the return value of a function, keyf applied to each element.
+
+  Unlike `group-by`, the values of the map are constructed via an initf and
+  collatef function. The initf function is applied to the first element
+  matched by keyf, and defaults to the identity function. The collatef function
+  takes the result of initf and the next keyed element, and produces a new
+  value.
+
+  To put this in context, the `group-by` function can be defined as:
+
+      (defn group-by [f coll]
+        (collate-by f conj vector coll))
+
+  While the `medley.core/index-by` function can be (and is) defined as:
+
+      (defn index-by [f coll]
+        (collate-by f (fn [_ x] x) coll))"
+  {:added "1.8.0"}
+  ([keyf collatef coll]
+   (collate-by keyf collatef identity coll))
+  ([keyf collatef initf coll]
+   (persistent!
+    (reduce (fn [m v]
+              (let [k (keyf v)]
+                (assoc! m k #?(:cljs
+                               (if (contains? m k)
+                                 (collatef (get m k) v)
+                                 (initf v))
+                               :default
+                               (if-let [kv (find m k)]
+                                 (collatef (val kv) v)
+                                 (initf v))))))
+            (transient {})
+            coll))))
+
+(defn index-by
+  "Returns a map of the elements of coll keyed by the result of f on each
+  element. The value at each key will be the last element in coll associated
+  with that key. This function is similar to `clojure.core/group-by`, except
+  that elements with the same key are overwritten, rather than added to a
+  vector of values."
+  {:added "1.2.0"}
+  [f coll]
+  #_(persistent! (reduce #(assoc! %1 (f %2) %2) (transient {}) coll))
+  (collate-by f (fn [_ x] x) coll))
+
 (defn interleave-all
   "Returns a lazy seq of the first item in each coll, then the second, etc.
   Unlike `clojure.core/interleave`, the returned seq contains all items in the
@@ -166,13 +359,14 @@
         (or s1 s2)))))
   ([c1 c2 & colls]
    (lazy-seq
-    (let [ss (remove nil? (map seq (conj colls c2 c1)))]
-      (if (seq ss)
+    (let [ss (keep seq (conj colls c2 c1))]
+      (when (seq ss)
         (concat (map first ss) (apply interleave-all (map rest ss))))))))
 
 (defn distinct-by
   "Returns a lazy sequence of the elements of coll, removing any elements that
-  return duplicate values when passed to a function f."
+  return duplicate values when passed to a function f. Returns a stateful
+  transducer when no collection is provided."
   ([f]
    (fn [rf]
      (let [seen (volatile! #{})]
@@ -199,7 +393,8 @@
 
 (defn dedupe-by
   "Returns a lazy sequence of the elements of coll, removing any **consecutive**
-  elements that return duplicate values when passed to a function f."
+  elements that return duplicate values when passed to a function f. Returns a
+  stateful transducer when no collection is provided."
   ([f]
    (fn [rf]
      (let [pv (volatile! ::none)]
@@ -218,7 +413,8 @@
 
 (defn take-upto
   "Returns a lazy sequence of successive items from coll up to and including
-  the first item for which `(pred item)` returns true."
+  the first item for which `(pred item)` returns true. Returns a transducer
+  when no collection is provided."
   ([pred]
    (fn [rf]
      (fn
@@ -233,11 +429,12 @@
    (lazy-seq
     (when-let [s (seq coll)]
       (let [x (first s)]
-        (cons x (if-not (pred x) (take-upto pred (rest s)))))))))
+        (cons x (when-not (pred x) (take-upto pred (rest s)))))))))
 
 (defn drop-upto
   "Returns a lazy sequence of the items in coll starting *after* the first item
-  for which `(pred item)` returns true."
+  for which `(pred item)` returns true. Returns a stateful transducer when no
+  collection is provided."
   ([pred]
    (fn [rf]
      (let [dv (volatile! true)]
@@ -251,9 +448,111 @@
   ([pred coll]
    (rest (drop-while (complement pred) coll))))
 
+(defn partition-between
+  "Applies pred to successive values in coll, splitting it each time `(pred
+  prev-item item)` returns logical true. Returns a lazy seq of partitions.
+  Returns a stateful transducer when no collection is provided."
+  {:added "1.7.0"}
+  ([pred]
+   (fn [rf]
+     (let [part #?(:clj  (java.util.ArrayList.)
+                   :cljr (System.Collections.ArrayList.)
+                   :cljs (array-list))
+           prev (volatile! ::none)]
+       (fn
+         ([] (rf))
+         ([result]
+          (rf (if #?(:cljr    (zero? (.-Count part))
+                     :default (.isEmpty part))
+                result
+                (let [v (vec (#?(:cljr .ToArray :default .toArray) part))]
+                  (#?(:cljr .Clear :default .clear) part)
+                  (unreduced (rf result v))))))
+         ([result input]
+          (let [p @prev]
+            (vreset! prev input)
+            (if (or (#?(:cljs keyword-identical? :default identical?) p ::none)
+                    (not (pred p input)))
+              (do (#?(:cljr .Add :default .add) part input)
+                  result)
+              (let [v (vec (#?(:cljr .ToArray :default .toArray) part))]
+                (#?(:cljr .Clear :default .clear) part)
+                (let [ret (rf result v)]
+                  (when-not (reduced? ret)
+                    (#?(:cljr .Add :default .add) part input))
+                  ret)))))))))
+  ([pred coll]
+   (lazy-seq
+    (letfn [(take-part [prev coll]
+              (lazy-seq
+               (when-let [[x & xs] (seq coll)]
+                 (when-not (pred prev x)
+                   (cons x (take-part x xs))))))]
+      (when-let [[x & xs] (seq coll)]
+        (let [run (take-part x xs)]
+          (cons (cons x run)
+                (partition-between pred
+                                   (lazy-seq (drop (count run) xs))))))))))
+
+(defn partition-after
+  "Returns a lazy sequence of partitions, splitting after `(pred item)` returns
+  true. Returns a stateful transducer when no collection is provided."
+  {:added "1.5.0"}
+  ([pred]
+   (partition-between (fn [x _] (pred x))))
+  ([pred coll]
+   (partition-between (fn [x _] (pred x)) coll)))
+
+(defn partition-before
+  "Returns a lazy sequence of partitions, splitting before `(pred item)` returns
+  true. Returns a stateful transducer when no collection is provided."
+  {:added "1.5.0"}
+  ([pred]
+   (partition-between (fn [_ x] (pred x))))
+  ([pred coll]
+   (partition-between (fn [_ x] (pred x)) coll)))
+
+(defn window
+  "A sliding window, returning a lazy sequence of partitions, containing each
+  element and n-1 preceeding elements, when present. Therefore partitions at the
+  start may contain fewer items than the rest. Returns a stateful transducer
+  when no collection is provided. For a sliding window containing each element
+  and n-1 _following_ elements, use `clojure.core/partition` with a `step` size
+  of 1."
+  {:added "1.9.0"}
+  ([n]
+   (fn [rf]
+     (let [part #?(:clj  (java.util.ArrayList. n)
+                   :cljr (System.Collections.ArrayList.)
+                   :cljs (array))]
+       (fn
+         ([] (rf))
+         ([result] (rf result))
+         ([result x]
+          #?(:clj (.add part x) :cljr (.Add part x) :cljs (.push part x))
+          (when (< n #?(:clj  (.size part)
+                        :cljr (.Count part)
+                        :cljs (.-length part)))
+            #?(:clj  (.remove part 0)
+               :cljr (.RemoveAt part 0)
+               :cljs (.shift part)))
+          (rf result (vec #?(:clj  (.toArray part)
+                             :cljr (.ToArray part)
+                             :cljs (.slice part)))))))))
+  ([n coll]
+   (letfn [(part [part-n coll]
+             (let [run (doall (take part-n coll))]
+               (lazy-seq
+                (when (== part-n (count run))
+                  (cons run
+                        (part (min n (inc part-n))
+                              (if (== n part-n) (rest coll) coll)))))))]
+     (part (min 1 n) coll))))
+
 (defn indexed
   "Returns an ordered, lazy sequence of vectors `[index item]`, where item is a
-  value in coll, and index its position starting from zero."
+  value in coll, and index its position starting from zero. Returns a stateful
+  transducer when no collection is provided."
   ([]
    (fn [rf]
      (let [i (volatile! -1)]
@@ -265,6 +564,75 @@
   ([coll]
    (map-indexed vector coll)))
 
+(defn insert-nth
+  "Returns a lazy sequence of the items in coll, with a new item inserted at
+  the supplied index, followed by all subsequent items of the collection. Runs
+  in O(n) time. Returns a stateful transducer when no collection is provided."
+  {:added "1.2.0"}
+  ([index item]
+   (fn [rf]
+     (let [idx (volatile! (inc index))]
+       (fn
+         ([] (rf))
+         ([result]
+          (if (= @idx 1)
+            (rf (rf result item))
+            (rf result)))
+         ([result x]
+          (if (zero? (vswap! idx dec))
+            (rf (rf result item) x)
+            (rf result x)))))))
+  ([index item coll]
+   (lazy-seq
+    (if (zero? index)
+      (cons item coll)
+      (when (seq coll)
+        (cons (first coll) (insert-nth (dec index) item (rest coll))))))))
+
+(defn remove-nth
+  "Returns a lazy sequence of the items in coll, except for the item at the
+  supplied index. Runs in O(n) time. Returns a stateful transducer when no
+  collection is provided."
+  {:added "1.2.0"}
+  ([index]
+   (fn [rf]
+     (let [idx (volatile! (inc index))]
+       (fn
+         ([] (rf))
+         ([result] (rf result))
+         ([result x]
+          (if (zero? (vswap! idx dec))
+            result
+            (rf result x)))))))
+  ([index coll]
+   (lazy-seq
+    (if (zero? index)
+      (rest coll)
+      (when (seq coll)
+        (cons (first coll) (remove-nth (dec index) (rest coll))))))))
+
+(defn replace-nth
+  "Returns a lazy sequence of the items in coll, with a new item replacing the
+  item at the supplied index. Runs in O(n) time. Returns a stateful transducer
+  when no collection is provided."
+  {:added "1.2.0"}
+  ([index item]
+   (fn [rf]
+     (let [idx (volatile! (inc index))]
+       (fn
+         ([] (rf))
+         ([result] (rf result))
+         ([result x]
+          (if (zero? (vswap! idx dec))
+            (rf result item)
+            (rf result x)))))))
+  ([index item coll]
+   (lazy-seq
+    (if (zero? index)
+      (cons item (rest coll))
+      (when (seq coll)
+        (cons (first coll) (replace-nth (dec index) item (rest coll))))))))
+
 (defn abs
   "Returns the absolute value of a number."
   [x]
@@ -276,14 +644,16 @@
   This function therefore acts like an atomic `deref` then `swap!`."
   {:arglists '([atom f & args])}
   ([atom f]
-   #?(:clj  (loop []
-              (let [value @atom]
-                (if (compare-and-set! atom value (f value))
-                  value
-                  (recur))))
-      :cljs (let [value @atom]
-              (reset! atom (f value))
-              value)))
+   #?(:cljs
+      (let [value @atom]
+        (reset! atom (f value))
+        value)
+      :default
+      (loop []
+        (let [value @atom]
+          (if (compare-and-set! atom value (f value))
+            value
+            (recur))))))
   ([atom f & args]
    (deref-swap! atom #(apply f % args))))
 
@@ -299,26 +669,31 @@
   Clojure as well as ClojureScript."
   [ex]
   #?(:clj  (when (instance? Throwable ex) (.getMessage ^Throwable ex))
+     :cljr (when (instance? Exception ex) (.-Message ^Exception ex))
      :cljs (cljs.core/ex-message ex)))
 
 (defn ex-cause
   "Returns the cause attached to the given ExceptionInfo/Throwable object. For
-  all other types returns nil. Same as `cljs.core/ex-clause` except it works for
+  all other types returns nil. Same as `cljs.core/ex-cause` except it works for
   Clojure as well as ClojureScript."
   [ex]
   #?(:clj  (when (instance? Throwable ex) (.getCause ^Throwable ex))
+     :cljr (when (instance? Exception ex) (.-InnerException ^Exception ex))
      :cljs (cljs.core/ex-cause ex)))
 
 (defn uuid?
   "Returns true if the value is a UUID."
   [x]
-  (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) x))
+  (instance? #?(:clj  java.util.UUID
+                :cljr System.Guid
+                :cljs cljs.core/UUID) x))
 
 (defn uuid
   "Returns a UUID generated from the supplied string. Same as `cljs.core/uuid`
   in ClojureScript, while in Clojure it returns a `java.util.UUID` object."
   [s]
   #?(:clj  (java.util.UUID/fromString s)
+     :cljr (System.Guid. s)
      :cljs (cljs.core/uuid s)))
 
 (defn random-uuid
@@ -326,4 +701,81 @@
   for Clojure as well as ClojureScript."
   []
   #?(:clj  (java.util.UUID/randomUUID)
+     :cljr (System.Guid/NewGuid)
      :cljs (cljs.core/random-uuid)))
+
+(defn regexp?
+  "Returns true if the value is a regular expression."
+  {:added "1.4.0"}
+  [x]
+  (instance? #?(:clj  java.util.regex.Pattern
+                :cljr System.Text.RegularExpressions.Regex
+                :cljs js/RegExp) x))
+
+(defn index-of
+  "Returns the index of the first occurrence of the item in the sequential
+  collection coll, or nil if not found."
+  {:added "1.9.0"}
+  #?(:clj     [^java.util.List coll item]
+     :cljr    [^System.Collections.IEnumerable coll item]
+     :default [coll item])
+  (when (some? coll)
+    (let [index (#?(:cljr .IndexOf :default .indexOf) coll item)]
+      (when-not (neg? index) index))))
+
+(defn find-in
+  "Similar to `clojure.core/find`, except that it finds a key/value pair in an
+  nested associate structure `m`, given a sequence of keys `ks`. See also:
+  `clojure.core/get-in`."
+  {:added "1.9.0"}
+  [m ks]
+  (if (next ks)
+    (-> (get-in m (butlast ks)) (find (last ks)))
+    (find m (first ks))))
+
+(letfn [(first* [s v] (if s (first s) v))]
+  (defn map-padded
+    "Similar to `clojure.core/map`, except that it runs until all colls are
+    exhausted, using `val` as the missing value for each exhausted coll."
+    {:added "1.10.0"}
+    #_{:clj-kondo/ignore [:unused-binding]}
+    ([f val c1] (map f c1))
+    ([f val c1 c2]
+     (lazy-seq
+      (let [s1 (seq c1) s2 (seq c2)]
+        (when (or s1 s2)
+          (cons (f (first* s1 val) (first* s2 val))
+                (map-padded f val (rest s1) (rest s2)))))))
+    ([f val c1 c2 c3]
+     (lazy-seq
+      (let [s1 (seq c1) s2 (seq c2) s3 (seq c3)]
+        (when (or s1 s2 s3)
+          (cons (f (first* s1 val) (first* s2 val) (first* s3 val))
+                (map-padded f val (rest s1) (rest s2) (rest s3)))))))
+    ([f val c1 c2 c3 & colls]
+     (let [step (fn step [cs]
+                  (lazy-seq
+                   (let [ss (mapv seq cs)]
+                     (when (some identity ss)
+                       (cons (apply f (mapv #(first* % val) ss))
+                             (step (mapv rest ss)))))))]
+       (step (conj colls c3 c2 c1))))))
+
+(defn sequence-padded
+  "Similar to `clojure.core/sequence`, except that it runs until all colls are
+  exhausted, using `val` as the missing value for each exhausted coll."
+  {:added "1.10.0"}
+  #_{:clj-kondo/ignore [:unused-binding]}
+  ([xform val c1] (sequence xform c1))
+  ([xform val c1 & colls]
+   (let [f (xform conj)
+         step (fn step [cs]
+                (lazy-seq
+                 (let [ss (mapv seq cs)]
+                   (if (some identity ss)
+                     (let [res (apply f nil (mapv #(if % (first %) val) ss))]
+                       (cond (reduced? res) (f (deref res))
+                             (nil? res) (step (mapv rest ss))
+                             :else (concat res (lazy-seq (step (mapv rest ss))))))
+                     (f nil)))))]
+     (step (conj colls c1)))))


=====================================
test/medley/core_test.cljc
=====================================
@@ -1,7 +1,8 @@
 (ns medley.core-test
-  #?(:clj (:import [clojure.lang ArityException]))
-  (:require #?(:clj  [clojure.test :refer :all]
-               :cljs [cljs.test :refer-macros [deftest is testing]])
+  #?@(:cljs    []
+      :default [(:import [clojure.lang ArityException])])
+  (:require #?(:cljs    [cljs.test :refer-macros [deftest is testing]]
+               :default [clojure.test :refer :all])
             [medley.core :as m]))
 
 (deftest test-find-first
@@ -19,13 +20,45 @@
          {}))
   (is (= (m/dissoc-in {:a {:b {:c 1} :d 2}} [:a :b :c])
          {:a {:d 2}}))
+  (is (= (m/dissoc-in {:a {:b {:c 1} :d 2} :b {:c {:d 2 :e 3}}} [:a :b :c] [:b :c :d])
+         {:a {:d 2} :b {:c {:e 3}}}))
   (is (= (m/dissoc-in {:a 1} [])
          {:a 1})))
 
 (deftest test-assoc-some
   (is (= (m/assoc-some {:a 1} :b 2) {:a 1 :b 2}))
   (is (= (m/assoc-some {:a 1} :b nil) {:a 1}))
-  (is (= (m/assoc-some {:a 1} :b 2 :c nil :d 3) {:a 1 :b 2 :d 3})))
+  (is (= (m/assoc-some {:a 1} :b 2 :c nil :d 3) {:a 1 :b 2 :d 3}))
+  (is (= (m/assoc-some nil :a 1) {:a 1}))
+  (is (= (m/assoc-some nil :a 1 :b 2) {:a 1 :b 2}))
+  (is (nil? (m/assoc-some nil :a nil)))
+  (is (nil? (m/assoc-some nil :a nil :b nil)))
+  (let [input (with-meta {:a 1} {:m 42})]
+    (is (= {:m 42} (meta (m/assoc-some input :b 2 :c nil :d 3))))))
+
+(deftest test-update-existing
+  (is (= (m/update-existing {:a 1} :a inc) {:a 2}))
+  (is (= (m/update-existing {:a 1 :b 2} :a inc) {:a 2 :b 2}))
+  (is (= (m/update-existing {:b 2} :a inc) {:b 2}))
+  (is (= (m/update-existing {:a nil} :a str) {:a ""}))
+  (is (= (m/update-existing {} :a str) {})))
+
+(deftest test-update-existing-in
+  (is (= (m/update-existing-in {:a 1} [:a] inc) {:a 2}))
+  (is (= (m/update-existing-in {:a 1 :b 2} [:a] inc) {:a 2 :b 2}))
+  (is (= (m/update-existing-in {:b 2} [:a] inc) {:b 2}))
+  (is (= (m/update-existing-in {:a nil} [:a] str) {:a ""}))
+  (is (= (m/update-existing-in {} [:a] str) {}))
+  (is (= (m/update-existing-in {:a [:b {:c 42} :d]} [:a 1 :c] inc)
+         {:a [:b {:c 43} :d]}))
+  (is (= (m/update-existing-in {:a [:b {:c 42} :d]} [:a 1 :c] + 7)
+         {:a [:b {:c 49} :d]}))
+  (is (= (m/update-existing-in {:a [:b {:c 42} :d]} [:a 1 :c] + 3 4)
+         {:a [:b {:c 49} :d]}))
+  (is (= (m/update-existing-in {:a [:b {:c 42} :d]} [:a 1 :c] + 3 3 1)
+         {:a [:b {:c 49} :d]}))
+  (is (= (m/update-existing-in {:a [:b {:c 42} :d]} [:a 1 :c] vector 9 10 11 12 13 14)
+         {:a [:b {:c [42 9 10 11 12 13 14]} :d]})))
 
 (deftest test-map-entry
   (is (= (key (m/map-entry :a 1)) :a))
@@ -35,25 +68,62 @@
   (is (= (type (m/map-entry :a 1))
          (type (first {:a 1})))))
 
+(defrecord MyRecord [x])
+
 (deftest test-map-kv
   (is (= (m/map-kv (fn [k v] [(name k) (inc v)]) {:a 1 :b 2})
          {"a" 2 "b" 3}))
   (is (= (m/map-kv (fn [k v] [(name k) (inc v)]) (sorted-map :a 1 :b 2))
          {"a" 2 "b" 3}))
   (is (= (m/map-kv (fn [k v] (m/map-entry (name k) (inc v))) {:a 1 :b 2})
-         {"a" 2 "b" 3})))
+         {"a" 2 "b" 3}))
+  (testing "map-kv with record"
+    (is (= (m/map-kv (fn [k v] (m/map-entry (name k) (inc v))) (->MyRecord 1)) {"x" 2}))))
 
 (deftest test-map-keys
   (is (= (m/map-keys name {:a 1 :b 2})
          {"a" 1 "b" 2}))
   (is (= (m/map-keys name (sorted-map :a 1 :b 2))
-         (sorted-map "a" 1 "b" 2))))
+         (sorted-map "a" 1 "b" 2)))
+  (testing "map-keys with record"
+    (is (= (m/map-keys name (->MyRecord 1)) {"x" 1}))))
 
 (deftest test-map-vals
   (is (= (m/map-vals inc {:a 1 :b 2})
          {:a 2 :b 3}))
   (is (= (m/map-vals inc (sorted-map :a 1 :b 2))
-         (sorted-map :a 2 :b 3))))
+         (sorted-map :a 2 :b 3)))
+  (testing "map-vals with record"
+    (is (= (m/map-vals inc (->MyRecord 1)) {:x 2})))
+  (testing "multiple collections"
+    (is (= (m/map-vals + {:a 1 :b 2 :c 3} {:a 4 :c 5 :d 6})
+           {:a 5, :c 8}))
+    (is (= (m/map-vals min
+                       (sorted-map :z 10 :y 8 :x 4)
+                       {:x 7, :y 14, :z 13}
+                       {:x 11, :y 6, :z 9}
+                       {:x 19, :y 3, :z 2}
+                       {:x 4, :y 0, :z 16}
+                       {:x 17, :y 14, :z 13})
+           (sorted-map :x 4 :y 0 :z 2)))
+    (is (= (m/map-vals #(%1 %2) {:a nil? :b some?} {:b nil})
+           {:b false}))))
+
+(deftest test-map-kv-keys
+  (is (= (m/map-kv-keys + {1 2, 2 4})
+         {3 2, 6 4}))
+  (is (= (m/map-kv-keys + (sorted-map 1 2, 2 4))
+         (sorted-map 3 2, 6 4)))
+  (is (= (m/map-kv-keys str (->MyRecord 1))
+         {":x1" 1})))
+
+(deftest test-map-kv-vals
+  (is (= (m/map-kv-vals + {1 2, 2 4})
+         {1 3, 2 6}))
+  (is (= (m/map-kv-vals + (sorted-map 1 2, 2 4))
+         (sorted-map 1 3, 2 6)))
+  (is (= (m/map-kv-vals str (->MyRecord 1))
+         {:x ":x1"})))
 
 (deftest test-filter-kv
   (is (= (m/filter-kv (fn [k v] (and (keyword? k) (number? v))) {"a" 1 :b 2 :c "d"})
@@ -93,17 +163,17 @@
 
 (deftest test-queue
   (testing "empty"
-    #?(:clj  (is (instance? clojure.lang.PersistentQueue (m/queue)))
-       :cljs (is (instance? cljs.core.PersistentQueue (m/queue))))
+    #?(:cljs    (is (instance? cljs.core.PersistentQueue (m/queue)))
+       :default (is (instance? clojure.lang.PersistentQueue (m/queue))))
     (is (empty? (m/queue))))
   (testing "not empty"
-    #?(:clj  (is (instance? clojure.lang.PersistentQueue (m/queue [1 2 3])))
-       :cljs (is (instance? cljs.core.PersistentQueue (m/queue [1 2 3]))))
+    #?(:cljs    (is (instance? cljs.core.PersistentQueue (m/queue [1 2 3])))
+       :default (is (instance? clojure.lang.PersistentQueue (m/queue [1 2 3]))))
     (is (= (first (m/queue [1 2 3])) 1))))
 
 (deftest test-queue?
-  #?(:clj  (is (m/queue? clojure.lang.PersistentQueue/EMPTY))
-     :cljs (is (m/queue? cljs.core.PersistentQueue.EMPTY)))
+  #?(:cljs    (is (m/queue? cljs.core.PersistentQueue.EMPTY))
+     :default (is (m/queue? clojure.lang.PersistentQueue/EMPTY)))
   (is (not (m/queue? []))))
 
 (deftest test-boolean?
@@ -119,12 +189,49 @@
   (is (= (m/least "a" "b") "a"))
   (is (= (m/least 3 2 5 -1 0 2) -1)))
 
+(deftest test-least-by
+  (is (= (m/least-by :a) nil))
+  (is (= (m/least-by :a {:a 42}) {:a 42}))
+  (is (= (m/least-by :a {:a 3} {:a 1} {:a 2}) {:a 1}))
+  (is (= (m/least-by :a {:a 3 :b 1} {:a 2 :b 2} {:a 2 :b 3}) {:a 2 :b 3}))
+  (is (= (m/least-by last "foo" "baa" "baz" "qux") "baa")))
+
 (deftest test-greatest
   (is (= (m/greatest) nil))
   (is (= (m/greatest "a") "a"))
   (is (= (m/greatest "a" "b") "b"))
   (is (= (m/greatest 3 2 5 -1 0 2) 5)))
 
+(deftest test-greatest-by
+  (is (= (m/greatest-by :a) nil))
+  (is (= (m/greatest-by :a {:a 42}) {:a 42}))
+  (is (= (m/greatest-by :a {:a 3} {:a 1} {:a 2}) {:a 3}))
+  (is (= (m/greatest-by :a {:a 2 :b 1} {:a 3 :b 2} {:a 3 :b 3}) {:a 3 :b 3}))
+  (is (= (m/greatest-by last "foo" "baa" "baz" "qux") "baz")))
+
+(deftest test-join
+  (is (= (m/join [[1 2] []  [3] [4 5 6]]) [1 2 3 4 5 6]))
+  (is (= (m/join (sorted-map :x 1, :y 2, :z 3)) [:x 1 :y 2 :z 3]))
+  (let [a (atom 0)
+        s (m/join (iterate #(do (swap! a inc) (range (inc (count %)))) ()))]
+    (is (= (first s) 0))
+    (is (= @a 1))
+    (is (= (second s) 0))
+    (is (= @a 2))))
+
+(deftest test-deep-merge
+  (is (= (m/deep-merge) nil))
+  (is (= (m/deep-merge {:a 1}) {:a 1}))
+  (is (= (m/deep-merge {:a 1} nil) {:a 1}))
+  (is (= (m/deep-merge {:a 1} {:a 2 :b 3}) {:a 2 :b 3}))
+  (is (= (m/deep-merge {:a {:b 1 :c 2}} {:a {:b 2 :d 3}}) {:a {:b 2 :c 2 :d 3}}))
+  (is (= (m/deep-merge {:a {:b 1}} {:a 1}) {:a 1}))
+  (is (= (m/deep-merge {:a 1} {:b 2} {:b 3 :c 4}) {:a 1 :b 3 :c 4}))
+  (is (= (m/deep-merge {:a {:b {:c {:d 1}}}} {:a {:b {:c {:e 2}}}}) {:a {:b {:c {:d 1 :e 2}}}}))
+  (is (= (m/deep-merge {:a {:b [1 2]}} {:a {:b [3 4]}}) {:a {:b [3 4]}}))
+  (is (= (m/deep-merge (->MyRecord 1) {:x 2}) (->MyRecord 2)))
+  (is (= (m/deep-merge {:a (->MyRecord 1)} {:a {:x 2 :y 3}}) {:a (map->MyRecord {:x 2 :y 3})})))
+
 (deftest test-mapply
   (letfn [(foo [& {:keys [bar]}] bar)]
     (is (= (m/mapply foo {}) nil))
@@ -135,9 +242,31 @@
     (is (= (m/mapply foo 0 {:baz 1}) [0 1]))
     (is (= (m/mapply foo 0 {:spam 1}) [0 nil]))
     (is (= (m/mapply foo 0 nil) [0 nil]))
-    #?@(:clj  [(is (thrown? ArityException (m/mapply foo {})))
-               (is (thrown? IllegalArgumentException (m/mapply foo 0)))]
-        :cljs [(is (thrown? js/Error (m/mapply foo 0)))])))
+    (is (thrown? #?(:clj  IllegalArgumentException
+                    :cljr ArgumentException
+                    :cljs js/Error) (m/mapply foo 0)))
+    #?(:cljs    (is (= (m/mapply foo {}) [nil nil]))
+       :default (is (thrown? ArityException (m/mapply foo {}))))))
+
+(deftest test-collate-by
+  (is (= (m/collate-by identity conj vector [1 2 2 3 3])
+         {1 [1], 2 [2 2], 3 [3 3]}))
+  (is (= (m/collate-by identity conj hash-set [1 2 2 3 3])
+         {1 #{1}, 2 #{2}, 3 #{3}}))
+  (is (= (m/collate-by first conj list ["foo" "bar" "baz"])
+         {\f '("foo"), \b '("baz" "bar")}))
+  (is (= (m/collate-by first (fn [_ x] x) ["foo" "bar" "baz"])
+         {\f "foo", \b "baz"}))
+  (is (= (m/collate-by even? + [1 2 3 4 5 6])
+         {true 12, false 9}))
+  (is (= (m/collate-by first (fn [_ x] x) []) {}))
+  (is (= (m/collate-by first conj vector []) {})))
+
+(deftest test-index-by
+  (is (= (m/index-by identity [1 2 3]) {1 1, 2 2, 3 3}))
+  (is (= (m/index-by inc [1 2 3]) {2 1, 3 2, 4 3}))
+  (is (= (m/index-by first ["foo" "bar" "baz"]) {\f "foo", \b "baz"}))
+  (is (= (m/index-by first []) {})))
 
 (deftest test-interleave-all
   (is (= (m/interleave-all []) []))
@@ -209,6 +338,91 @@
     (is (= (into [] (m/drop-upto zero?) [0 1 2 3 4 5 6]) [1 2 3 4 5 6]))
     (is (= (into [] (m/drop-upto zero?) [1 2 3 4 5 6 7]) []))))
 
+(deftest test-partition-after
+  (testing "sequences"
+    (is (= (m/partition-after identity nil) '()))
+    (is (= (m/partition-after even? [1 3 5 6 8 9 10 11]) '((1 3 5 6) (8) (9 10) (11))))
+    (is (= (m/partition-after even? [2 4 6 8 10]) '((2) (4) (6) (8) (10))))
+    (is (= (m/partition-after even? [1 3 5 7 9]) '((1 3 5 7 9)))))
+
+  (testing "transducers"
+    (is (= (transduce (m/partition-after identity) conj nil) []))
+    (is (= (transduce (m/partition-after even?) conj [1 3 5 6 8 9 10 11])
+           [[1 3 5 6] [8] [9 10] [11]]))
+    (is (= (sequence (m/partition-after even?) [2 4 6 8 10]) '([2] [4] [6] [8] [10])))
+    (is (= (into [] (m/partition-after even?) [1 3 5 7 9]) [[1 3 5 7 9]]))
+    (is (= (transduce (m/partition-after even?)
+                      (completing (fn [coll x]
+                                    (cond-> (conj coll x) (= [8] x) reduced)))
+                      []
+                      [1 3 5 6 8 9])
+           [[1 3 5 6] [8]]))))
+
+(deftest test-partition-before
+  (testing "sequences"
+    (is (= (m/partition-before identity nil) '()))
+    (is (= (m/partition-before even? [1 3 5 6 8 9 10 11]) '((1 3 5) (6) (8 9) (10 11))))
+    (is (= (m/partition-before even? [2 4 6 8 10]) '((2) (4) (6) (8) (10))))
+    (is (= (m/partition-before even? [1 3 5 7 9]) '((1 3 5 7 9)))))
+
+  (testing "transducers"
+    (is (= (transduce (m/partition-before identity) conj nil) []))
+    (is (= (transduce (m/partition-before even?) conj [1 3 5 6 8 9 10 11])
+           [[1 3 5] [6] [8 9] [10 11]]))
+    (is (= (sequence (m/partition-before even?) [2 4 6 8 10]) '([2] [4] [6] [8] [10])))
+    (is (= (into [] (m/partition-before even?) [1 3 5 7 9]) [[1 3 5 7 9]]))
+    (is (= (transduce (m/partition-before even?)
+                      (completing (fn [coll x]
+                                    (cond-> (conj coll x) (= [6] x) reduced)))
+                      []
+                      [1 3 5 6 8 9])
+           [[1 3 5] [6]]))))
+
+(deftest test-partition-between
+  (testing "sequences"
+    (is (= (m/partition-between = nil) '()))
+    (is (= (m/partition-between < [1]) '((1))))
+    (is (= (m/partition-between < [1 1 2 2 1 1 3 3 3])
+           '((1 1) (2 2 1 1) (3 3 3))))
+    (is (= (m/partition-between < [1 2 3 4]) '((1) (2) (3) (4))))
+    (is (= (m/partition-between < [4 3 2 1]) '((4 3 2 1)))))
+
+  (testing "transducers"
+    (is (= (transduce (m/partition-between <) conj nil) []))
+    (is (= (transduce (m/partition-between <) conj [1]) [[1]]))
+    (is (= (transduce (m/partition-between <) conj [1 1 2 2 1 1 3 3 3])
+           [[1 1] [2 2 1 1] [3 3 3]]))
+    (is (= (sequence (m/partition-between <) [1 2 3 4]) '([1] [2] [3] [4])))
+    (is (= (into [] (m/partition-between <) [4 3 2 1]) [[4 3 2 1]]))
+    (is (= (transduce (m/partition-between <)
+                      (completing (fn [coll x]
+                                    (cond-> (conj coll x) (= [8] x) reduced)))
+                      []
+                      [1 2 3 2 8 9])
+           [[1] [2] [3 2] [8]]))))
+
+(deftest test-window
+  (testing "sequences"
+    (is (= (m/window 2 nil) '()))
+    (is (= (m/window 2 [:a]) '((:a))))
+    (is (= (m/window 2 [:a :b]) '((:a) (:a :b))))
+    (is (= (m/window 2 [:a :b :c]) '((:a) (:a :b) (:b :c))))
+    (is (= (take 3 (m/window 0 [:a :b :c])) '(() () ())))
+    (is (= (m/window 10 [:a :b :c]) '((:a) (:a :b) (:a :b :c)))))
+  (testing "transducers"
+    (is (= (transduce (m/window 2) conj nil) []))
+    (is (= (transduce (m/window 2) conj [:a]) [[:a]]))
+    (is (= (transduce (m/window 2) conj [:a :b]) [[:a] [:a :b]]))
+    (is (= (transduce (m/window 2) conj [:a :b :c]) [[:a] [:a :b] [:b :c]]))
+    (is (= (transduce (m/window 0) conj [:a :b :c]) [[] [] []]))
+    (is (= (transduce (m/window 10) conj [:a :b :c]) [[:a] [:a :b] [:a :b :c]]))
+    (is (= (transduce (m/window 2)
+                      (completing (fn [coll x]
+                                    (cond-> (conj coll x) (= [:a :b] x) reduced)))
+                      []
+                      [:a :b :c :d])
+           [[:a] [:a :b]]))))
+
 (deftest test-indexed
   (testing "sequences"
     (is (= (m/indexed [:a :b :c :d])
@@ -222,15 +436,51 @@
     (is (= (into [] (m/indexed) [])
            []))))
 
+(deftest test-insert-nth
+  (testing "sequences"
+    (is (= (m/insert-nth 0 :a [1 2 3 4]) [:a 1 2 3 4]))
+    (is (= (m/insert-nth 1 :a [1 2 3 4]) [1 :a 2 3 4]))
+    (is (= (m/insert-nth 3 :a [1 2 3 4]) [1 2 3 :a 4]))
+    (is (= (m/insert-nth 4 :a [1 2 3 4]) [1 2 3 4 :a])))
+
+  (testing "transducers"
+    (is (= (into [] (m/insert-nth 0 :a) [1 2 3 4]) [:a 1 2 3 4]))
+    (is (= (into [] (m/insert-nth 1 :a) [1 2 3 4]) [1 :a 2 3 4]))
+    (is (= (into [] (m/insert-nth 3 :a) [1 2 3 4]) [1 2 3 :a 4]))
+    (is (= (into [] (m/insert-nth 4 :a) [1 2 3 4]) [1 2 3 4 :a]))))
+
+(deftest test-remove-nth
+  (testing "sequences"
+    (is (= (m/remove-nth 0 [1 2 3 4]) [2 3 4]))
+    (is (= (m/remove-nth 1 [1 2 3 4]) [1 3 4]))
+    (is (= (m/remove-nth 3 [1 2 3 4]) [1 2 3])))
+
+  (testing "transducers"
+    (is (= (into [] (m/remove-nth 0) [1 2 3 4]) [2 3 4]))
+    (is (= (into [] (m/remove-nth 1) [1 2 3 4]) [1 3 4]))
+    (is (= (into [] (m/remove-nth 3) [1 2 3 4]) [1 2 3]))))
+
+(deftest test-replace-nth
+  (testing "sequences"
+    (is (= (m/replace-nth 0 :a [1 2 3 4]) [:a 2 3 4]))
+    (is (= (m/replace-nth 1 :a [1 2 3 4]) [1 :a 3 4]))
+    (is (= (m/replace-nth 3 :a [1 2 3 4]) [1 2 3 :a])))
+
+  (testing "transducers"
+    (is (= (into [] (m/replace-nth 0 :a) [1 2 3 4]) [:a 2 3 4]))
+    (is (= (into [] (m/replace-nth 1 :a) [1 2 3 4]) [1 :a 3 4]))
+    (is (= (into [] (m/replace-nth 3 :a) [1 2 3 4]) [1 2 3 :a]))))
+
 (deftest test-abs
   (is (= (m/abs -3) 3))
   (is (= (m/abs 2) 2))
   (is (= (m/abs -2.1) 2.1))
   (is (= (m/abs 1.8) 1.8))
-  #?@(:clj [(is (= (m/abs -1/3) 1/3))
-            (is (= (m/abs 1/2) 1/2))
-            (is (= (m/abs 3N) 3N))
-            (is (= (m/abs -4N) 4N))]))
+  #?@(:cljs    []
+      :default [(is (= (m/abs -1/3) 1/3))
+                (is (= (m/abs 1/2) 1/2))
+                (is (= (m/abs 3N) 3N))
+                (is (= (m/abs -4N) 4N))]))
 
 (deftest test-deref-swap!
   (let [a (atom 0)]
@@ -248,12 +498,14 @@
 
 (deftest test-ex-message
   (is (= (m/ex-message (ex-info "foo" {})) "foo"))
-  (is (= (m/ex-message (new #?(:clj Exception :cljs js/Error) "bar")) "bar")))
+  (let [ex (new #?(:cljs js/Error :default Exception) "bar")]
+    (is (= (m/ex-message ex) "bar"))))
 
 (deftest test-ex-cause
-  (let [cause (new #?(:clj Exception :cljs js/Error) "foo")]
+  (let [cause (new #?(:cljs js/Error :default Exception) "foo")]
     (is (= (m/ex-cause (ex-info "foo" {} cause)) cause))
-    #?(:clj (is (= (m/ex-cause (Exception. "foo" cause)) cause)))))
+    #?@(:cljs    []
+        :default [(is (= (m/ex-cause (Exception. "foo" cause)) cause))])))
 
 (deftest test-uuid?
   (let [x #uuid "d1a4adfa-d9cf-4aa5-9f05-a15365d1bfa6"]
@@ -264,12 +516,111 @@
 
 (deftest test-uuid
   (let [x (m/uuid "d1a4adfa-d9cf-4aa5-9f05-a15365d1bfa6")]
-    (is (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) x))
+    (is (instance? #?(:clj  java.util.UUID
+                      :cljr System.Guid
+                      :cljs cljs.core.UUID) x))
     (is (= x #uuid "d1a4adfa-d9cf-4aa5-9f05-a15365d1bfa6"))))
 
 (deftest test-random-uuid
   (let [x (m/random-uuid)
         y (m/random-uuid)]
-    (is (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) x))
-    (is (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) y))
+    (is (instance? #?(:clj  java.util.UUID
+                      :cljr System.Guid
+                      :cljs cljs.core.UUID) x))
+    (is (instance? #?(:clj  java.util.UUID
+                      :cljr System.Guid
+                      :cljs cljs.core.UUID) y))
     (is (not= x y))))
+
+(deftest test-regexp?
+  (is (m/regexp? #"x"))
+  (is (not (m/regexp? "x")))
+  (is (not (m/regexp? nil))))
+
+(deftest test-index-of
+  (is (nil? (m/index-of nil :a)))
+  (is (nil? (m/index-of [] :a)))
+  (is (nil? (m/index-of '() :a)))
+
+  (is (nil? (m/index-of  [:a] :b)))
+  (is (nil? (m/index-of '(:a) :b)))
+
+  (is (= 1 (m/index-of  [:a :b :c :d] :b)))
+  (is (= 2 (m/index-of '(:a :b :c :d) :c)))
+
+  (is (= 1 (m/index-of (range 0 10) 1)))
+  (is (= 1 (m/index-of (map str [:a :b]) ":b"))))
+
+(deftest test-find-in
+  (is (nil? (m/find-in {} [:a])))
+  (is (nil? (m/find-in {} [:a :b])))
+  (is (nil? (m/find-in {:a {}} [:a :b])))
+  (is (= [:a 1] (m/find-in {:a 1} [:a])))
+  (is (= [:b 2] (m/find-in {:a {:b 2}} [:a :b])))
+  (is (= [:b {:c 3}] (m/find-in {:a {:b {:c 3}}} [:a :b])))
+  (is (= [:c 3] (m/find-in {:a {:b {:c 3}}} [:a :b :c]))))
+
+(deftest test-map-padded
+  (is (= (map + (range 3) (range 4) (range 5) (range 10))
+         [0 4 8]
+         (take 3 (m/map-padded + 0 (range 3) (range 4) (range 5) (range 10)))))
+  (is (= [0 4 8 19 28 35 36 37 38 39]
+         (m/map-padded + 10 (range 3) (range 4) (range 5) (range 10))))
+  (is (= ()
+         (m/map-padded + 10 () () ())))
+  (testing "laziness"
+    (let [state (volatile! [])]
+      (is (= [0 4 8 19 28]
+             (take 5 (m/map-padded (fn [a b c d]
+                                     (vswap! state conj [a b c d])
+                                     (+ a b c d))
+                                   10
+                                   (range 3) (range 4) (range 5) (range 10)))))
+      (is (= [[0 0 0 0] [1 1 1 1] [2 2 2 2] [10 3 3 3] [10 10 4 4]]
+             @state))))
+  (testing "handles sequences with nils"
+    (is (= [[nil 0 0 0] [nil 1 1 1] [nil 2 2 2]
+            [:missing 3 3 3]
+            [:missing :missing 4 4]
+            [:missing :missing :missing 5]]
+           (take 6 (m/map-padded vector :missing [nil nil nil] (range 4) (range 5) (range 10)))))))
+
+(deftest test-sequence-padded
+  (is (= (map + (range 3) (range 4) (range 5) (range 10))
+         [0 4 8]
+         (take 3 (m/sequence-padded (map +) 0 (range 3) (range 4) (range 5) (range 10)))))
+  (is (= [0 4 8 19 28 35 36 37 38 39]
+         (m/sequence-padded (map +) 10 (range 3) (range 4) (range 5) (range 10))))
+  (is (= ()
+         (m/sequence-padded (map +) 10 () () ())))
+  (testing "laziness"
+    (let [state (volatile! [])]
+      (is (= [0 4 8 19 28]
+             (take 5 (m/sequence-padded
+                      (map (fn [a b c d]
+                             (vswap! state conj [a b c d])
+                             (+ a b c d)))
+                      10
+                      (range 3) (range 4) (range 5) (range 10)))))
+      (is (= [[0 0 0 0] [1 1 1 1] [2 2 2 2] [10 3 3 3] [10 10 4 4]]
+             @state))))
+  (testing "handles sequences with nils"
+    (is (= [[nil 0 0 0] [nil 1 1 1] [nil 2 2 2]
+            [:missing 3 3 3]
+            [:missing :missing 4 4]
+            [:missing :missing :missing 5]]
+           (take 6 (m/sequence-padded (map vector) :missing [nil nil nil] (range 4) (range 5) (range 10))))))
+  (testing "slightly more complex xform"
+    (let [xf
+          (comp
+           (map vector)
+           (drop 5)
+           (filter (comp even? second))
+           (map #(mapv inc %))
+           (take 5))]
+      (is (= [[101 19 507] [101 25 509] [101 31 511] [101 37 513] [101 43 515]]
+             (m/sequence-padded xf
+                                100
+                                (range 5)
+                                (range 0 50 3)
+                                (range 500 1000)))))))



View it on GitLab: https://salsa.debian.org/clojure-team/medley-clojure/-/commit/9f623c853d3e9dd5ab4e65210382ee241d9390c1

-- 
View it on GitLab: https://salsa.debian.org/clojure-team/medley-clojure/-/commit/9f623c853d3e9dd5ab4e65210382ee241d9390c1
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20260730/101d4066/attachment.htm>


More information about the pkg-java-commits mailing list