Skip to content

Commit f22d972

Browse files
authored
Basic clj-kondo support, minor doc updates, some deprecations (#66)
- Minor updates to docstrings and ns forms - Ignore kondo cache - Deprecated several utility fns - Clean up test use/requires - Update README - Add kondo info - Clean up code snippets - Add deps.edn coordinate - Basic kondo config - Eliminate transitive lint-as - Upgrade project release 0.4.6 - Upgrade deps - Set min Java version at 8 - Updated bound-fn docstrings with rationale - Minor .gitignore update
1 parent 3e40436 commit f22d972

13 files changed

Lines changed: 189 additions & 56 deletions

File tree

.clj-kondo/config.edn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{:config-paths ["../resources/clj-kondo.exports/potemkin/potemkin/"]}
2+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ push
99
.nrepl*
1010
.idea
1111
*.iml
12+
.cache

README.md

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44

55
Potemkin is a collection of facades and workarounds for things that are more difficult than they should be. All functions are within the `potemkin` namespace.
66

7-
### usage
7+
### Usage
88

9-
```clj
10-
[potemkin "0.4.5"]
9+
##### Leiningen
10+
```clojure
11+
[potemkin "0.4.6"]
12+
```
13+
14+
##### deps.edn
15+
```clojure
16+
potemkin/potemkin {:mvn/version "0.4.6"}
1117
```
1218

1319
### `import-vars`
@@ -18,7 +24,7 @@ The former approach places an onus on the creator of the library; the various or
1824

1925
`import-vars` allows functions, macros, and values to be defined in one namespace, and exposed in another. This means that the structure of your code and the structure of your API can be decoupled.
2026

21-
```clj
27+
```clojure
2228
(import-vars
2329
[clojure.walk
2430
prewalk
@@ -35,7 +41,7 @@ Despite this, there are only six functions which really matter: `get`, `assoc`,
3541

3642
For instance, here's a map which will automatically realize any delays, allowing for lazy evaluation semantics:
3743

38-
```clj
44+
```clojure
3945
(def-map-type LazyMap [m mta]
4046
(get [_ k default-value]
4147
(if (contains? m k)
@@ -60,7 +66,7 @@ For instance, here's a map which will automatically realize any delays, allowing
6066

6167
Often a map is just a view onto another object, especially when dealing with Java APIs. While we can create a function which converts it into an entirely separate object, for both performance and memory reasons it can be useful to create a map which simply acts as a delegate to the underlying objects:
6268

63-
```clj
69+
```clojure
6470
(def-derived-map StringProperties [^String s]
6571
:base s
6672
:lower-case (.toLowerCase s)
@@ -75,7 +81,7 @@ The reason it's so laborious to define a map-like data structure is because the
7581

7682
However, using `def-abstract-type`, we can avoid this:
7783

78-
```clj
84+
```clojure
7985
(def-abstract-type ASeq
8086
(more [this]
8187
(let [n (next this)]
@@ -86,7 +92,7 @@ However, using `def-abstract-type`, we can avoid this:
8692

8793
This abstract type may be used within the body of `deftype+`, which is just like a vanilla `deftype` except for the support for abstract types.
8894

89-
```clj
95+
```clojure
9096
(deftype+ CustomSeq [s]
9197
ASeq
9298
clojure.lang.ISeq
@@ -105,7 +111,7 @@ While `definterface` uses an entirely different convention than `defprotocol`, `
105111

106112
Gensyms enforce hygiene within macros, but when quote syntax is nested, they can become a pain. This, for instance, doesn't work:
107113

108-
```clj
114+
```clojure
109115
`(let [x# 1]
110116
~@(map
111117
(fn [n] `(+ x# ~n))
@@ -114,7 +120,7 @@ Gensyms enforce hygiene within macros, but when quote syntax is nested, they can
114120

115121
Because `x#` is going to expand to a different gensym in the two different contexts. One way to work around this is to explicitly create a gensym ourselves:
116122

117-
```clj
123+
```clojure
118124
(let [x-sym (gensym "x")]
119125
`(let [~x-sym 1]
120126
~@(map
@@ -124,18 +130,14 @@ Because `x#` is going to expand to a different gensym in the two different conte
124130

125131
However, this is pretty tedious, since we may need to define quite a few of these explicit gensym names. Using `unify-gensyms`, however, we can rely on the convention that any var with two hashes at the end should be unified:
126132

127-
```clj
133+
```clojure
128134
(unify-gensyms
129135
`(let [x## 1]
130136
~@(map
131137
(fn [n] `(+ x## ~n))
132138
(range 3)))
133139
```
134140

135-
### `fast-bound-fn` and `fast-memoize`
136-
137-
Variants of Clojure's `bound-fn` and `memoize` which are significantly faster.
138-
139141
### License
140142

141143
Copyright © 2013 Zachary Tellman

project.clj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
(defproject potemkin "0.4.5"
1+
(defproject potemkin "0.4.6"
22
:license {:name "MIT License"}
33
:description "Some useful facades."
44
:dependencies [[clj-tuple "0.2.2"]
55
[riddley "0.1.12"]]
6-
:profiles {:dev {:dependencies [[criterium "0.4.3"]
6+
:profiles {:dev {:dependencies [[criterium "0.4.6"]
77
[collection-check "0.1.6"]]}
8-
:provided {:dependencies [[org.clojure/clojure "1.8.0-RC4"]]}}
8+
:provided {:dependencies [[org.clojure/clojure "1.11.1"]]}}
99
:global-vars {*warn-on-reflection* true}
1010
:test-selectors {:default #(not (some #{:benchmark}
1111
(cons (:tag %) (keys %))))
1212
:benchmark :benchmark
1313
:all (constantly true)}
1414
:java-source-paths ["src"]
15-
:jvm-opts ^:replace ["-server" "-Xmx4g"]
16-
:javac-options ["-source" "1.6" "-target" "1.6"]
15+
:javac-options ["-source" "1.8" "-target" "1.8"]
1716
:repositories {"sonatype-oss-public" "https://oss.sonatype.org/content/groups/public/"})
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{:lint-as {potemkin.collections/compile-if clojure.core/if
2+
potemkin.collections/reify-map-type clojure.core/reify
3+
potemkin.collections/def-map-type clj-kondo.lint-as/def-catch-all
4+
potemkin.collections/def-derived-map clj-kondo.lint-as/def-catch-all
5+
6+
potemkin.types/reify+ clojure.core/reify
7+
potemkin.types/defprotocol+ clojure.core/defprotocol
8+
potemkin.types/deftype+ clojure.core/deftype
9+
potemkin.types/defrecord+ clojure.core/defrecord
10+
potemkin.types/definterface+ clojure.core/defprotocol
11+
potemkin.types/extend-protocol+ clojure.core/extend-protocol
12+
potemkin.types/def-abstract-type clj-kondo.lint-as/def-catch-all
13+
14+
potemkin.utils/doit clojure.core/doseq
15+
potemkin.utils/doary clojure.core/doseq
16+
potemkin.utils/condp-case clojure.core/condp
17+
potemkin.utils/fast-bound-fn clojure.core/bound-fn
18+
19+
potemkin.walk/prewalk clojure.walk/prewalk
20+
potemkin.walk/postwalk clojure.walk/postwalk
21+
potemkin.walk/walk clojure.walk/walk
22+
23+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24+
;;;; top-level from import-vars
25+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26+
27+
;; Have hooks
28+
;;potemkin/import-fn potemkin.namespaces/import-fn
29+
;;potemkin/import-macro potemkin.namespaces/import-macro
30+
;;potemkin/import-def potemkin.namespaces/import-def
31+
32+
;; Internal, not transitive
33+
;;potemkin/unify-gensyms potemkin.macros/unify-gensyms
34+
;;potemkin/normalize-gensyms potemkin.macros/normalize-gensyms
35+
;;potemkin/equivalent? potemkin.macros/equivalent?
36+
37+
potemkin/condp-case clojure.core/condp
38+
potemkin/doit potemkin.utils/doit
39+
potemkin/doary potemkin.utils/doary
40+
41+
potemkin/def-abstract-type clj-kondo.lint-as/def-catch-all
42+
potemkin/reify+ clojure.core/reify
43+
potemkin/defprotocol+ clojure.core/defprotocol
44+
potemkin/deftype+ clojure.core/deftype
45+
potemkin/defrecord+ clojure.core/defrecord
46+
potemkin/definterface+ clojure.core/defprotocol
47+
potemkin/extend-protocol+ clojure.core/extend-protocol
48+
49+
potemkin/reify-map-type clojure.core/reify
50+
potemkin/def-derived-map clj-kondo.lint-as/def-catch-all
51+
potemkin/def-map-type clj-kondo.lint-as/def-catch-all}
52+
53+
;; leave import-vars alone, kondo special-cases it
54+
:hooks {:macroexpand {#_#_potemkin.namespaces/import-vars potemkin.namespaces/import-vars
55+
potemkin.namespaces/import-fn potemkin.namespaces/import-fn
56+
potemkin.namespaces/import-macro potemkin.namespaces/import-macro
57+
potemkin.namespaces/import-def potemkin.namespaces/import-def
58+
59+
#_#_potemkin/import-vars potemkin.namespaces/import-vars
60+
potemkin/import-fn potemkin.namespaces/import-fn
61+
potemkin/import-macro potemkin.namespaces/import-macro
62+
potemkin/import-def potemkin.namespaces/import-def}}}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
(ns potemkin.namespaces
2+
(:require [clj-kondo.hooks-api :as api]))
3+
4+
(defn import-macro*
5+
([sym]
6+
`(def ~(-> sym name symbol) ~sym))
7+
([sym name]
8+
`(def ~name ~sym)))
9+
10+
(defmacro import-fn
11+
([sym]
12+
(import-macro* sym))
13+
([sym name]
14+
(import-macro* sym name)))
15+
16+
(defmacro import-macro
17+
([sym]
18+
(import-macro* sym))
19+
([sym name]
20+
(import-macro* sym name)))
21+
22+
(defmacro import-def
23+
([sym]
24+
(import-macro* sym))
25+
([sym name]
26+
(import-macro* sym name)))
27+
28+
#_
29+
(defmacro import-vars
30+
"Imports a list of vars from other namespaces."
31+
[& syms]
32+
(let [unravel (fn unravel [x]
33+
(if (sequential? x)
34+
(->> x
35+
rest
36+
(mapcat unravel)
37+
(map
38+
#(symbol
39+
(str (first x)
40+
(when-let [n (namespace %)]
41+
(str "." n)))
42+
(name %))))
43+
[x]))
44+
syms (mapcat unravel syms)
45+
result `(do
46+
~@(map
47+
(fn [sym]
48+
(let [vr (resolve sym)
49+
m (meta vr)]
50+
(cond
51+
(nil? vr) `(throw (ex-info (format "`%s` does not exist" '~sym) {}))
52+
(:macro m) `(def ~(-> sym name symbol) ~sym)
53+
(:arglists m) `(def ~(-> sym name symbol) ~sym)
54+
:else `(def ~(-> sym name symbol) ~sym))))
55+
syms))]
56+
result))

src/potemkin.clj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
(ns potemkin
22
(:require
3-
[potemkin namespaces types collections macros utils]))
3+
[potemkin.namespaces]
4+
[potemkin.types]
5+
[potemkin.collections]
6+
[potemkin.macros]
7+
[potemkin.utils]))
48

59
(potemkin.namespaces/import-vars potemkin.namespaces/import-vars) ;; totally meta
610

src/potemkin/collections.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@
214214
(with-meta [this meta])
215215
216216
All other necessary functions will be defined so that this behaves like a normal
217-
Clojure map. These can be overriden, if desired."
217+
Clojure map. These can be overridden, if desired."
218218
[name params & body]
219219
(let [fns '{get get*
220220
assoc assoc*

src/potemkin/types.clj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -289,25 +289,25 @@
289289
(defonce type-bodies (atom {}))
290290

291291
(defmacro deftype+
292-
"A deftype that won't evaluate if an equivalent datatype with the same name already exists,
292+
"A deftype that won't evaluate if an equivalent type with the same name already exists,
293293
and allows abstract types to be used."
294294
[name params & body]
295295
(let [body (->> (list* 'deftype name params 'potemkin.types.PotemkinType body)
296-
clean-deftype
297-
expand-deftype
298-
deftype*->deftype)
296+
clean-deftype
297+
expand-deftype
298+
deftype*->deftype)
299299

300300
classname (with-meta (symbol (str (namespace-munge *ns*) "." name)) (meta name))
301301

302302
prev-body (when (class? (ns-resolve *ns* name))
303303
(@type-bodies classname))]
304304

305305
(when-not (and prev-body
306-
(equivalent?
307-
(transform-deftype* identity prev-body)
308-
(transform-deftype* identity body)))
306+
(equivalent?
307+
(transform-deftype* identity prev-body)
308+
(transform-deftype* identity body)))
309309
(swap! type-bodies assoc classname
310-
(r/macroexpand-all body))
310+
(r/macroexpand-all body))
311311

312312
body)))
313313

src/potemkin/utils.clj

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
[java.util.concurrent
77
ConcurrentHashMap]))
88

9-
(defmacro fast-bound-fn
10-
"Creates a variant of bound-fn which doesn't assume you want a merged
11-
context between the source and execution environments."
9+
(defmacro ^{:deprecated true
10+
:no-doc true
11+
:superseded-by "clojure.core/bound-fn"} fast-bound-fn
12+
"Quite probably not faster than core bound-fn these days.
13+
14+
~45% slower in personal testing. Be sure to profile your use case."
1215
[& fn-body]
1316
(let [{:keys [major minor]} *clojure-version*
1417
use-thread-bindings? (and (= 1 major) (< minor 3))
@@ -31,16 +34,20 @@
3134
(finally
3235
(clojure.lang.Var/resetThreadBindingFrame curr-frame#)))))))))
3336

34-
(defn fast-bound-fn*
35-
"Creates a function which conveys bindings, via fast-bound-fn."
37+
(defn ^{:deprecated true
38+
:no-doc true
39+
:superseded-by "clojure.core/bound-fn*"} fast-bound-fn*
40+
"Quite probably not faster than core bound-fn* these days.
41+
42+
~45% slower in personal testing. Be sure to profile your use case."
3643
[f]
3744
(fast-bound-fn [& args]
3845
(apply f args)))
3946

40-
(defn retry-exception? [x]
47+
(defn ^:no-doc retry-exception? [x]
4148
(= "clojure.lang.LockingTransaction$RetryEx" (.getName ^Class (class x))))
4249

43-
(defmacro try*
50+
(defmacro ^:deprecated ^:no-doc try*
4451
"A variant of try that is fully transparent to transaction retry exceptions"
4552
[& body+catch]
4653
(let [body (take-while
@@ -85,24 +92,27 @@
8592

8693
;;; fast-memoize
8794

88-
(definline re-nil [x]
95+
(definline ^:no-doc re-nil [x]
8996
`(let [x# ~x]
9097
(if (identical? ::nil x#) nil x#)))
9198

92-
(definline de-nil [x]
99+
(definline ^:no-doc de-nil [x]
93100
`(let [x# ~x]
94101
(if (nil? x#) ::nil x#)))
95102

96-
(defmacro memoize-form [m f & args]
103+
(defmacro ^:no-doc memoize-form [m f & args]
97104
`(let [k# (t/vector ~@args)]
98105
(let [v# (.get ~m k#)]
99106
(if-not (nil? v#)
100107
(re-nil v#)
101108
(let [v# (de-nil (~f ~@args))]
102109
(re-nil (or (.putIfAbsent ~m k# v#) v#)))))))
103110

104-
(defn fast-memoize
105-
"A version of `memoize` which has equivalent behavior, but is faster."
111+
(defn ^{:deprecated true
112+
:no-doc true
113+
:superseded-by "clojure.core/memoize"} fast-memoize
114+
"Quite possibly not faster than core memoize any more.
115+
See https://github.com/clj-commons/byte-streams/pull/50 and profile your use case."
106116
[f]
107117
(let [m (ConcurrentHashMap.)]
108118
(fn
@@ -131,7 +141,7 @@
131141
;;;
132142

133143
(defmacro doit
134-
"A version of doseq that doesn't emit all that inline-destroying chunked-seq code."
144+
"An iterable-based version of doseq that doesn't emit inline-destroying chunked-seq code."
135145
[[x it] & body]
136146
(let [it-sym (gensym "iterable")]
137147
`(let [~it-sym ~it

0 commit comments

Comments
 (0)