Fork me on GitHub
#clojure
<
2020-09-19
>
kimim03:09:13

Dear all, is it possible to get body of a function as string, then I'd like to print the function body. Thanks.

the2bears15:09:15

What's your use case? Functions in code you've written, or otherwise?

andy.fingerhut03:09:02

source will do that if the body of the function is stored in a file, and its definition was loaded from the file.

andy.fingerhut03:09:28

Clojure does not otherwise save the body of functions as anything other than their compiled byte-code format.

andy.fingerhut03:09:07

one could modify Clojure to save functions in other forms than that, but it does not do so as implemented today.

ivana14:09:36

@gary001 quick & dirty

(defn f [x] (into (if (instance? clojure.lang.MapEntry (first x)) {} []) x))
  (f (lazy-seq {:a 1 :b 2 :c 3}))
  (f (lazy-seq [1 2 3]))
  (f (lazy-seq '(1 2 3)))
  (f (lazy-seq `(1 2 3)))
  (f (lazy-seq nil))

jsn14:09:13

@ivana (f (lazy-seq (mapcat seq [{:a 1} {:b 2} {:c 3}]))) ? 🙂

ivana14:09:42

@jason358 yep, seq of map entries should be a map )

ivana14:09:50

even if (f (lazy-seq (mapcat seq [{:a 1} {:a 2} {:a 3}]))) 🙂

ivana14:09:46

moreover, if (*f* (*lazy-seq* (*concat* (*seq* {:a 1 :stuck_out_tongue: 2 :c 3}) [[:d 4]]))) 🙂

ivana14:09:52

but for last case one can use

(defn f [x] (into (if (every? #(instance? clojure.lang.MapEntry %) x) {} []) x))

dpsutton14:09:53

user=> (lazy-seq (mapcat seq [{:a 1} {:a 2} {:a 3}]))
([:a 1] [:a 2] [:a 3])
user=> (f (lazy-seq (mapcat seq [{:a 1} {:a 2} {:a 3}])))
{:a 3}

ivana14:09:29

(set [1 1 1 1 1 1])
#{1}

dpsutton14:09:02

sure. i'm just pointing out perhaps a pitfall of the above function. a sequence of maps is a valid datastructure. combining them into a single map has the potential for data loss. in the domain perhaps this is acceptable, or perhaps the input data has more constraints than we know and this will never happen. but just pointing out a potential pitfall in the function

ivana14:09:49

yep, reducing api to lazy seqs already reduces our possibilities. so we should accetpt it or change api. but in current state it is all we can do/ and I never met seq of map entries with duplicate keys in wild life )

ivana14:09:32

sequence of maps is a valid datastructure - yep, but do not mix it with seq of map entries

ivana15:09:27

anyway, we can play further )

(defn assoc-mm [maps k v]
    (let [i (->> maps
                 (map-indexed (fn [i m] (when-not (contains? m k) i)))
                 (filter identity)
                 first)]
      (if i (update maps i assoc k v) (conj maps {k v}))))

  (defn f [x] (if (every? #(instance? clojure.lang.MapEntry %) x)
                (reduce (fn [acc [k v]] (assoc-mm acc k v)) [] x)
                (into [] x)))

stephenmhopper22:09:27

Is there a way to intercept function calls with something like with-redefs and then call the original function?

seancorfield22:09:52

(let [orig f] (with-redefs [f (fn [& args] (do-stuff) (orig args))] (body-code))) like that @stephenmhopper?

stephenmhopper22:09:26

Yeah, I had tried that and got a stack overflow, but I’ll give it another shot

seancorfield22:09:41

(but beware of all the usual caveats around with-redefs and lazy sequences and threads etc)

seancorfield22:09:22

@stephenmhopper

user=> (defn foo [x] (println "foo" x))
#'user/foo
user=> (defn bar [x] (foo x) (foo (* 2 x)))
#'user/bar
user=> (let [orig foo] (with-redefs [foo (fn [x] (println "redefined" x) (orig x))] (bar 13)))
redefined 13
foo 13
redefined 26
foo 26
nil
user=>

stephenmhopper22:09:56

Interesting. It’s working now. I’m not sure what I was doing wrong before. Thank you!