André
André

Reputation: 13317

how do I return a previously calculated result in clojure?

I am new to clojure, so excuse me, if I am not using the right terminology yet. I am looking for a way, to do the following in the most idiomatic way:

I have a function that merges any given number of maps into one, let's call it merge-maps.

Now I need a second function that calls the first, then calls a static void java function with the result and then returns the exact same result. I get the first two things, but I fail in doing the third. Since the static void method evaulates to nil, I have to somehow return the value, but I don't understand how:

(defn my-funky-function [& ms]
   (def merged (merge-maps ms))
   (SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff")
   ;????? <- how do I "return" merged from here?)
)

Upvotes: 2

Views: 265

Answers (2)

sloth
sloth

Reputation: 101042

Like this

(defn my-funky-function [& ms]
   (def merged (merge-maps ms))
   (SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff")
   merged)

Note: you should use a let binding instead of creating a Var with def.

(defn my-funky-function [& ms]
   (let [merged (merge-maps ms)]
     (SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff")
     merged))

Upvotes: 1

Leon Grapenthin
Leon Grapenthin

Reputation: 9266

No need for a function, and also don't use def inside of a function unless you explicitly intend to "create and intern a global var" (docstring of def). Merging maps can be done with merge.

(doto (merge ms) (SomeJavaClass/someStaticVoidMethod "some" "other" "stuff"))

EDIT: Because my answer may be misunderstood regarding the correct use of def (see Leonids comment): Using def inside of a function is never a good idea. I quoted from the docstring to hint at the global effect of using def which you certainly don't want to have in your function.

EDIT2: Here a bit of more explanation regarding the line above: doto always returns it's first argument. If it is mutable and the expressions after it modify it as a side effect, the modified version is returned. However, if you just want to invoke a static method that (presumably) has side effects that don't modify the first argument, it is correct to use doto because you can rely on the immutability of your maps.

Since you were asking for a general idiomatic approach and want to solve a similar problem when your static method (or function with side effects) does not expect your desired return value as a first argument, you can always do this:

(doto (merge ms) (#(SomeJavaClass/someStaticMethod "Some" "other" "stuff" %)))

Now if for any reason you need to be sure that the static method never modifies the argument (if it is a mutable thing), using a let block doesn't help. This is because in the let-block you are binding a symbol to the mutable thing and you would not get it's old value from the symbol. You would need to create a copy of the old value beforehand, usually by invoking it's constructor and creating a new instance of it.

Example at the repl:

(let [a (into-array [4 3 2 1]]
  (java.util.Arrays/sort a)
  a)
(first *1)
=> 1

;; now how to return the original thiing
(let [a (into-array [4 3 2 1])
      a-copy (aclone a)]
  (java.util.Arrays/sort a)
  ;; do some other ops on a, maybe invoke side-effects
  a-copy)
(first *1)
=> 4

So if the first let-block produces your desired result, the let-block can be replaced with doto which is a helper macro that does exactly nothing else than creating that let-block for you.

(doto (into-array [4 3 2 1]) java.util.Arrays/sort)
(first *1)
=> 1
(macroexpand '(doto (into-array [4 3 2 1]) java.util.Arrays/sort))
=> (let* [G__7326 (into-array [4 3 2 1])] (java.util.Arrays/sort G__7326) G__7326)

Since you are not "doing anything" to your merged-maps, the name "doto" might be slightly misleading but hopefully I could convice you to save a few lines of redundant code.

Upvotes: 1

Related Questions