Skip to content

Instantly share code, notes, and snippets.

@didibus
Last active January 5, 2021 22:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save didibus/3bb36bdb712b3c62b15b6efb91cd5f06 to your computer and use it in GitHub Desktop.
Save didibus/3bb36bdb712b3c62b15b6efb91cd5f06 to your computer and use it in GitHub Desktop.
Some Clojure tagged literals to help create functions in various ways, such as create a function that discards any argument passed to it.
;; This one will create a function that takes 0 or more args and discards them all (doesn't use them).
(defn &-tag-reader
[[f & args]]
`(fn [~'& ~'args] (~f ~@args)))
(set! *data-readers* (assoc *data-readers* '& user/&-tag-reader))
(mapv #&(rand-int 5) (range 10))
;;=> [3 0 4 0 2 0 4 4 4 2]
;; This one extends the previous one, so that you can also specify parameters to use if you want like so:
;; #&(a b | + a b)
;; Notice that it will still wrap the expression in parenthesis, same as normal #()
(defn &-tag-reader
[form]
(let [split-sym '|
[params exprs] (split-with (complement #{split-sym}) form)]
(cond
(#{split-sym} (first form))
`(fn [~'& args#] (~@form))
(seq exprs)
`(fn [~@params] (~@(rest exprs)))
:else
`(fn [~'& args#] (~@form)))))
(set! *data-readers* (assoc *data-readers* '& user/&-tag-reader))
(mapv #&(rand-int 5) (range 10))
;;=> [4 4 0 1 3 2 2 3 4 4]
(mapv #&(e | rand-int e) (range 10))
;;=> [0 0 1 2 1 3 5 4 7 3]
;; Also works even if you do have a Var named |
(defn |
[a b]
(+ a b))
(mapv #&(e | | e e) (range 10))
;;=> [0 2 4 6 8 10 12 14 16 18]
(mapv #&(| 10 10) (range 10))
;;=> [20 20 20 20 20 20 20 20 20 20]
;; This one can just use $named symbols as args and it will infer
;; the number of params from them. Their order will be based on
;; alphabetical order of the names of the used $named symbols.
(require '[clojure.walk :as walk])
(defn $-tag-reader
[form]
(let [params (atom #{})]
(walk/postwalk
(fn [e]
(if (and (symbol? e) (= \$ (first (name e))))
(swap! params conj e)
e))
form)
(let [param-map (reduce (fn[acc e] e (assoc acc e (gensym)))
(sorted-map)
@params)
new-form (walk/postwalk
(fn [e]
(if (and (symbol? e) (param-map e))
(param-map e)
e))
form)]
`(fn [~@(vals param-map)]
(~@new-form)))))
(set! *data-readers* (assoc *data-readers* '$ user/$-tag-reader))
(mapv #$(+ $range 10) (range 10))
;;=> [10 11 12 13 14 15 16 17 18 19]
(reduce #$(assoc $acc $e (rand-int 10)) {} [:a :b :c :d])
;;=> {:a 3, :b 4, :c 2, :d 2}
;; Finally, here's a final two that kind of combine all prior features nicely:
(require '[clojure.walk :as walk])
(defn f$-tag-reader
[form vararg?]
(let [params (atom #{})]
(walk/postwalk
(fn [e]
(if (and (symbol? e) (= \$ (first (name e))))
(swap! params conj e)
e))
form)
(let [param-map (reduce (fn[acc e] e (assoc acc e (gensym)))
(sorted-map)
@params)
new-form (walk/postwalk
(fn [e]
(if (and (symbol? e) (param-map e))
(param-map e)
e))
form)]
(if vararg?
`(fn [~@(vals param-map) ~'& more#]
(~@new-form))
`(fn [~@(vals param-map)]
(~@new-form))))))
(defn f-tag-reader
[form]
(let [split-sym '|
[params exprs] (split-with (complement #{split-sym}) form)]
(cond
(#{split-sym} (first form))
(f$-tag-reader form false)
(seq exprs)
`(fn [~@params] (~@(rest exprs)))
:else
(f$-tag-reader form false))))
(defn f_-tag-reader
[form]
(let [split-sym '|
[params exprs] (split-with (complement #{split-sym}) form)]
(cond
(#{split-sym} (first form))
(f$-tag-reader form true)
(seq exprs)
`(fn [~@params ~'& more#] (~@(rest exprs)))
:else
(f$-tag-reader form true))))
(set! *data-readers* (assoc *data-readers* 'f user/f-tag-reader))
(set! *data-readers* (assoc *data-readers* 'f_ user/f_-tag-reader))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment