Fork me on GitHub

When nil should result if a test is false, and only one form is needed when the test is true, is it more idiomatic to use (when test xxx) or (if test xxx)? Or is neither really more idiomatic?


Let me try one more time mentioning side effects: When nil should result if a test is false, and only one form is needed when the test is true and there are no side effects, is it more idiomatic to use (when test xxx) or (if test xxx)?


It depends whether when should imply side effects, I think.


@mark540 Have you looked at what the Community Style Guide has to say about that?


(I wasn't sure what it said, so that was just a suggestion... now I look, it seems to only make a recommendation of (when cond form1 form2) over (if cond (do form1 form2)) which, yeah, I'd agree with... but doesn't cover simple (if cond form) vs (when cond form)...)


Personally, I would use (when cond form) instead of an if without an else form, since that makes it clear (to me) that you are deliberately returning nil for (not cond) rather than you just accidentally omitted the else form in an if.


Right, the style didn't say, so I thought I'd see what you all thought. I like that about when also. But I wasn't sure if side effects were implied. I guess you don't think so.


(sorry, was having dinner)


No, I don't think if vs when is a good indication of pure functions vs side-effects -- that's far too subtle of a choice, IMO @mark540


Good naming should make that clear I think (have you read Elements of Clojure by Zach Tellman?).

💯 5

I'm reading it now, really impressive.


There is a camp that uses a ! suffix to indicate side-effects but that's never really been a consistent convention (and side-effects have a tendency of propagating throughout your program so you'd have! exclamation! marks! everywhere! 🙂 )

😂 5

Yes. I was thinking of functions like doseq that are only useful with side effects and probably shouldn't be used when there are none. when could be thought of in that category since if is an alternative without side effects, but I think you're right that when is also used instead of if for clarity.


I find (when cond form) more natural than (if cond form nil) to be honest


But maybe that's because I've been doing Clojure a long time now and my brain automatically sees when as returning nil in the non-`cond` case...?


A lot of Clojure's idioms are non-obvious at first, and also depending on what your language background is...


An example where I think when is much more natural -- when you're constructing a string, using str, and you have a bunch of conditional parts:

(str "something"
     (when cond1
       "option 1")
     (when cond2
       "option 2")

✔️ 10

Hi everyone ! :hugging_face: been a long time admirer of clojure and especially the vibrant community but didn't have a chance learning it yet I am planning to start learning clojure and will be for playing with quil (processing library) mostly which books or materials would you recommend for someone who has prior knowledge of functional languages (OCaml, haskell, javascript , bit of elixir) but little to no experience in java


if you have some previous programming experience (like you seem to have) I highly recommend


it covers pretty much everything, except the more recent additions to the core like transducer


great . Thanks for the recommendation quick question though, does it cover tooling and java side of clojure ?


it has a chapter on Java interop, don’t remember about the tooling though


if you learn the basics of Leiningen you will be set for tooling for a good while


Thank you for the reply i'll be buying the book now


Also omg!! lein is really easy to setup. 💯 on user on boarding after coming from js, having a single tool is such a relief


haha yeah 😄 there are a couple of different build tools in Clojure as well (Leiningen, Boot, clj), but for beginners I recommend sticking to Leiningen since it is by far the most used and because of this you can find a solution to most common issues on SO

Eric Ervin00:01:49

@U7W7M34UQ, there is a #quil channel. it's really quiet down there right now. maybe I'll make some noise.


@U9U0G3Q8Y Thanks for link 🙂


If you want to have a go with a server/client environment to apply the theory you will be learning, you can def try ...


thanks i'll keep an eye on it. but currently looking for learning the language features more


What would be a good book/resource to start learning ClojureScript? was a good resource to learn about Clojure. Now I am at the latter part of the books, I am looking for a good resource to learn ClojureScript.


@asanka.abeyweera you really want to get familiar with libraries like reagent, hiccup, and reframe .. once you know those, you can potentially build anything.. 👌:skin-tone-3:

👍 10

@asanka.abeyweera do you know JavaScript?


I am not an expert. Just know the basics. 🙂

Ian Fernandez13:01:34

there's some way to convert a curl request to a httpkit request?

Sy Borg17:01:11

hi. I'm trying to solve problem #22 on 4clojure. I know this code is stupid and not optimal, but it works in my REPL. However 4Clojure complains on bad count. Can anyone please explain why?


are you wanting a fix? or just an explanation?

Sy Borg17:01:08

a fix for what? explanation would be nice


a fix to your code that solves the problem


#(reduce (fn[i _] (inc i)) 0 %)


np, btw the reason yours failed is because the underlying code in for uses count. I imagine the restriction check sees it too.


that’s weird


@sy_borg you’re thinking of cnt as a variable, not a binding


you’re incrementing it, which means you’re thinking of it as something with state


the classic way to do that is the loop form

Sy Borg17:01:48

well, with all respect, you can't know what I'm thinking. my question was why the code behaves differently.


Are you expecting that cnt will have a different value than 0, ever?


what makes you think that works in your repl?


I mean, I guess it does


I wonder what input 4clojure is giving it


Basically the for is returning a sequence of N 1's, where N is the number of iterations through the for, which is one for each element in (seq s) (I think)


So yeah, I can't see why 4clojure complains that it is giving the wrong answer.

Sy Borg17:01:56

yes, that was my idea to generate a seq of 1s

Sy Borg17:01:08

(in a bit weird way, I know)


Why not just (for [i s] 1)?


oh, I bet it is because the macro expand of for calls count


so if you can re-formulate that to not use for (which is pretty straightforward) it will work

Sy Borg17:01:34

@madstap too simple 🙂


yeah, for calls count when dealing with chunked seqs


4clojure must be checking for verboten function calls after macro expansion


(for [i s] 1) can be expressed almost as nicely as a call to map

Sy Borg17:01:22

one of the proposed solutions - (fn [coll] (reduce (fn [x y] (inc x)) 0 coll))


slightly more obscure solution: (fn [coll] (last (map (fn [_ x] x) coll (rest (range)))))

Sy Borg17:01:22

so may it be considered ok also - (defn mycount [s] (reduce + (for [i s] 1)))?


note that collections often define their own version of count that’s optimized for their type, so in production code no, but 4clojure yes

Sy Borg17:01:17

sure thanks, guys


stylistically I think you should always pass reduce an initial value

✔️ 15

although in the case of + you’re lucky it also has a 0-arg


why would you pass the init value if it is identical to the 0-arg call of f? I don't see the benefit of being extra verbose in that case


the 0 arg version is only called if the collection is empty


passing in a 0 value at the beginning helps you write the function being reduced as a proper monoid sum


ah, I didn't realize the 0-arg was only called given an empty collection. I thought it was always called if no initial value was given. Makes sense!


for some newer variants of reduce I believe it is always called if there is no init

👍 5

maybe transduce does that


I think I was under this impression from fold


Yeah, transduce always calls (f) to get an init. Contrast that to what happens when reduce has no init (from the docstring).

If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
applying f to that result and the 3rd item, etc. If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments.  If coll has only 1 item, it
is returned and f is not called.
I've found this very error prone, so to avoid thinking about it it's better to just always supply the init.


from the other side of things, there are some clojure interfaces that allow for collections with a specialized version of reduce, or defined entirely in terms of reduce, and those interfaces (clojure.langReduce and clojure.lang.ReduceInit) are distinct and are different only on if you pass in the initial value or not, and usually implementing ReduceInit (where you pass in the initial value) is easier then implementing Reduce

Sy Borg18:01:03

again, #23 on 4Clojure - why not just into ()? instead of, for example, (fn [coll] (reduce conj '() coll))


using into () seems reasonable to me.


Very important question (:thinking_face:) here - need to know why I figure the doc for clojure.string/join and the implementation seems to have the parameters switched. Am I right or is there some hidden greatness?


source and doc string both seem to be ([coll] [separator coll])?

Lennart Buit21:01:32

str/join is multi arity, if you pass it one arg, its only a collection, if you pass it two, you pass both a seperator and a collection


what do you mean by switched?


I think he means flipped?


Yeah. Sorry. Trying to copy from Emacs to iPhone 😅


that's what i thought. but both source and doc show separator and then coll. (or just coll)


i never see coll then separator


interpose also has [sep coll], this seems to be the pattern all over

Lennart Buit21:01:01

is also great for partial application (def comma-joiner (partial str/join ","))


also, (interpose sep) gives you a transducer, which is nice


i had a question about something similar. the metadata can be modified to clarify the docstring beyond what the "source" docstring actually is


is that what spec uses?


Sorry. I really can’t figure out how to do the darn code markings in the chat...


noob.core> (clojure.string/join [1 2 3 4] ":") ":" noob.core> (clojure.string/join ":" [1 2 3 4]) "1:2:3:4"

Lennart Buit21:01:54

yeah, the last is correct, its seperator first and collection second


surround with back-ticks

Lennart Buit21:01:12

that the first does something is mere accident I would say


its similar to (+ :cow) happens to work

Lennart Buit21:01:06

yeah, mere accident 😛


ah but only cljs


clj has a cast in it


in the first one it uses [1 2 3 4] as the separator, but since you have only one character, it’s return as it is


Ok. Thanks. In that case I’m reading the doc wrong, I think.


(str/join [1 2 3 4] "foo")
"f[1 2 3 4]o[1 2 3 4]o"

Lennart Buit21:01:45

how does that even work


it just calls str on the separator

Lennart Buit21:01:58

strings are not seq right


they are seqable


Ah. Hum. So the doc describes two different arity implementations? (It took a while to sink in 😋)


two different arities


Thanks. All is well in the world again. Back to noob.core 😀


Being on guard about undefined things silently working is new for me. Silently failing is one thing, but... hmm. Very philosophical experience, getting into Clojure.


@mattias504 this is not undefined behavior


Right, thanks. Had to double check the history, you already explained. Thanks for the patience!


Can anyone point me towards more good, non-trivial open source projects that have rest APIS? I have been looking at and so far.