Conditionally adding to a collection - clojure

I want to add something to a collection based on a condition and leave it alone otherwise.
I found myself writing something like this:
(defn make-zoo
[zoo has-ice]
(let [zoo (if has-ice (conj zoo "penguins") zoo)]
zoo))
(make-zoo ["tigers"] false) ;["tigers"]
(make-zoo ["polar bears"] true) ;["polar bears" "penguins"]
I'm pretty new to Clojure, but this seems like a clunky solution for a common operation. Is there a more elegant way to address this?

We can simplify make-zoo using the cond-> macro, a conditional derivative of the -> threading macro:
(defn make-zoo [zoo has-ice]
(cond-> zoo, has-ice (conj "penguins")))

One simplification is to just leave out the let statement:
(defn make-zoo
[zoo has-ice]
(if has-ice
(conj zoo "penguins")
zoo))
(make-zoo ["tigers"] false) => ["tigers"]
(make-zoo ["polar bears"] true) => ["polar bears" "penguins"]

Related

Return value from middle of `do` (Clojure)

I have a list of I/O functions to run in a game, but need to collect the value from a function somewhere in the middle of the do
(defn setup-steps [game-state]
(do (io/clear-screen)
(print-welcome-message)
(initial-setup) ;; value to be collected
(io/clear-screen)
(io/print-board game-state)))
Is there a smart way to return the value from somewhere in the middle of a do?
Down the line, I am using the return value of setup-steps to update an atom, like so:
(defn game-loop [game]
(while (:game-in-progress? #game)
;; Here is where I am using the results
(->> (s-io/setup-steps #game) (state/updater game))
(while (:game-in-progress? #game)
(->> (m-io/turn-steps #game) (state/updater game)))
(->> (eg-io/end-game-steps #game) (state/updater game)))
(eg-io/exit-game))
Where
(defn updater
"Updates the game state.
This is the only place where the game atom is modified."
[game update-params]
(swap! game merge update-params))
I'm sure you could write a macro for this, but I don't really understand macros yet.
And maybe I am thinking about this the wrong way... Is it more idiomatic to dow the swap!ing inside setup-steps?
Any reason you can't assign the result in a let and return it at the end of the function?
(defn setup-steps [game-state]
(io/clear-screen)
(print-welcome-message)
(let [v (initial-setup)] ;; value to be collected
(io/clear-screen)
(io/print-board game-state)
v))
EDIT: Got rid of the redundant do that Ryan Asensio mentioned.
Fundamentally the only way to do this is with let, as clartaq shows. But if you find this distasteful there are a number of ways you could wrap this up in a macro to make it prettier to look at and also more clear about what you're doing. Here is the simplest one, which I like to call returning:
(defmacro returning [x & more]
`(let [x# ~x]
(do ~#more)
x#))
(defn foo []
(x)
(y)
(returning (z)
(a)
(b)))
The do in returning is of course superfluous, but I think it's still useful to emphasize that more is evaluated only for side effects.

Conditional "assignment" in functional programming

I am programming something that doesn't have side-effects, but my code is not very readable.
Consider the following piece of code:
(let [csv_data (if header_row (cons header_row data_rows) data_rows)]
)
I'm trying to use csv_data in a block of code. What is a clean way of conditioning on the presence of a header_row? I've looked at if-let, but couldn't see how that could help here.
I have run into similar situations with functional for-loops as well where I'm binding the result to a local variable, and the code looks like a pile of expressions.
Do I really have to create a separate helper function in so many cases?
What am I missing here?
Use the cond->> macro
(let [csv_data (cond->> data_rows
header_row (cons header-row)]
)
It works like the regular ->> macro, but before each threading form a test expression has to be placed that determines whether the threading form will be used.
There is also cond->. Read more about threading macros here: Official threading macros guide
First, don't use underscore, prefer dashes.
Second, there is nothing wrong with a little helper function; after all, this seems to be a requirement for handling your particular data format.
Third, if you can change your data so that you can skip those decisions and have a uniform representation for all corner cases, this is even better. A header row contains a different kind of data (column names?), so you might prefer to keep them separate:
(let [csv {:header header :rows rows}]
...)
Or maybe at some point you could have "headers" and "rows" be of the same type: sequences of rows. Then you can concat them directly.
The ensure-x idiom is a very common way to normalize your data:
(defn ensure-list [data]
(and data (list data)))
For example:
user=> (ensure-list "something")
("something")
user=> (ensure-list ())
(())
user=> (ensure-list nil)
nil
And thus:
(let [csv (concat (ensure-list header) rows)]
...)
i would propose an utility macro. Something like this:
(defmacro update-when [check val-to-update f & params]
`(if-let [x# ~check]
(~f x# ~val-to-update ~#params)
~val-to-update))
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> (:header :data1 :data2)
user> (let [header-row nil
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> [:data1 :data2]
it is quite universal, and lets you fulfill more complex tasks then just simple consing. Like for example you want to reverse some coll if check is trueish, and concat another list...
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows
(fn [h d & params] (apply concat (reverse d) params))
[1 2 3] ['a 'b 'c])]
csv-data))
;;=> (:data2 :data1 1 2 3 a b c)
update
as noticed by #amalloy , this macro should be a function:
(defn update-when [check val-to-update f & params]
(if check
(apply f check val-to-update params)
val-to-update))
After thinking about the "cost" of a one-line helper function in the namespace I've came up with a local function instead:
(let [merge_header_fn (fn [header_row data_rows]
(if header_row
(cons header_row data_rows)
data_rows))
csv_data (merge_header_fn header_row data_rows) ]
...
<use csv_data>
...
)
Unless someone can suggest a more elegant way of handling this, I will keep this as an answer.

How to combine a let binding and an if statment in Clojure

I'm learning Clojure, and trying to figure out the language to write things concisely and clearly. I'm trying to conditionally do something with a value after getting a value (say from a database) from a function that either returns the value or nil. I'm currently using a let binding, and then an if statement. Here is something similar to what I have currently:
(defn foo [key]
(let [result (try-getting! key)]
(if result
(+ result 50)
50)))
I'm trying to figure out if there is a more consise way to do this, perhaps some sort of combined if-let binding? Is there a way to write this better?
Thanks!
There is form if-let for this:
(defn foo [key]
(if-let [result (try-getting! key)]
(+ result 50)
50))
Yes, in fact if-let is what you want here.
(if-let [result (try-getting! key)]
(+ result 50)
50)

Side effects in Lisp/Clojure

My question is about structuring lisp code with side effects. The particular example I have in mind comes from Clojure, but I think it can apply to any lisp.
In this case, I am interacting with an existing library that requires some functions to be called in a particular order. The final function call creates the value I need for the rest of the procedure.
The code looks like this:
(defn foo []
(let [_ procedure-with-side-effect
__ another-procedure-with-side-effect
value procedure-that-creates-the-value]
(do-something value)))
This works and everything is great, except I think the let block looks hideous. Is there a better way to do this?
If you don't need the intermediate values of the function calls, you can just put a bunch of function calls in the body of the defn:
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(do-something (procedure-that-creates-the-value)))
While this is the best for this code, there are other options. You can also put any number of function calls in the body of a let:
(let [val 3]
(fun-call-1)
(fun-call-2)
(fun-call-3 val))
And if you don't want to bind any values, you can use do:
(do (fun-call-1)
(fun-call-2)
(fun-call-3))
In Lisp every function body is a ordered set of forms. The value(s) of the last form will be returned. If the procedures don't use intermediate result values as arguments, a LET is not necessary. If the procedure-that-creates-the-value does not need to be documented by naming a variable, the LET binding for its value is also not necessary.
So in Lisp the code is just this:
(defun foo ()
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(do-something (procedure-that-creates-the-value)))
I'm not super experienced, but I'd do it this way:
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(let [value (procedure-that-creates-the-value)]
(do-something value)))
or
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(-> (procedure-that-creates-the-value)
do-something))
or
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(do-something (procedure-that-creates-the-value)))
Edit: defn expressions are wrapped with an implicit do.

Function composition in Clojure?

Can Clojure implement (g ∘ f) constructions like Haskell's g . f? I'm currently using workarounds like (fn [n] (not (zero? n))), which isn't nearly as nice :)
There is a function to do this in clojure.core called comp. E.g.
((comp not zero?) 5)
; => true
You might also want to consider using -> / ->>, e.g.
(-> 5 zero? not)
; => true
You can use Clojure's reader-macro shortcut for anonymous functions to rewrite your version in fewer keystrokes.
user=> (#(not (zero? %)) 1)
true
For the specific case of composing not and another function, you can use complement.
user=> ((complement zero?) 1)
true
Using not in combination with when and if is common enough that if-not and when-not are core functions.
user=> (if-not (zero? 1) :nonzero :zero)
:nonzero
Another way is (reduce #(%2 %1) n [zero? not]) but I like (comp not zero?), already mentioned by Michal, better.