|
| 1 | +(ns active.clojure.function |
| 2 | + "Redefines higher order functions and function combinators from |
| 3 | + clojure.core via applicable records (ifn? but not fn?). The |
| 4 | + advantage is, that those objects compare = if they are created from |
| 5 | + equal arguments. Disadvantages are that they are probably a bit |
| 6 | + slower. They also don't implement some additional protocols like |
| 7 | + Runnable yet." |
| 8 | + (:refer-clojure :exclude [partial constantly comp complement juxt fnil every-pred some-fn bound-fn* |
| 9 | + completing])) |
| 10 | + |
| 11 | +#?(:cljs |
| 12 | + (defrecord ^:no-doc Partial [f_ args] |
| 13 | + IFn |
| 14 | + (-invoke [this] (apply f_ args)) |
| 15 | + (-invoke [this a] (apply f_ (concat args (list a)))) |
| 16 | + (-invoke [this a b] (apply f_ (concat args (list a b)))) |
| 17 | + (-invoke [this a b c] (apply f_ (concat args (list a b c)))) |
| 18 | + (-invoke [this a b c d] (apply f_ (concat args (list a b c d)))) |
| 19 | + (-invoke [this a b c d e] (apply f_ (concat args (list a b c d e)))) |
| 20 | + (-invoke [this a b c d e f] (apply f_ (concat args (list a b c d e f)))) |
| 21 | + (-invoke [this a b c d e f g] (apply f_ (concat args (list a b c d e f g)))) |
| 22 | + (-invoke [this a b c d e f g h] (apply f_ (concat args (list a b c d e f g h)))) |
| 23 | + (-invoke [this a b c d e f g h i] (apply f_ (concat args (list a b c d e f g h i)))) |
| 24 | + (-invoke [this a b c d e f g h i j] (apply f_ (concat args (list a b c d e f g h i j)))) |
| 25 | + (-invoke [this a b c d e f g h i j k] (apply f_ (concat args (list a b c d e f g h i j k)))) |
| 26 | + (-invoke [this a b c d e f g h i j k l] (apply f_ (concat args (list a b c d e f g h i j k l)))) |
| 27 | + (-invoke [this a b c d e f g h i j k l m] (apply f_ (concat args (list a b c d e f g h i j k l m)))) |
| 28 | + (-invoke [this a b c d e f g h i j k l m n] (apply f_ (concat args (list a b c d e f g h i j k l m n)))) |
| 29 | + (-invoke [this a b c d e f g h i j k l m n o] (apply f_ (concat args (list a b c d e f g h i j k l m n o)))) |
| 30 | + (-invoke [this a b c d e f g h i j k l m n o p] (apply f_ (concat args (list a b c d e f g h i j k l m n o p)))) |
| 31 | + (-invoke [this a b c d e f g h i j k l m n o p q] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q)))) |
| 32 | + (-invoke [this a b c d e f g h i j k l m n o p q r] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q r)))) |
| 33 | + (-invoke [this a b c d e f g h i j k l m n o p q r s] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q r s)))) |
| 34 | + (-invoke [this a b c d e f g h i j k l m n o p q r s t] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q r s t)))) |
| 35 | + (-invoke [this a b c d e f g h i j k l m n o p q r s t rest] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q r s t) rest))))) |
| 36 | + |
| 37 | +#?(:clj |
| 38 | + (defrecord ^:no-doc Partial [f_ args] |
| 39 | + clojure.lang.IFn |
| 40 | + (applyTo [this arglist] (apply f_ (concat args arglist))) |
| 41 | + (invoke [this] (apply f_ args)) |
| 42 | + (invoke [this a] (apply f_ (concat args (list a)))) |
| 43 | + (invoke [this a b] (apply f_ (concat args (list a b)))) |
| 44 | + (invoke [this a b c] (apply f_ (concat args (list a b c)))) |
| 45 | + (invoke [this a b c d] (apply f_ (concat args (list a b c d)))) |
| 46 | + (invoke [this a b c d e] (apply f_ (concat args (list a b c d e)))) |
| 47 | + (invoke [this a b c d e f] (apply f_ (concat args (list a b c d e f)))) |
| 48 | + (invoke [this a b c d e f g] (apply f_ (concat args (list a b c d e f g)))) |
| 49 | + (invoke [this a b c d e f g h] (apply f_ (concat args (list a b c d e f g h)))) |
| 50 | + (invoke [this a b c d e f g h i] (apply f_ (concat args (list a b c d e f g h i)))) |
| 51 | + (invoke [this a b c d e f g h i j] (apply f_ (concat args (list a b c d e f g h i j)))) |
| 52 | + (invoke [this a b c d e f g h i j k] (apply f_ (concat args (list a b c d e f g h i j k)))) |
| 53 | + (invoke [this a b c d e f g h i j k l] (apply f_ (concat args (list a b c d e f g h i j k l)))) |
| 54 | + (invoke [this a b c d e f g h i j k l m] (apply f_ (concat args (list a b c d e f g h i j k l m)))) |
| 55 | + (invoke [this a b c d e f g h i j k l m n] (apply f_ (concat args (list a b c d e f g h i j k l m n)))) |
| 56 | + (invoke [this a b c d e f g h i j k l m n o] (apply f_ (concat args (list a b c d e f g h i j k l m n o)))) |
| 57 | + (invoke [this a b c d e f g h i j k l m n o p] (apply f_ (concat args (list a b c d e f g h i j k l m n o p)))) |
| 58 | + (invoke [this a b c d e f g h i j k l m n o p q] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q)))) |
| 59 | + (invoke [this a b c d e f g h i j k l m n o p q r] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q r)))) |
| 60 | + (invoke [this a b c d e f g h i j k l m n o p q r s] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q r s)))) |
| 61 | + (invoke [this a b c d e f g h i j k l m n o p q r s t] (apply f_ (concat args (list a b c d e f g h i j k l m n o p q r s t)))))) |
| 62 | + |
| 63 | + |
| 64 | +(defn partial |
| 65 | + "Takes a function f and fewer than the normal arguments to f, and |
| 66 | + returns a fn that takes a variable number of additional args. When |
| 67 | + called, the returned function calls f with args + additional args." |
| 68 | + [f & args] |
| 69 | + (Partial. f args)) |
| 70 | + |
| 71 | +(letfn [(_lift-variadic [f fargs & args] |
| 72 | + (apply (apply f fargs) args))] |
| 73 | + ;; Note: this is most easiest way to lift a higher-order fn f, but often not the most efficient |
| 74 | + (defn lift-variadic [f & fargs] |
| 75 | + (partial _lift-variadic f fargs))) |
| 76 | + |
| 77 | +(letfn [(_constantly [v & args] |
| 78 | + v)] |
| 79 | + (defn constantly |
| 80 | + "Returns a function that takes any number of arguments and returns x." |
| 81 | + [v] |
| 82 | + (partial _constantly v))) |
| 83 | + |
| 84 | +(defn comp |
| 85 | + "Takes a set of functions and returns a fn that is the composition |
| 86 | + of those fns. The returned fn takes a variable number of args, |
| 87 | + applies the rightmost of fns to the args, the next |
| 88 | + fn (right-to-left) to the result, etc." |
| 89 | + ([] identity) |
| 90 | + ([f] f) |
| 91 | + ([f g & fs] |
| 92 | + (apply lift-variadic clojure.core/comp f g fs))) |
| 93 | + |
| 94 | +(defn complement |
| 95 | + "Takes a fn f and returns a fn that takes the same arguments as f, |
| 96 | + has the same effects, if any, and returns the opposite truth value." |
| 97 | + [f] |
| 98 | + (lift-variadic clojure.core/complement f)) |
| 99 | + |
| 100 | +(defn juxt |
| 101 | + "Takes a set of functions and returns a fn that is the juxtaposition |
| 102 | + of those fns. The returned fn takes a variable number of args, and |
| 103 | + returns a vector containing the result of applying each fn to the |
| 104 | + args (left-to-right). |
| 105 | + ((juxt a b c) x) => [(a x) (b x) (c x)]" |
| 106 | + [f & fns] |
| 107 | + (apply lift-variadic clojure.core/juxt f fns)) |
| 108 | + |
| 109 | +(defn fnil |
| 110 | + "Takes a function f, and returns a function that calls f, replacing |
| 111 | + a nil first argument to f with the supplied value x. Higher arity |
| 112 | + versions can replace arguments in the second and third |
| 113 | + positions (y, z). Note that the function f can take any number of |
| 114 | + arguments, not just the one(s) being nil-patched." |
| 115 | + ([f x] (lift-variadic clojure.core/fnil f x)) |
| 116 | + ([f x y] (lift-variadic clojure.core/fnil f x y)) |
| 117 | + ([f x y z] (lift-variadic clojure.core/fnil f x y z))) |
| 118 | + |
| 119 | +(defn every-pred |
| 120 | + "Takes a set of predicates and returns a function f that returns true if all of its |
| 121 | + composing predicates return a logical true value against all of its arguments, else it returns |
| 122 | + false. Note that f is short-circuiting in that it will stop execution on the first |
| 123 | + argument that triggers a logical false result against the original predicates." |
| 124 | + [p & ps] |
| 125 | + (apply lift-variadic clojure.core/every-pred p ps)) |
| 126 | + |
| 127 | + |
| 128 | +(defn some-fn |
| 129 | + "Takes a set of predicates and returns a function f that returns the first logical true value |
| 130 | + returned by one of its composing predicates against any of its arguments, else it returns |
| 131 | + logical false. Note that f is short-circuiting in that it will stop execution on the first |
| 132 | + argument that triggers a logical true result against the original predicates." |
| 133 | + [p & ps] |
| 134 | + (apply lift-variadic clojure.core/some-fn p ps)) |
| 135 | + |
| 136 | +#?(:clj |
| 137 | + (letfn [(_bound-fn* [bindings f & args] |
| 138 | + (apply with-bindings* bindings f args))] |
| 139 | + (defn bound-fn* |
| 140 | + "Returns a function, which will install the same bindings in effect as in |
| 141 | + the thread at the time bound-fn* was called and then call f with any given |
| 142 | + arguments. This may be used to define a helper function which runs on a |
| 143 | + different thread, but needs the same bindings in place." |
| 144 | + [f] |
| 145 | + ;; Note: this cannot be done with lift-variadic, because |
| 146 | + ;; get-thread-bindings is side-effectful, and has to be called |
| 147 | + ;; now, not later. |
| 148 | + (let [bindings (get-thread-bindings)] |
| 149 | + (partial _bound-fn* bindings f))))) |
| 150 | + |
| 151 | +(defn completing |
| 152 | + "Takes a reducing function f of 2 args and returns a fn suitable for |
| 153 | + transduce by adding an arity-1 signature that calls cf (default - |
| 154 | + identity) on the result argument." |
| 155 | + ([f] (completing f identity)) |
| 156 | + ([f cf] (lift-variadic clojure.core/completing f cf))) |
0 commit comments