1

I'm relatively new to Clojure, so I'm having trouble wrapping my mind around how to make the following work.

Starting with a Java method (from the reflect package), I want to extract various properties and end up with a map that looks something like the following:

{ :name "test-method
  :return-type "String"
  :public true
}

Since the logic of building the keys can be rather complex, I'd like to chain a series of functions that take the current map and the method object and either modify the map or return it as is. Something like:

(defn build-public 
[acc method] 
(if (is-public? method) (assoc acc :public true) acc))

Which might be called like:

(make-method-map method [build-public build-return-type build-name])

I've tried a couple of different approaches but can't seem to make it work, any suggestions would be greatly appreciated.

4 Answers 4

2

the simple way is to apply every function one by one with reduce:

user> (defn make-method-map [method fns]
        (reduce (fn [acc f] (f acc method))
                {} fns))
#'user/make-method-map-2

user> (defn make-name [acc method]
        (assoc acc :name 123))
#'user/make-name

user> (defn make-other [acc method]
        (assoc acc :something "ok"))
#'user/make-other

user> (make-method-map {:a 1 :b 2} [make-name make-other])
;;=> {:name 123, :something "ok"}
Sign up to request clarification or add additional context in comments.

1 Comment

I ended up implementing this solution. It's been working great. Thanks!
0

One way to do this is with cond-> which combines the concepts of cond and ->.

(cond-> {:my-map 1}
  (= 2 2) (assoc :two-equals-two true)
  (= true false) (assoc :not-possible "hey"))
=> {:my-map 1, :two-equals-two true}

The clauses on the left determine whether the forms on the right will be evaluated, with the initial value threaded through in the first position.

1 Comment

This is really great. I've been digging into cond more, really awesome tool!
0

I would write make-method-map something like this:

(defn method-map [method]
  (-> {}
      (assoc :return-type (get-return-type method))
      (assoc :name (get-name method))
      (cond-> (public? method) (assoc :public? true))))

Note how you can nest cond-> (or as-> or ->>) inside a ->. Putting any of them inside of a ->> or similar might not work as you'd expect, though.

Why that is is left as an exercise to the reader.

Actually I would write the thing like this:

(defn method-map [method]
  (-> {}
      (assoc :return-type (get-return-type method))
      (assoc :name (get-name method))
      (assoc :public? (public? method))))

Or with a literal map if I could get away with it (one step doesn't depend on the next, etc.)

(defn method-map [method]
  {:return-type (get-return-type method)
   :name (get-name method)
   :public? (public? method)})

But that doesn't show off the trick with nesting cond->.

If you find yourself with a vector of functions, then leetwinski's answer is good. Like everything in programming, it depends.

Edit: For a real world example of a vector of functions being used, check out the concept of interceptors in re-frame or pedestal.

1 Comment

The nested threading macro thing is explained well here.
0

I like the reduce fns option, but here is an alternative with a bit different function signatures:

(defn make-name  [method] {:name   (str (:a method) "-" (:b method))})
(defn make-other [method] {:a-is-3 (= (:a method) 3)})

(defn make-method-map [method fns]
  (into {} ((apply juxt fns) method)))

(defn make-method-map [method fns]
  (reduce merge ((apply juxt fns) method)))

(defn make-method-map [method fns]
  (apply merge ((apply juxt fns) method)))


(make-method-map {:a 1 :b 2} [make-name make-other])
; {:name "1-2", :a-is-3 false}

juxt returns a vector of function return values, here the acc is not taken as an input argument but instead into or reduce is used to "merge" these hash-maps into the final result. Originally I thought of using comp but these styles of functions aren't that "composable".

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.