[Git][clojure-team/prismatic-plumbing-clojure][upstream] New upstream version 0.6.0
Jérôme Charaoui (@lavamind)
gitlab at salsa.debian.org
Fri Jul 15 01:46:00 BST 2022
Jérôme Charaoui pushed to branch upstream at Debian Clojure Maintainers / prismatic-plumbing-clojure
Commits:
ded0d134 by Jérôme Charaoui at 2022-07-14T20:06:13-04:00
New upstream version 0.6.0
- - - - -
22 changed files:
- + .github/workflows/test.yml
- .gitignore
- CHANGELOG.md
- README.md
- + deps.edn
- project.clj
- src/plumbing/core.cljx → src/plumbing/core.cljc
- src/plumbing/fnk/pfnk.cljx → src/plumbing/fnk/pfnk.cljc
- src/plumbing/fnk/schema.cljx → src/plumbing/fnk/schema.cljc
- src/plumbing/graph.cljx → src/plumbing/graph.cljc
- src/plumbing/graph/positional.clj
- src/plumbing/graph_async.cljx → src/plumbing/graph_async.cljc
- src/plumbing/map.cljx → src/plumbing/map.cljc
- test/plumbing/core_test.cljx → test/plumbing/core_test.cljc
- test/plumbing/fnk/fnk_examples_test.cljx → test/plumbing/fnk/fnk_examples_test.cljc
- test/plumbing/fnk/pfnk_test.cljx → test/plumbing/fnk/pfnk_test.cljc
- test/plumbing/fnk/schema_test.cljx → test/plumbing/fnk/schema_test.cljc
- test/plumbing/graph_async_test.cljx → test/plumbing/graph_async_test.cljc
- test/plumbing/graph_examples_test.cljx → test/plumbing/graph_examples_test.cljc
- test/plumbing/graph_test.cljx → test/plumbing/graph_test.cljc
- test/plumbing/map_test.cljx → test/plumbing/map_test.cljc
- + test/plumbing/test_runner.cljs
Changes:
=====================================
.github/workflows/test.yml
=====================================
@@ -0,0 +1,44 @@
+name: Test
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ schedule:
+ # monthly
+ - cron: "0 0 1 * *"
+
+env:
+ #bump to clear caches
+ ACTION_CACHE_VERSION: 'v1'
+
+jobs:
+ test:
+ strategy:
+ matrix:
+ java: ['8', '11', '17']
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout at v2
+ - uses: actions/cache at v2
+ with:
+ path: ~/.m2/repository
+ key: ${{ env.ACTION_CACHE_VERSION }}-${{ runner.os }}-maven-${{ hashFiles('**/project.clj') }}-${{ matrix.java }}
+ restore-keys: |
+ ${{ env.ACTION_CACHE_VERSION }}-${{ runner.os }}-maven-${{ hashFiles('**/project.clj') }}-
+ - name: Prepare java
+ uses: actions/setup-java at v2
+ with:
+ distribution: 'temurin'
+ java-version: ${{ matrix.java }}
+ - uses: actions/setup-node at v2
+ with:
+ node-version: '10.13.0'
+ - name: Install clojure tools
+ uses: DeLaGuardo/setup-clojure at 3.5
+ with:
+ lein: 2.9.7
+ - name: Run tests
+ run: lein all test
=====================================
.gitignore
=====================================
@@ -8,4 +8,5 @@ target/**
.repl
out
.lein-repl-history
-/doc/
\ No newline at end of file
+/doc/
+.cpcache/
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,8 @@
+## 0.6.0
+ * **BREAKING** Minimum supported Clojure version is now 1.8
+ * migrate cljx => cljc
+ * Fix #138: Compile graphs bigger than 100 nodes with interpreted mode in Clojure
+
## 0.5.5
* Bump schema dependency to avoid issues with Clojure 1.9 out of the box.
=====================================
README.md
=====================================
@@ -127,7 +127,7 @@ We can also have higher-order functions on Graphs to wrap the behavior on each s
@(::profile-data (profiled-stats {:xs (range 10000)})))
```
-… and so on. For more examples and details about Graph, check out the [graph examples test](https://github.com/plumatic/plumbing/blob/master/test/plumbing/graph_examples_test.cljx).
+… and so on. For more examples and details about Graph, check out the [graph examples test](https://github.com/plumatic/plumbing/blob/master/test/plumbing/graph_examples_test.cljc).
<a name="fnk"/>
@@ -226,7 +226,7 @@ Ever wanted to conditionally do steps in a `->>` or `->`? Now you can with our
[1 3 5 7 9])
```
-Check out [`plumbing.core`](https://github.com/plumatic/plumbing/blob/master/src/plumbing/core.cljx) for many other useful functions.
+Check out [`plumbing.core`](https://github.com/plumatic/plumbing/blob/master/src/plumbing/core.cljc) for many other useful functions.
## ClojureScript
@@ -262,7 +262,7 @@ Plumbing now has a [mailing list](https://groups.google.com/forum/#!forum/prisma
## Supported Clojure versions
-Plumbing is currently supported on Clojure 1.5.x and 1.6.x.
+Plumbing is currently supported on Clojure 1.8 or later, and the latest ClojureScript version.
## License
=====================================
deps.edn
=====================================
@@ -0,0 +1,3 @@
+{:paths ["src"]
+ :deps {prismatic/schema {:mvn/version "1.2.0"}
+ de.kotka/lazymap {:mvn/version "3.1.0"}}}
=====================================
project.clj
=====================================
@@ -1,74 +1,46 @@
-(defproject prismatic/plumbing "0.5.5"
+(defproject prismatic/plumbing "0.6.0"
:description "Prismatic's Clojure utility belt."
:url "https://github.com/plumatic/plumbing"
:license {:name "Eclipse Public License - v 1.0"
:url "http://www.eclipse.org/legal/epl-v10.html"
:distribution :repo}
- :dependencies [[prismatic/schema "1.1.7"]
+ :dependencies [[prismatic/schema "1.2.0"]
[de.kotka/lazymap "3.1.0" :exclusions [org.clojure/clojure]]]
- :profiles {:dev {:dependencies [[org.clojure/clojure "1.6.0"]
- [org.clojure/clojurescript "0.0-2665"]
- [org.clojure/core.async "0.1.346.0-17112a-alpha"]]
- :plugins [[com.keminglabs/cljx "0.6.0" :exclusions [org.clojure/clojure]]
- [codox "0.8.8"]
- [lein-cljsbuild "1.0.5"]
- [com.cemerick/clojurescript.test "0.3.1"]]
- :cljx {:builds [{:source-paths ["src"]
- :output-path "target/generated/src/clj"
- :rules :clj}
- {:source-paths ["src"]
- :output-path "target/generated/src/cljs"
- :rules :cljs}
- {:source-paths ["test"]
- :output-path "target/generated/test/clj"
- :rules :clj}
- {:source-paths ["test"]
- :output-path "target/generated/test/cljs"
- :rules :cljs}]}}
- :1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]}
- :1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]}
- :1.8 {:dependencies [[org.clojure/clojure "1.8.0-RC1"]]}}
-
- :jar-exclusions [#"\.cljx"]
- :aliases {"all" ["with-profile" "dev:dev,1.5:dev,1.7;dev,1.8"]
- "deploy" ["do" "clean," "cljx" "once," "deploy" "clojars"]
- "test" ["do" "clean," "cljx" "once," "test," "with-profile" "dev" "cljsbuild" "test"]}
+ :profiles {:dev {:dependencies [[org.clojure/clojure "1.10.3"]
+ [org.clojure/clojurescript "1.10.891"]
+ [org.clojure/core.async "1.4.627"]]
+ :plugins [[codox "0.10.8"]
+ [lein-cljsbuild "1.1.8"]
+ [lein-doo "0.1.10"]]}
+ :1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}
+ :1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]}
+ :1.11 {:dependencies [[org.clojure/clojure "1.11.0-master-SNAPSHOT"]]
+ :repositories [["sonatype-oss-public" {:url "https://oss.sonatype.org/content/groups/public"}]]}}
+
+ :aliases {"all" ["with-profile" "+1.8:+1.9:+dev:+1.11"]
+ "deploy" ["do" "deploy" "clojars"]
+ "test" ["do" "test," "doo" "node" "test" "once"]}
:lein-release {:deploy-via :shell
:shell ["lein" "deploy"]}
- :auto-clean false
-
- :source-paths ["target/generated/src/clj" "src"]
-
- :resource-paths ["target/generated/src/cljs"]
+ :source-paths ["src"]
+ :test-paths ["test"]
- :test-paths ["target/generated/test/clj" "test"]
-
- :cljsbuild {:test-commands {"unit" ["phantomjs" :runner
- "this.literal_js_was_evaluated=true"
- "target/unit-test.js"]}
- :builds
- {:dev {:source-paths ["src"
- "target/generated/src/clj"
- "target/generated/src/cljs"]
+ :cljsbuild {:builds
+ {:dev {:source-paths ["src"]
:compiler {:output-to "target/main.js"
:optimizations :whitespace
:pretty-print true}}
- :test {:source-paths ["src"
- "target/generated/src/clj"
- "target/generated/src/cljs"
- "target/generated/test/clj"
- "target/generated/test/cljs"]
+ :test {:source-paths ["src" "test"]
:compiler {:output-to "target/unit-test.js"
- :optimizations :whitespace
-
+ :main plumbing.test-runner
+ :target :nodejs
:pretty-print true}}}}
- :codox {:src-uri-mapping {#"target/generated/src/clj" #(str "src/" % "x")}
- :src-dir-uri "http://github.com/plumatic/plumbing/blob/master/"
+ :codox {:src-dir-uri "http://github.com/plumatic/plumbing/blob/master/"
:src-linenum-anchor-prefix "L"}
:jvm-opts ^:replace [])
=====================================
src/plumbing/core.cljx → src/plumbing/core.cljc
=====================================
@@ -1,17 +1,17 @@
(ns plumbing.core
"Utility belt for Clojure in the wild"
(:refer-clojure :exclude [update])
- #+cljs
+ #?(:cljs
(:require-macros
- [plumbing.core :refer [for-map lazy-get -unless-update]]
- [schema.macros :as schema-macros])
+ [plumbing.core :refer [for-map lazy-get]]
+ [schema.macros :as schema-macros]))
(:require
[schema.utils :as schema-utils]
- #+clj [schema.macros :as schema-macros]
- [plumbing.fnk.schema :as schema :include-macros true]
- #+clj [plumbing.fnk.impl :as fnk-impl]))
+ #?(:clj [schema.macros :as schema-macros])
+ [plumbing.fnk.schema :as schema #?@(:cljs [:include-macros true])]
+ #?(:clj [plumbing.fnk.impl :as fnk-impl])))
-#+clj (set! *warn-on-reflection* true)
+#?(:clj (set! *warn-on-reflection* true))
(def ^:private +none+
"A sentinel value representing missing portions of the input data."
@@ -20,6 +20,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Maps
+#?(:clj
(defmacro for-map
"Like 'for' for building maps. Same bindings except the body should have a
key-expression and value-expression. If a key is repeated, the last
@@ -37,33 +38,7 @@
(doseq ~seq-exprs
(let [~m-sym @m-atom#]
(reset! m-atom# (assoc! ~m-sym ~key-expr ~val-expr))))
- (persistent! @m-atom#))))
-
-(defmacro -unless-update
- "Execute and yield body only if Clojure version preceeds introduction
- of 'update' into core namespace."
- [body]
- `(schema-macros/if-cljs
- ~body
- ~(when (pos? (compare
- [1 7 0]
- (mapv #(get *clojure-version* %)
- [:major :minor :incremental])))
- body)))
-
-(-unless-update
- (defn update
- "Updates the value in map m at k with the function f.
-
- Like update-in, but for updating a single top-level key.
- Any additional args will be passed to f after the value.
-
- WARNING As of Clojure 1.7 this function exists in clojure.core and
- will not be exported by this namespace."
- ([m k f] (assoc m k (f (get m k))))
- ([m k f x1] (assoc m k (f (get m k) x1)))
- ([m k f x1 x2] (assoc m k (f (get m k) x1 x2)))
- ([m k f x1 x2 & xs] (assoc m k (apply f (get m k) x1 x2 xs)))))
+ (persistent! @m-atom#)))))
(defn map-vals
"Build map k -> (f v) for [k v] in map, preserving the initial type"
@@ -121,12 +96,13 @@
:else
x))
+#?(:clj
(defmacro lazy-get
"Like get but lazy about default"
[m k d]
`(if-let [pair# (find ~m ~k)]
(val pair#)
- ~d))
+ ~d)))
(defn safe-get
"Like get but throw an exception if not found"
@@ -220,7 +196,7 @@
[f s]
(keep-indexed (fn [i x] (when (f x) i)) s))
-#+clj
+#?(:clj
(defn frequencies-fast
"Like clojure.core/frequencies, but faster.
Uses Java's equal/hash, so may produce incorrect results if
@@ -229,16 +205,16 @@
(let [res (java.util.HashMap.)]
(doseq [x xs]
(.put res x (unchecked-inc (int (or (.get res x) 0)))))
- (into {} res)))
+ (into {} res))))
-#+clj
+#?(:clj
(defn distinct-fast
"Like clojure.core/distinct, but faster.
Uses Java's equal/hash, so may produce incorrect results if
given values that are = but not .equal"
[xs]
(let [s (java.util.HashSet.)]
- (filter #(when-not (.contains s %) (.add s %) true) xs)))
+ (filter #(when-not (.contains s %) (.add s %) true) xs))))
(defn distinct-by
"Returns elements of xs which return unique
@@ -252,14 +228,14 @@
(do (swap! s conj id)
x))))
-#+clj
+#?(:clj
(defn distinct-id
"Like distinct but uses reference rather than value identity, very clojurey"
[xs]
(let [s (java.util.IdentityHashMap.)]
(doseq [x xs]
(.put s x true))
- (iterator-seq (.iterator (.keySet s)))))
+ (iterator-seq (.iterator (.keySet s))))))
(defn interleave-all
"Analogy: partition:partition-all :: interleave:interleave-all"
@@ -298,30 +274,35 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Control flow
+#?(:clj
(defmacro ?>>
"Conditional double-arrow operation (->> nums (?>> inc-all? (map inc)))"
[do-it? & args]
`(if ~do-it?
(->> ~(last args) ~@(butlast args))
- ~(last args)))
+ ~(last args))))
+#?(:clj
(defmacro ?>
"Conditional single-arrow operation (-> m (?> add-kv? (assoc :k :v)))"
[arg do-it? & rest]
`(if ~do-it?
(-> ~arg ~@rest)
- ~arg))
+ ~arg)))
+#?(:clj
(defmacro fn->
"Equivalent to `(fn [x] (-> x ~@body))"
[& body]
- `(fn [x#] (-> x# ~@body)))
+ `(fn [x#] (-> x# ~@body))))
+#?(:clj
(defmacro fn->>
"Equivalent to `(fn [x] (->> x ~@body))"
[& body]
- `(fn [x#] (->> x# ~@body)))
+ `(fn [x#] (->> x# ~@body))))
+#?(:clj
(defmacro <-
"Converts a ->> to a ->
@@ -330,13 +311,15 @@
Jason W01fe is happy to give a talk anywhere any time on
the calculus of arrow macros"
[& body]
- `(-> ~(last body) ~@(butlast body)))
+ `(-> ~(last body) ~@(butlast body))))
+#?(:clj
(defmacro as->>
"Like as->, but can be used in double arrow."
[name & forms-and-expr]
- `(as-> ~(last forms-and-expr) ~name ~@(butlast forms-and-expr)))
+ `(as-> ~(last forms-and-expr) ~name ~@(butlast forms-and-expr))))
+#?(:clj
(defmacro memoized-fn
"Like fn, but memoized (including recursive calls).
@@ -352,7 +335,7 @@
v#
(let [v# (do ~@body)]
(swap! a# assoc args# v#)
- v#))))))
+ v#)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Miscellaneous
@@ -375,8 +358,8 @@
(first (swap-pair! a (constantly new-val))))
(defn millis ^long []
- #+clj (System/currentTimeMillis)
- #+cljs (.getTime (js/Date.)))
+ #?(:clj (System/currentTimeMillis)
+ :cljs (.getTime (js/Date.))))
(defn mapply
"Like apply, but applies a map to a function with positional map
@@ -387,6 +370,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; fnk
+#?(:clj
(defmacro letk
"Keyword let. Accepts an interleaved sequence of binding forms and map forms like:
(letk [[a {b 2} [:f g h] c d {e 4} :as m & more] a-map ...] & body)
@@ -423,8 +407,9 @@
cur-body-form)]
`(let [~map-sym ~value-form] ~body-form))))
`(do ~@body)
- (reverse (partition 2 bindings))))
+ (reverse (partition 2 bindings)))))
+#?(:clj
(defmacro if-letk
"bindings => binding-form test
@@ -440,15 +425,17 @@
(if temp#
(letk [~form temp#]
~then)
- ~else)))))
+ ~else))))))
+#?(:clj
(defmacro when-letk
"bindings => binding-form test
When test is true, evaluates body with binding-form bound to the value of test"
[bindings & body]
- `(if-letk ~bindings (do ~@body)))
+ `(if-letk ~bindings (do ~@body))))
+#?(:clj
(defmacro fnk
"Keyword fn, using letk. Generates a prismatic/schema schematized fn that
accepts a single explicit map i.e., (f {:foo :bar}).
@@ -469,8 +456,9 @@
(schema-macros/extract-arrow-schematized-element &env args)
[nil args])
[bind body] (schema-macros/extract-arrow-schematized-element &env more-args)]
- (fnk-impl/fnk-form &env name? bind body &form)))
+ (fnk-impl/fnk-form &env name? bind body &form))))
+#?(:clj
(defmacro defnk
"Analogy: fn:fnk :: defn::defnk"
[& defnk-args]
@@ -482,6 +470,6 @@
(schema/assert-iae (symbol? name) "Name for defnk is not a symbol: %s" name)
(let [f (fnk-impl/fnk-form &env name bind body &form)]
`(def ~(with-meta name (merge (meta name) (assoc-when (or attr-map? {}) :doc docstring?)))
- ~f))))
+ ~f)))))
-#+clj (set! *warn-on-reflection* false)
+#?(:clj (set! *warn-on-reflection* false))
=====================================
src/plumbing/fnk/pfnk.cljx → src/plumbing/fnk/pfnk.cljc
=====================================
@@ -5,10 +5,10 @@
using fn->fnk, or using custom binding syntax (of which 'fnk' et al
are one possible example)."
(:require
- [schema.core :as s :include-macros true]
- [plumbing.fnk.schema :as schema :include-macros true]))
+ [schema.core :as s #?@(:cljs [:include-macros true])]
+ [plumbing.fnk.schema :as schema #?@(:cljs [:include-macros true])]))
-#+clj (set! *warn-on-reflection* true)
+#?(:clj (set! *warn-on-reflection* true))
(defprotocol PFnk
"Protocol for keyword functions and their specifications, e.g., fnks and graphs."
@@ -27,7 +27,8 @@
(defn output [^schema.core.FnSchema s]
(.-output-schema s))
-(extend-type #+clj clojure.lang.Fn #+cljs object
+(extend-type #?(:clj clojure.lang.Fn
+ :cljs object)
PFnk
(io-schemata [this]
(assert (fn? this))
@@ -53,4 +54,4 @@
[f]
(:name (meta f)))
-#+clj (set! *warn-on-reflection* false)
+#?(:clj (set! *warn-on-reflection* false))
=====================================
src/plumbing/fnk/schema.cljx → src/plumbing/fnk/schema.cljc
=====================================
@@ -11,13 +11,13 @@
required, or provided via `instance`, and will thus deliberately drop extra key
schemas on inputs as appropriate. Output schemas may not have optional keys."
(:require
- [schema.core :as s :include-macros true]
+ [schema.core :as s #?@(:cljs [:include-macros true])]
[schema.utils :as schema-utils]
- #+clj [schema.macros :as schema-macros])
- #+cljs
+ #?(:clj [schema.macros :as schema-macros]))
+ #?(:cljs
(:require-macros
- #+cljs [schema.macros :as schema-macros]
- [plumbing.fnk.schema :refer [assert-iae]]))
+ [schema.macros :as schema-macros]
+ [plumbing.fnk.schema :refer [assert-iae]])))
(def Schema (s/protocol s/Schema))
(def InputSchema {(s/cond-pre (s/eq s/Keyword) schema.core.OptionalKey s/Keyword) Schema})
@@ -30,11 +30,12 @@
;;; Helpers
+#?(:clj
(defmacro assert-iae
"Like assert, but throws a RuntimeException in Clojure (not an AssertionError),
and also takes args to format."
[form & format-args]
- `(schema-macros/assert! ~form ~@format-args))
+ `(schema-macros/assert! ~form ~@format-args)))
(defn assert-distinct
"Like (assert (distinct? things)) but with a more helpful error message."
@@ -70,9 +71,9 @@
nil)
(defn map-schema? [m]
- #+clj (instance? clojure.lang.APersistentMap m)
- #+cljs (or (instance? cljs.core.PersistentArrayMap m)
- (instance? cljs.core.PersistentHashMap m)))
+ #?(:clj (instance? clojure.lang.APersistentMap m)
+ :cljs (or (instance? cljs.core.PersistentArrayMap m)
+ (instance? cljs.core.PersistentHashMap m))))
;;; Input schemata
=====================================
src/plumbing/graph.cljx → src/plumbing/graph.cljc
=====================================
@@ -22,16 +22,16 @@
For more details and examples of Graphs, see test/plumbing/graph_examples_test.cljx."
(:refer-clojure :exclude [compile])
(:require
- #+clj [lazymap.core :as lazymap]
+ #?(:clj [lazymap.core :as lazymap])
[schema.core :as s]
- #+clj [schema.macros :as schema-macros]
- [plumbing.fnk.schema :as schema :include-macros true]
+ #?(:clj [schema.macros :as schema-macros])
+ [plumbing.fnk.schema :as schema #?@(:cljs [:include-macros true])]
[plumbing.fnk.pfnk :as pfnk]
- #+clj [plumbing.fnk.impl :as fnk-impl]
- #+clj [plumbing.graph.positional :as graph-positional]
- [plumbing.core :as plumbing :include-macros true]
+ #?(:clj [plumbing.fnk.impl :as fnk-impl])
+ #?(:clj [plumbing.graph.positional :as graph-positional])
+ [plumbing.core :as plumbing #?@(:cljs [:include-macros true])]
[plumbing.map :as map])
- #+cljs (:require-macros [schema.macros :as schema-macros]))
+ #?(:cljs (:require-macros [schema.macros :as schema-macros])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -68,11 +68,11 @@
(apply working-array-map))]
(assert (every? keyword? (keys graph)))
(with-meta graph
- {::io-schemata (update-in (reduce schema/sequence-schemata
- [{} {}]
- (for [[k node] graph]
- [k (pfnk/io-schemata node)]))
- [0] assoc s/Keyword s/Any)
+ {::io-schemata (update (reduce schema/sequence-schemata
+ [{} {}]
+ (for [[k node] graph]
+ [k (pfnk/io-schemata node)]))
+ 0 assoc s/Keyword s/Any)
::self graph}))))
;; Any Clojure map can be treated as a graph directly, without calling ->graph
@@ -81,10 +81,10 @@
(plumbing/safe-get (meta (->graph g)) ::io-schemata))
(extend-protocol pfnk/PFnk
- #+clj clojure.lang.IPersistentMap
- #+cljs cljs.core.PersistentArrayMap
+ #?(:clj clojure.lang.IPersistentMap
+ :cljs cljs.core.PersistentArrayMap)
(io-schemata [g] (io-schemata* g))
- #+cljs cljs.core.PersistentHashMap
+ #?(:cljs cljs.core.PersistentHashMap)
(io-schemata [g] (io-schemata* g)))
(defn- split-nodes [s]
@@ -125,28 +125,55 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Compiling and running graphs
-#+clj
+#?(:clj (declare interpreted-eager-compile))
+#?(:clj
(defn eager-compile
"Compile graph specification g to a corresponding fnk that is optimized for
speed. Wherever possible, fnks are called positionally, to reduce the
overhead of creating and destructuring maps, and the return value is a
record, which is much faster to create and access than a map. Compilation
- is relatively slow, however, due to internal calls to 'eval'."
- [g]
- (if (fn? g)
- g
- (let [g (for [[k sub-g] (->graph g)]
- [k (eager-compile sub-g)])]
- (graph-positional/positional-flat-compile (->graph g)))))
-
-#+clj
+ is relatively slow, however, due to internal calls to 'eval'.
+
+ Options:
+ :positional-limit is used to decide when to switch to interpreted mode,
+ which does not compile positionally. If positional compilation is required,
+ use option {:positional-limit ##Inf} (at the risk of method-too-large errors)."
+ ([g] (eager-compile g {}))
+ ([g {:keys [positional-limit]
+ :or {positional-limit graph-positional/max-graph-size}
+ :as _opts}]
+ (let [eager-compile (fn eager-compile [g]
+ (if (fn? g)
+ g
+ (let [g* (for [[k sub-g] (->graph g)]
+ [k (eager-compile sub-g)])]
+ (when (every? second g*)
+ (let [g (->graph g*)]
+ (when (<= (-> g pfnk/output-schema count)
+ positional-limit)
+ (graph-positional/positional-flat-compile g)))))))]
+ (or (eager-compile g)
+ (interpreted-eager-compile g))))))
+
+#?(:clj
(defn positional-eager-compile
"Like eager-compile, but produce a non-keyword function that can be called
with args in the order provided by arg-ks, avoiding the overhead of creating
and destructuring a top-level map. This can yield a substantially faster
- fn for Graphs with very computationally inexpensive node fnks."
+ fn for Graphs with very computationally inexpensive node fnks.
+
+ Warning: if any level of g exceeds `graph-positional/max-graph-size`, compilation
+ may fail. Do not use for arbitrarily large graphs."
[g arg-ks]
- (fnk-impl/positional-fn (eager-compile g) arg-ks))
+ (fnk-impl/positional-fn
+ (eager-compile g
+ ;; there is no interpreted mode (yet) for positional compilation,
+ ;; but it is required by `positional-fn`. this forces eager-compile to
+ ;; always return a positional graph, even though compilation may fail
+ ;; due to code size. when available, should be replaced with a scalable
+ ;; positional compilation.
+ {:positional-limit Double/POSITIVE_INFINITY})
+ arg-ks)))
(defn simple-flat-compile
"Helper method for simple (non-nested) graph compilations that convert a graph
@@ -202,7 +229,7 @@
(fn [m] m)
(fn [m k f] (assoc m k (restricted-call f m)))))
-#+clj
+#?(:clj
(defn lazy-compile
"Compile graph specification g to a corresponding fnk that returns a
lazymap of the node result fns on a given input. This fnk returns
@@ -216,9 +243,9 @@
g
false
(fn [m] (reduce-kv assoc (lazymap/lazy-hash-map) m)) ;; into is extremely slow on lazymaps.
- (fn [m k f] (lazymap/delay-assoc m k (delay (restricted-call f m))))))
+ (fn [m k f] (lazymap/delay-assoc m k (delay (restricted-call f m)))))))
-#+clj ;; TODO: move out.
+#?(:clj ;; TODO: move out.
(defn par-compile
"Experimental. Launches one future per node at startup; we probably woudln't
use this in production, and will release more sophisticated parallel
@@ -235,7 +262,7 @@
g
true
(fn [m] (into (lazymap/lazy-hash-map) m))
- (fn [m k f] (lazymap/delay-assoc m k (future (restricted-call f m))))))
+ (fn [m k f] (lazymap/delay-assoc m k (future (restricted-call f m)))))))
(defn compile
"Compile graph specification g to a corresponding fnk using the a default
@@ -243,8 +270,8 @@
Clojure: eager-compile
ClojureScript: interpreted-eager-compile"
[g]
- #+clj (eager-compile g)
- #+cljs (interpreted-eager-compile g))
+ #?(:clj (eager-compile g)
+ :cljs (interpreted-eager-compile g)))
(defn run
"Eagerly run a graph on an input by compiling and then executing on this input."
@@ -300,6 +327,7 @@
node-fn))
g)))))
+#?(:clj
(defmacro instance
"Experimental.
@@ -312,7 +340,7 @@
{:z 10}))"
([g m] `(instance ~g [] ~m))
([g bind m]
- `(comp-partial ~g (plumbing/fnk ~bind ~m))))
+ `(comp-partial ~g (plumbing/fnk ~bind ~m)))))
(defn profiled
"Modify graph spec g, producing a new graph spec with a new top-level key
@@ -326,11 +354,12 @@
(pfnk/fn->fnk
(fn [m]
(let [pm (plumbing/safe-get m profile-key)
- start #+clj (System/nanoTime) #+cljs (plumbing/millis)
+ start #?(:clj (System/nanoTime)
+ :cljs (plumbing/millis))
res (f (dissoc m profile-key))]
(swap! pm assoc-in ks
- #+clj (/ (- (System/nanoTime) start) 1000000.0)
- #+cljs (- (plumbing/millis) start))
+ #?(:clj (/ (- (System/nanoTime) start) 1000000.0)
+ :cljs (- (plumbing/millis) start)))
res))
[(assoc (pfnk/input-schema f)
profile-key s/Any)
=====================================
src/plumbing/graph/positional.clj
=====================================
@@ -1,5 +1,6 @@
(ns plumbing.graph.positional
- "A compilation method for graphs that avoids maps for speed."
+ "A compilation method for graphs that avoids maps for speed.
+ Prone to failure for graphs with more nodes than `max-graph-size`."
(:use plumbing.core)
(:require
[schema.core :as s]
@@ -9,6 +10,15 @@
(:import
clojure.lang.IFn))
+(def max-graph-size
+ "The positional compilation algorithm provided by this namespace
+ reliably succeeds only with graphs with `max-graph-size` or less nodes..
+
+ The basic strategy is to generate a defrecord field for each node
+ (of which there is a limit of around 120) and then generate a constructor
+ function (whose code size grows linearly in the number of nodes)."
+ 100)
+
(defn def-graph-record
"Define a record for the output of a graph. It is usable as a function to be
as close to a map as possible. Return the typename."
=====================================
src/plumbing/graph_async.cljx → src/plumbing/graph_async.cljc
=====================================
@@ -1,16 +1,16 @@
(ns plumbing.graph-async
- #+cljs
+ #?(:cljs
(:require-macros
- [cljs.core.async.macros :refer [go]])
+ [cljs.core.async.macros :refer [go]]))
(:require
- #+clj [clojure.core.async :as async :refer [go <! >!]]
- #+cljs [cljs.core.async :as async :refer [<! >!]]
- #+clj [clojure.core.async.impl.protocols :as async-protocols]
- #+cljs [cljs.core.async.impl.protocols :as async-protocols]
+ #?(:clj [clojure.core.async :as async :refer [go <! >!]]
+ :cljs [cljs.core.async :as async :refer [<! >!]])
+ #?(:clj [clojure.core.async.impl.protocols :as async-protocols]
+ :cljs [cljs.core.async.impl.protocols :as async-protocols])
[plumbing.fnk.pfnk :as pfnk]
- [plumbing.fnk.schema :as schema :include-macros true]
- [plumbing.core :as plumbing :include-macros true]
- [plumbing.graph :as graph :include-macros true]))
+ [plumbing.fnk.schema :as schema #?@(:cljs [:include-macros true])]
+ [plumbing.core :as plumbing #?@(:cljs [:include-macros true])]
+ [plumbing.graph :as graph #?@(:cljs [:include-macros true])]))
(defn asyncify
"Take a fnk f and return an async version by wrapping non-channel
@@ -73,7 +73,7 @@
(swap! results assoc k r)
(doseq [c (child-map k)]
(when (empty? (c (swap! remaining-parents
- update-in [c]
+ update c
disj k)))
(run-node c)))))))]
(doseq [k (keys g)]
=====================================
src/plumbing/map.cljx → src/plumbing/map.cljc
=====================================
@@ -2,9 +2,8 @@
"Common operations on maps (both Clojure immutable and mutable Java stuff)"
(:refer-clojure :exclude [flatten])
(:require
- [plumbing.core :as plumbing :include-macros true]
- [plumbing.fnk.schema :as schema :include-macros true]
- #+cljs [clojure.set :as set]))
+ [plumbing.core :as plumbing #?@(:cljs [:include-macros true])]
+ [plumbing.fnk.schema :as schema #?@(:cljs [:include-macros true])]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -117,7 +116,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Java mutable Maps
-#+clj
+#?(:clj
(do
(defn update-key!
"Transform value in java.util.Map m under key k with fn f."
@@ -169,12 +168,12 @@
(let [m (java.util.HashMap.)]
(doseq [[ks v] nested-counts]
(inc-key-in! m ks v))
- m)))
+ m))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Ops on graphs represented as maps.
-#+clj
+#?(:clj
(defn topological-sort
"Take an adjacency list representation of a graph (a map from node names to
sequences of child node names), and return a topological ordering of the node
@@ -202,9 +201,9 @@
r (.remove re c)]
(when (.containsKey re r)
(throw (IllegalArgumentException. (format "Graph contains a cycle containing %s and %s" c r)))))
- candidate)))
+ candidate))))
-#+cljs
+#?(:cljs
(defn topological-sort
[child-map & [include-leaves?]]
(let [e (atom child-map)
@@ -228,4 +227,4 @@
r rs]
(when (find @re r)
(throw (ex-info (str "Graph contains a cycle containing " c " and " r) {:nodes [c r]}))))
- candidate)))
+ candidate))))
=====================================
test/plumbing/core_test.cljx → test/plumbing/core_test.cljc
=====================================
@@ -4,15 +4,15 @@
[schema.test :as schema-test]
[plumbing.core :as p :include-macros true]
[plumbing.fnk.pfnk :as pfnk]
- #+clj [plumbing.fnk.impl :as fnk-impl]
- #+clj [clojure.test :refer :all]
- #+cljs [cemerick.cljs.test :refer-macros [is are deftest testing use-fixtures]]))
+ #?(:clj [plumbing.fnk.impl :as fnk-impl])
+ #?(:clj [clojure.test :refer :all]
+ :cljs [cljs.test :refer-macros [is are deftest testing use-fixtures]])))
-#+cljs
+#?(:cljs
(do
(def Exception js/Error)
(def AssertionError js/Error)
- (def Throwable js/Error))
+ (def Throwable js/Error)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Maps
@@ -33,21 +33,6 @@
(is (= (count m) 1000))
(is (= (m 999) 499500))))
-(p/-unless-update
- (deftest update-test
- (testing "0 extra args"
- (is (= {:a 5, :b 0}
- (p/update {:a 4, :b 0} :a inc)))
- (is (= {:a 1}
- (p/update {} :a (fnil inc 0)))))
- (testing "1 extra arg"
- (is (= {:a 42, :b 0}
- (p/update {:a 6, :b 0} :a * 7)))
- (is (= {:a #{42}}
- (p/update {} :a (fnil conj #{}) 42))))
- (testing "100 extra args"
- (is (= {:a 4951} (apply p/update {:a 1} :a + (range 100)))))))
-
(deftest map-vals-test
(is (= (p/map-vals inc {:a 0 :b 0})
{:a 1 :b 1}))
@@ -164,7 +149,7 @@
(is (= [0 1 2] (p/positions odd? [1 3 5 2 4 6])))
(is (= [1 3 5] (take 3 (p/positions odd? (range))))))
-#+clj
+#?(:clj
(deftest frequencies-fast-test
(is (= {\p 2, \s 4, \i 4, \m 1}
(p/frequencies-fast "mississippi")))
@@ -173,23 +158,23 @@
;; We don't return the right thing on = but not .equals things,
;; because of the difference between Java Maps and Clojure maps.
(is (= {1 1}
- (p/frequencies-fast [1 (BigInteger. "1")]))))
-#+clj
+ (p/frequencies-fast [1 (BigInteger. "1")])))))
+#?(:clj
(deftest distinct-fast-test
(is (= [1 2 3]
(p/distinct-fast [1 2 3])))
(is (= [1 2 3]
(p/distinct-fast [1 2 3 2 1 2 3 2 2])))
(is (= []
- (p/distinct-fast []))))
+ (p/distinct-fast [])))))
-#+clj
+#?(:clj
(defn are-fast-things-faster []
(let [s (apply concat (repeat 100 (range 10000)))]
(doseq [f [frequencies p/frequencies-fast distinct p/distinct-fast]]
(println f)
(dotimes [_ 5]
- (time (doall (f s)))))))
+ (time (doall (f s))))))))
(deftest distinct-by-test
(is (= [{:id 1 :data "a"}]
@@ -210,13 +195,13 @@
[1 3]
[3 1]])))))
-#+clj
+#?(:clj
(deftest distinct-id-test
(let [x (p/distinct-id [:a :b :c :a :b (Long. 1) (Long. 1)])]
(is (= 5 (count x)))
(is (= #{:a :b :c 1} (set x)))
(is (= #{:a :b :c 1} (set x)))
- (is (empty? (p/distinct-id nil)))))
+ (is (empty? (p/distinct-id nil))))))
(deftest interleave-all-test
(is (= [:a 0 :b 1 :c :d] (p/interleave-all [:a :b :c :d] [0 1]))))
@@ -364,12 +349,12 @@
(deftest letk-dont-require-map-for-nested-only-as
(is (= 1 (p/letk [[[:a :as a]] {:a 1}] a))))
-#+clj
+#?(:clj
(deftest letk-no-multiple-binding-test
(is (thrown? Exception (eval '(p/letk [[a a] {:a 1}] a))))
(is (thrown? Exception (eval '(p/letk [[a/a b/a] {:a/a 1 :b/a 2}] a))))
(is (= 1 (p/letk [[a] {:a 1} [a] {:a a}] a)))
- (is (= 1 (p/letk [[a/b] {:a/b 1} [a/b] {:a/b b}] b))))
+ (is (= 1 (p/letk [[a/b] {:a/b 1} [a/b] {:a/b b}] b)))))
(deftest letk-multi-shadow-test
(let [a 1 b 2 c 3 e 4 e 5
@@ -424,7 +409,7 @@
(is (= [1 2 3]
((p/fnk [a {b (* a 2)} {c (inc b)}] [a b c]) {:a 1}))))
- #+clj
+ #?(:clj
(testing "positional-fn"
(let [f (p/fnk [a {b 2} [:c :as c0] [:d d1 {d2 2} [:d3 :as d30] [:d4 d41 :as d4]]]
(is (= [a b c0 d1 d2 d30 d41 d4]
@@ -434,7 +419,7 @@
((fnk-impl/positional-fn f [:d :a :c])
{:d1 4 :d3 17 :d4 {:d41 18 :d42 :foo}} 4 3)
(is (= @call-count 4))
- (is (thrown? Throwable ((p/fnk [a] a) {:b 3}))))))
+ (is (thrown? Throwable ((p/fnk [a] a) {:b 3})))))))
(testing "fnk output-schema"
(doseq [f [(p/fnk [] {:a 1 :b {:b1 2}})
@@ -559,11 +544,11 @@
(is (thrown? Throwable (keyfn-test-docstring :wheres :mycar))))
;; Test that type hints are properly propagated for fnk and defnk.
-#+clj
+#?(:clj
(p/defnk ^Byte a-typehinted-defnk [^Long l]
- (.byteValue l))
+ (.byteValue l)))
-#+clj
+#?(:clj
(deftest type-hints-test
(is (= Byte (:tag (meta #'a-typehinted-defnk))))
(doseq [f [a-typehinted-defnk
@@ -571,9 +556,9 @@
(p/fnk [{^Long l 1}] (.byteValue l))
(p/fnk [^Long l & m] (.byteValue l))]]
(is (= (Byte. (byte 1)) (f {:l (Long. 1)})))
- (is (thrown? Exception (f {:l (Integer. 1)})))))
+ (is (thrown? Exception (f {:l (Integer. 1)}))))))
-#+clj
+#?(:clj
(deftest ^:slow repeated-bindings-test
(is (thrown? Exception (eval '(p/fnk [x [:x y]] (+ x y)))))
(is (thrown? Exception (eval '(p/fnk [{x {:y 1}} [:x y]] (+ x y)))))
@@ -582,7 +567,7 @@
(is (thrown? Exception (eval '(p/fnk [{x {:y 1}} x] (+ x y)))))
(is (thrown? Exception (eval '(p/fnk [x [:x y] :as m] (+ x y)))))
(is (thrown? Exception (eval '(p/fnk [{x {:y 1}} [:x y] :as m] (+ x y)))))
- (is (thrown? Exception (eval '(p/fnk [{x {:y 1}} x :as m] (+ x y))))))
+ (is (thrown? Exception (eval '(p/fnk [{x {:y 1}} x :as m] (+ x y)))))))
(deftest optional-self-shadow-test
(is (= 1 (let [b 1] ((p/fnk [{a b}] a) {}))))
@@ -608,7 +593,8 @@
(is (= 3 ((p/fnk [[:m x]] (+ x (:x m))) {:m {:x 2}})))))
(deftest miliis-test
- (let [now #+clj (System/currentTimeMillis) #+cljs (.getTime (js/Date.))
+ (let [now #?(:clj (System/currentTimeMillis)
+ :cljs (.getTime (js/Date.)))
threshold 5]
(is (> threshold
(- (p/millis) now)))))
=====================================
test/plumbing/fnk/fnk_examples_test.cljx → test/plumbing/fnk/fnk_examples_test.cljc
=====================================
@@ -1,22 +1,22 @@
(ns plumbing.fnk.fnk-examples-test
"Explaining input and output schemata, fnk syntax, and their relationships
by example."
- #+cljs
+ #?(:cljs
(:require-macros
- [cemerick.cljs.test :refer [is deftest testing]])
+ [cljs.test :refer [is deftest testing]]))
(:require
[schema.core :as s]
- [plumbing.core :as p :include-macros true]
+ [plumbing.core :as p #?@(:cljs [:include-macros true])]
[plumbing.fnk.schema :as schema]
[plumbing.fnk.pfnk :as pfnk]
- #+clj [clojure.test :refer :all]
- #+cljs cemerick.cljs.test))
+ #?(:clj [clojure.test :refer :all]
+ :cljs cljs.test)))
-#+cljs
+#?(:cljs
(do
(def Exception js/Error)
(def AssertionError js/Error)
- (def Throwable js/Error))
+ (def Throwable js/Error)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Input and output schemata
@@ -153,12 +153,12 @@
;; You can also provide schema information on the inputs, with
;; validation like schema.core/defn. See (doc fnk) for details.
-#+clj ;; This example uses clj-only annotations, but should otherwise work in cljs
+#?(:clj ;; This example uses clj-only annotations, but should otherwise work in cljs
(p/defnk a-schematized-fnk :- (s/pred odd?)
[a :- long b :- int]
- (+ a b))
+ (+ a b)))
-#+clj
+#?(:clj
(deftest a-schematized-fnk-test
(is (= [{:a long :b int s/Keyword s/Any} (s/pred odd?)]
(pfnk/io-schemata a-schematized-fnk)))
@@ -167,7 +167,7 @@
(s/with-fn-validation
(is (= 3 (a-schematized-fnk {:a 1 :b (int 2)})))
(is (thrown? Exception (a-schematized-fnk {:a 1 :b 2})))
- (is (thrown? Exception (a-schematized-fnk {:a 1 :b (int 1)})))))
+ (is (thrown? Exception (a-schematized-fnk {:a 1 :b (int 1)}))))))
;; fnks also have support for nested bindings, and nested maps
=====================================
test/plumbing/fnk/pfnk_test.cljx → test/plumbing/fnk/pfnk_test.cljc
=====================================
@@ -1,13 +1,13 @@
(ns plumbing.fnk.pfnk-test
- #+cljs
+ #?(:cljs
(:require-macros
- [cemerick.cljs.test :refer [is deftest testing]])
+ [cljs.test :refer [is deftest testing]]))
(:require
[schema.core :as s]
- [plumbing.core :as p :include-macros true]
+ [plumbing.core :as p #?@(:cljs [:include-macros true])]
[plumbing.fnk.pfnk :as pfnk]
- #+clj [clojure.test :refer :all]
- #+cljs cemerick.cljs.test))
+ #?(:clj [clojure.test :refer :all]
+ :cljs cljs.test)))
(deftest meta-round-trip-test
(let [i-schema {:x s/Any}
=====================================
test/plumbing/fnk/schema_test.cljx → test/plumbing/fnk/schema_test.cljc
=====================================
@@ -2,23 +2,23 @@
(:require
[schema.core :as s]
[schema.test :as schema-test]
- [plumbing.core :as p :include-macros true]
+ [plumbing.core :as p #?@(:cljs [:include-macros true])]
[plumbing.fnk.pfnk :as pfnk]
[plumbing.fnk.schema :as fnk-schema]
- #+clj [clojure.test :refer :all]
- #+cljs [cemerick.cljs.test :refer-macros [is are deftest testing use-fixtures]]))
+ #?(:clj [clojure.test :refer :all]
+ :cljs [cljs.test :refer-macros [is are deftest testing use-fixtures]])))
-#+cljs
+#?(:cljs
(do
(def Exception js/Error)
- (def RuntimeException js/Error))
+ (def RuntimeException js/Error)))
-#+clj ;; the expression-munging doesn't play well with cljs.
+#?(:clj ;; the expression-munging doesn't play well with cljs.
(deftest explicit-schema-key-map-test
(is (= {:foo true :bar false :baz false}
(fnk-schema/explicit-schema-key-map
{:foo s/Any (s/optional-key :bar) s/Any s/Keyword s/Keyword
- `(s/optional-key :baz) s/Any}))))
+ `(s/optional-key :baz) s/Any})))))
(deftest split-schema-keys-test
(is (= [[:foo :bar] [:baz :bat]]
@@ -27,8 +27,8 @@
(deftest merge-on-with-test
(is (= {0 5 4 9 9 12}
- (#+clj #'fnk-schema/merge-on-with #+cljs fnk-schema/merge-on-with
- #(quot % 2) min + {1 2 4 9 9 4} {9 8 0 3}))))
+ (#'fnk-schema/merge-on-with
+ #(quot % 2) min + {1 2 4 9 9 4} {9 8 0 3}))))
(deftest union-input-schemata-test
(is (= {:a s/Any}
@@ -130,13 +130,13 @@
:o3 {:x s/Any (s/optional-key :y) s/Any :z s/Any :q {:r s/Any s/Keyword s/Any} s/Keyword s/Any}
s/Keyword s/Any}
(p/fnk [{o1 1} o2 [:o3 x {y 2} z [:q r]]]))
- #+clj
+ #?(:clj
(do
(is (= [1 2] ((eval `(p/fnk [[:x ~'x] [:y ~'y]] [~'x ~'y])) {:x {:x 1} :y {:y 2}})))
(is (thrown? Throwable (eval `(p/fnk [{:y ~'x} {:y ~'y}] [~'x ~'y]))))
(is (thrown? Throwable (eval `(p/fnk [{:x ~'x} {:y ~'x}] [~'x]))))
(is (thrown? Throwable (eval `(p/fnk [[:x ~'x] ~'x] [~'x]))))
- (is (thrown? Throwable (eval `(p/fnk [{~'x 1} ~'x] [~'x]))))))
+ (is (thrown? Throwable (eval `(p/fnk [{~'x 1} ~'x] [~'x])))))))
(deftest fnk-out-schemata-test
=====================================
test/plumbing/graph_async_test.cljx → test/plumbing/graph_async_test.cljc
=====================================
@@ -1,14 +1,14 @@
(ns plumbing.graph-async-test
- #+cljs
+ #?(:cljs
(:require-macros
- [cljs.core.async.macros :refer [go]])
+ [cljs.core.async.macros :refer [go]]))
(:require
- [plumbing.core :as plumbing :include-macros true]
+ [plumbing.core :as plumbing #?@(:cljs [:include-macros true])]
[plumbing.graph-async :as graph-async]
- #+clj [clojure.core.async :as async :refer [go <!]]
- #+cljs [cljs.core.async :as async :refer [<!]]
- #+clj [clojure.test :refer :all]
- #+cljs [cemerick.cljs.test :refer-macros [is deftest testing done]]))
+ #?(:clj [clojure.core.async :as async :refer [go <!]]
+ :cljs [cljs.core.async :as async :refer [<!]])
+ #?(:clj [clojure.test :refer :all]
+ :cljs [cljs.test :refer-macros [is deftest testing]])))
(deftest async-compile-test
(let [c ((graph-async/async-compile
@@ -20,8 +20,9 @@
:e (plumbing/fnk [{d 9}] (+ d 90))})
{:x 1 :y 7})
expected-result {:a {:a1 2 :a2 -9} :b 4 :c -18 :d 49 :e 139}]
- #+clj (is (= expected-result
- (async/<!! c)))
- #+cljs (go
- (is (= expected-result (<! c)))
- (done))))
+ #?(:clj (is (= expected-result
+ (async/<!! c)))
+ :cljs (go
+ (is (= expected-result (<! c)))
+ ;;FIXME what's the equivalent to `cemerick.cljs.test/done`?
+ #_(done)))))
=====================================
test/plumbing/graph_examples_test.cljx → test/plumbing/graph_examples_test.cljc
=====================================
@@ -1,29 +1,29 @@
(ns plumbing.graph-examples-test
(:require
[schema.core :as s]
- [plumbing.core :as p :include-macros true]
- [plumbing.fnk.pfnk :as pfnk :include-macros true]
- [plumbing.graph :as graph :include-macros true]
+ [plumbing.core :as p #?@(:cljs [:include-macros true])]
+ [plumbing.fnk.pfnk :as pfnk #?@(:cljs [:include-macros true])]
+ [plumbing.graph :as graph #?@(:cljs [:include-macros true])]
[plumbing.map :as map]
- #+clj [clojure.test :refer :all]
- #+cljs [cemerick.cljs.test :refer-macros [is deftest testing]]))
+ #?(:clj [clojure.test :refer :all]
+ :cljs [cljs.test :refer-macros [is deftest testing]])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Helpers
-#+cljs
+#?(:cljs
(do
(def Exception js/Error)
(def AssertionError js/Error)
- (def Throwable js/Error))
+ (def Throwable js/Error)))
(defn hash-or-array-map?
"Returns true if x is a PersistentHashMap or PersistentArrayMap, otherwise false"
[x]
- #+clj (or (instance? clojure.lang.PersistentHashMap x)
- (instance? clojure.lang.PersistentArrayMap x))
- #+cljs (or (instance? cljs.core.PersistentHashMap x)
- (instance? cljs.core.PersistentArrayMap x)))
+ #?(:clj (or (instance? clojure.lang.PersistentHashMap x)
+ (instance? clojure.lang.PersistentArrayMap x))
+ :cljs (or (instance? cljs.core.PersistentHashMap x)
+ (instance? cljs.core.PersistentArrayMap x))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Motivation
@@ -137,16 +137,16 @@
;; or parallel-compile it so functions that don't depend on one-another
;; are done in separate threads.
-#+clj
-(def lazy-stats (graph/lazy-compile stats-graph))
+#?(:clj
+(def lazy-stats (graph/lazy-compile stats-graph)))
-#+clj
+#?(:clj
(deftest lazy-stats-test
(let [output (lazy-stats {:xs [1 2 3 6]})]
;; Nothing has actually been computed yet
(is (= (/ 25 2) (:m2 output)))
;; Now :n, :m, and :m2 have been computed, but :v is still behind a delay
- ))
+ )))
;; In cases where only some results from a set of related calculations are
;; needed, this lazy compilation can be very convenient and powerful.
@@ -155,14 +155,14 @@
;; problem, and lets the compiler do the work of figuring out what can be
;; done in parallel.
-#+clj
-(def par-stats (graph/par-compile stats-graph))
+#?(:clj
+(def par-stats (graph/par-compile stats-graph)))
-#+clj
+#?(:clj
(deftest par-stats-test
(let [output (par-stats {:xs [1 2 3 6]})]
;; Nodes are being computed in futures, with :m and :m2 going in parallel
- (is (= (/ 7 2) (:v output)))))
+ (is (= (/ 7 2) (:v output))))))
;; There are also some compilation modes for performance tuning. If you're
;; interested in the details, see below under "Compiling Graphs".
@@ -322,7 +322,7 @@
;; can take the value from the parent as input.
;; when using a positional graph, you need to order your nodes such that
;; the local version is created before it is used.
-#+clj
+#?(:clj
(deftest local-scoping-rules-test
(let [x (p/fnk [a x] (inc (+ a x)))
y (p/fnk [x] (* 2 x))
@@ -341,7 +341,7 @@
5 21)
normalize-output)]
(is (= expected-result graph-result positional-graph-result))
- (is (thrown? RuntimeException (graph/graph :y y :x x :z z)))))
+ (is (thrown? RuntimeException (graph/graph :y y :x x :z z))))))
;; If you're defining a graph explicitly in code, it's rather bad form
;; to put the nodes out of topological order (like the first example
@@ -447,7 +447,7 @@
;; For example, here's a graph with the same shape as 'stats' but
;; where the nodes are slow to compute:
-#+clj
+#?(:clj
(do
(def slow-graph
(graph/graph
@@ -490,7 +490,7 @@
;; lazy computes stuff as needed
(let [par (graph/par-compile slow-graph)
par-out (timed-is 0 keys (par in))]
- (timed-is 450 true? (= out (into {} par-out))))))) ;; :b1 and :b2 are done in parallel
+ (timed-is 450 true? (= out (into {} par-out)))))))) ;; :b1 and :b2 are done in parallel
;; There are also some compilation modes for performance tuning. If you're
;; going to call your graph in an inner loop, where creating and destructuring
@@ -505,14 +505,14 @@
:b2 (p/fnk [a] (- a 3))
:c (p/fnk [b1 b2] (+ b1 b2))))
-#+clj
+#?(:clj
(deftest positional-graph-test
(let [out {:a 4 :b1 8 :b2 1 :c 9}
positional-fast (graph/positional-eager-compile fast-graph [:x])
output (positional-fast 3)]
(testing "output is a record, not a map"
(is (not (hash-or-array-map? output))))
- (is (= out (into {} output)))))
+ (is (= out (into {} output))))))
;; You won't get all the speedup if you have fnks that expect graph parameters,
;; though, such as
@@ -523,7 +523,7 @@
;; It's also worth noting that eager-compile does many of the same
;; optimizations on the inside, so it also returns a record, not a map.
-#+clj
+#?(:clj
(deftest eager-graph-test
(let [out {:a 4 :b1 8 :b2 1 :c 9}
eager-fast (graph/eager-compile fast-graph)
@@ -531,7 +531,7 @@
;; The output is a record, not a map.
(testing "output is a record, not a map"
(is (not (hash-or-array-map? output))))
- (is (= out (into {} output)))))
+ (is (= out (into {} output))))))
;; On the other hand, if you're worried about the computational expense of
;; compiling, you can reduce it from a few tens of milliseconds (usually not a
@@ -612,7 +612,7 @@
;; Under this simple scheme, we can define functions to start up and
;; shutdown a service in just 20 lines of code.
-#+clj
+#?(:clj
(do
(defn resource-transform [g]
(assoc (map/map-leaves
@@ -702,7 +702,7 @@
(shutdown-service svc-map)))
(deftest ^:slow pointless-service-test
- (test-pointless-service pointless-service {:max-age 1})))
+ (test-pointless-service pointless-service {:max-age 1}))))
;; To make this sort of composition useful on a large scale, we also
;; need a way to provide contextual arguments to subgraphs,
=====================================
test/plumbing/graph_test.cljx → test/plumbing/graph_test.cljc
=====================================
@@ -6,15 +6,16 @@
[schema.core :as s]
[schema.test :as schema-test]
[plumbing.fnk.pfnk :as pfnk]
- #+clj [plumbing.fnk.impl :as fnk-impl]
- #+clj [clojure.test :refer :all]
- #+cljs [cemerick.cljs.test :refer-macros [is deftest testing use-fixtures]]))
+ #?@(:clj [[plumbing.fnk.impl :as fnk-impl]
+ [plumbing.graph.positional :as positional]])
+ #?(:clj [clojure.test :refer :all]
+ :cljs [cljs.test :refer-macros [is deftest testing use-fixtures]])))
-#+cljs
+#?(:cljs
(do
(def Exception js/Error)
(def AssertionError js/Error)
- (def Throwable js/Error))
+ (def Throwable js/Error)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -171,7 +172,7 @@
(deftest interpreted-eager-compile-test
(test-eager-compile graph/interpreted-eager-compile identity))
-#+clj
+#?(:clj
(deftest eager-compile-test
;; eager-compile outputs records rather than ordinary maps as outputs.
(test-eager-compile graph/eager-compile (partial walk/prewalk #(if (map? %) (into {} %) %)))
@@ -180,9 +181,44 @@
(is (= [2] (vals o)))
(is (= 2 (o :x) (get o :x) (:x o)))
(is (= {:x 2} (into {} o)))
- (is (not= {:x 2} o))))
-
-#+clj
+ (is (not= {:x 2} o)))))
+
+#?(:clj
+(deftest large-eager-compile-test
+ ;; graphs equal or smaller than positional/max-graph-size, eager-compile returns records
+ (let [size positional/max-graph-size
+ o ((graph/eager-compile
+ (apply
+ graph/graph
+ (mapcat (fn [i]
+ (let [k (keyword (str "x" i))]
+ [k (plumbing/fnk [b a] [k a b])]))
+ (range size))))
+ {:a 1, :b 2})
+ expected-map (into {}
+ (map #(let [k (keyword (str "x" %))]
+ [k [k 1 2]])
+ (range size)))]
+ (is (= [:x0 1 2] (o :x0) (get o :x0) (:x0 o)))
+ (is (= expected-map (into {} o)))
+ (is (not= expected-map o)))
+ ;; above positional/max-graph-size, eager-compile returns ordinary maps
+ (let [size (inc positional/max-graph-size)
+ o ((graph/eager-compile
+ (apply
+ graph/graph
+ (mapcat (fn [i]
+ (let [k (keyword (str "x" i))]
+ [k (plumbing/fnk [b a] [k a b])]))
+ (range size))))
+ {:a 1, :b 2})]
+ (is (= (into {}
+ (map #(let [k (keyword (str "x" %))]
+ [k [k 1 2]])
+ (range size)))
+ o)))))
+
+#?(:clj
(do ;; test defschema with eager-compile -- there were some issues previously
(ns test (:require [schema.core :as s]))
(s/defschema Foo {s/Keyword s/Num})
@@ -194,9 +230,9 @@
(is (= [{:bar test/Foo s/Keyword s/Any}
{:foo s/Any}]
(pfnk/io-schemata f)
- (pfnk/io-schemata g))))))
+ (pfnk/io-schemata g)))))))
-#+clj
+#?(:clj
(deftest positional-eager-compile-test
(let [f (graph/positional-eager-compile
(graph/graph
@@ -206,9 +242,24 @@
(is (= 19 (:x (f 5 3))))
(is (= 11 (:x (f fnk-impl/+none+ 3))))
(is (thrown? Exception (f 1)))
- (is (thrown? Exception (f 3 fnk-impl/+none+)))))
-
-#+clj
+ (is (thrown? Exception (f 3 fnk-impl/+none+))))))
+
+#?(:clj
+(deftest large-positional-eager-compile-test
+ (let [size (inc positional/max-graph-size) ;; make sure :positional-limit is respected
+ fields (vec (repeatedly size gensym))
+ f (graph/positional-eager-compile
+ (apply
+ graph/graph
+ (mapcat (fn [i]
+ (let [k (keyword (str "x" i))]
+ [k (plumbing/fnk [b a] [k a b])]))
+ (range size)))
+ [:b :a])]
+ (is (= [:x0 :asdf 42]
+ (:x0 (f 42 :asdf)))))))
+
+#?(:clj
(deftest lazy-compile-test
(let [a (atom [])
g (graph/graph
@@ -231,7 +282,7 @@
:x (plumbing/fnk [a])
:y (plumbing/fnk [b] (inc b))
:z (plumbing/fnk [y] (inc y))))
- {:b 3})))))
+ {:b 3}))))))
(deftest bind-non-map-with-as-test
(is (= (:y (graph/run (graph/graph :x (plumbing/fnk [] {:a "1"})
@@ -239,17 +290,17 @@
{}))
"1")))
-#+clj
+#?(:clj
(defn chain-graph [n]
(plumbing/for-map [i (range n)]
(keyword (str "x" (inc i)))
(let [p (keyword (str "x" i))]
- (pfnk/fn->fnk (fn [m] (inc (p m))) [{p s/Any} s/Any]))))
+ (pfnk/fn->fnk (fn [m] (inc (p m))) [{p s/Any} s/Any])))))
-#+clj
+#?(:clj
(deftest chain-graph-test
(is (= 100 (:x100 ((graph/eager-compile (chain-graph 100)) {:x0 0}))))
- (is (= 100 (:x100 ((graph/lazy-compile (chain-graph 100)) {:x0 0})))))
+ (is (= 100 (:x100 ((graph/lazy-compile (chain-graph 100)) {:x0 0}))))))
(deftest comp-partial-fn-test
@@ -315,7 +366,7 @@
(is (= {:x 16 :y 26} (select-keys (graph/run inst-o {:z 3}) [:x :y])))))
(is (thrown? Exception (graph/instance raw-g [z] {:a z})))))
-#+clj
+#?(:clj
(deftest ^:slow profiled-test
(let [approx-= (fn [x y] (< (Math/abs (- x y)) 10))
times {:a 100 :b 200 :c 400}
@@ -328,9 +379,9 @@
(is (= (select-keys execed [:a :b :c])
{:a 11 :b -10 :c -110}))
(doseq [[k t] times]
- (is (approx-= t (get @(:profile-stats execed) k))))))
+ (is (approx-= t (get @(:profile-stats execed) k)))))))
-#+cljs
+#?(:cljs
(deftest profiled-test
(let [stats-graph {:n (plumbing/fnk [xs] (count xs))
:m (plumbing/fnk [xs n] (/ (plumbing/sum identity xs) n))
@@ -341,9 +392,9 @@
profile-stats @(::profile-stats output)]
(is (map? profile-stats))
(is (= #{:n :m :m2 :v}
- (set (keys profile-stats))))))
+ (set (keys profile-stats)))))))
-#+clj
+#?(:clj
(defn time-graphs "How slow are big chain graphs?" []
(let [n 1000
g (chain-graph n)
@@ -354,6 +405,6 @@
:lazy (time (graph/lazy-compile g))}]
(println k)
(dotimes [_ 5]
- (println (time (plumbing/sum tk (repeatedly 10 #(f {:x0 1})))))))))
+ (println (time (plumbing/sum tk (repeatedly 10 #(f {:x0 1}))))))))))
(use-fixtures :once schema-test/validate-schemas)
=====================================
test/plumbing/map_test.cljx → test/plumbing/map_test.cljc
=====================================
@@ -4,16 +4,16 @@
[plumbing.core :as plumbing]
[plumbing.map :as map]
[clojure.string :as str]
- #+clj [clojure.test :refer :all]
- #+cljs [cemerick.cljs.test :refer-macros [is deftest testing use-fixtures]])
- #+cljs
- (:require-macros [plumbing.map :as map]))
+ #?(:clj [clojure.test :refer :all]
+ :cljs [cljs.test :refer-macros [is deftest testing use-fixtures]]))
+ #?(:cljs
+ (:require-macros [plumbing.map :as map])))
-#+cljs
+#?(:cljs
(do
(def Exception js/Error)
(def AssertionError js/Error)
- (def Throwable js/Error))
+ (def Throwable js/Error)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Clojure immutable maps
@@ -106,7 +106,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Java mutable Maps
-#+clj
+#?(:clj
(do
(deftest update-key!-test
(let [m (java.util.HashMap. {:a 1 :b 2})]
@@ -154,7 +154,7 @@
(deftest deep-collate-test
(is (= {:a {:b 3.0 :c -1.0} :b 4.0}
- (clojureize (map/deep-collate [[[:a :b] 1.0] [[:a :c] -1.0] [[:a :b] 2.0] [[:b] 4.0]]))))))
+ (clojureize (map/deep-collate [[[:a :b] 1.0] [[:a :c] -1.0] [[:a :b] 2.0] [[:b] 4.0]])))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
=====================================
test/plumbing/test_runner.cljs
=====================================
@@ -0,0 +1,20 @@
+(ns plumbing.test-runner
+ (:require [doo.runner :refer-macros [doo-tests]]
+ plumbing.core-test
+ plumbing.fnk.fnk-examples-test
+ plumbing.fnk.pfnk-test
+ plumbing.fnk.schema-test
+ plumbing.graph-async-test
+ plumbing.graph-examples-test
+ plumbing.graph-test
+ plumbing.map-test))
+
+(doo-tests
+ 'plumbing.core-test
+ 'plumbing.fnk.fnk-examples-test
+ 'plumbing.fnk.pfnk-test
+ 'plumbing.fnk.schema-test
+ 'plumbing.graph-async-test
+ 'plumbing.graph-examples-test
+ 'plumbing.graph-test
+ 'plumbing.map-test)
View it on GitLab: https://salsa.debian.org/clojure-team/prismatic-plumbing-clojure/-/commit/ded0d134d8377befc07f0dfe72c610fdd2c3f6e0
--
View it on GitLab: https://salsa.debian.org/clojure-team/prismatic-plumbing-clojure/-/commit/ded0d134d8377befc07f0dfe72c610fdd2c3f6e0
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20220715/d9e84a04/attachment.htm>
More information about the pkg-java-commits
mailing list