I am looking for something like doto
but instead of returning x
, I would like to return the last call value.
For instance, instead of:
(.size (doto (java.util.ArrayList.)
(.add 2)
(.add 3)))
I would like to write:
(mydoto (java.util.ArrayList.)
(.add 2)
(.add 3)
.size)
What is the idiomatic way of achieving this result?
doto
returns it's first argument. ->
and ->>
return the result of evaluating the last form (after threading the first expression through, see (clojure.repl/doc ->)
. The macro doto
will expand your code like this:
(let [r (java.util.ArrayList.)]
(.add r 2)
(.add r 3)
r)
When you are using .size
, it does not affect your array list and you are just interested in the return value of the method (unlike with .add
which returns a boolean value and changes the state of the ArrayList which you are interested in).
You can't mix these methods like in mydoto, because what you require are different kinds of return and input values. If you wanted to multiply the final size by 3, this would be idiomatic:
(-> (doto (java.util.ArrayList.)
(.add 2)
(.add 3))
.size
(* 3))
Or even
(-> (java.util.ArrayList.)
(doto (.add 2) (.add 3))
.size
(* 3))
These macros should make the code easier to read so depending on the context I'd write them in a way that makes most sense.
Clojure macros will allow you to do what you want, in fact it's only a 1 symbol change to the existing doto macro (in emacs use Meta-. to see the definition in core.clj).
(defmacro mydoto
"Like doto but returns the result of the last form
instead to the first argument"
[x & forms]
(let [gx (gensym)]
`(let [~gx ~x]
~@(map (fn [f]
(if (seq? f)
`(~(first f) ~gx ~@(next f))
`(~f ~gx)))
forms))))
This will allow you to write the following code:
(mydoto (java.util.ArrayList.)
(.add 2)
(.add 3)
.size)
;; => 2
One could write mydoto
using doto
, as mentioned by @lgrapenthin:
(defmacro mydoto
"Like doto but returns the result of the last form instead
of the first argument"
[& args]
`(-> (doto ~@(butlast args))
~(last args)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With