Fork me on GitHub

Hi, Joker says my do blocks are redundant, what does it mean?

Hi, Joker says my do blocks are redundant.
What does it mean?
(defn request-rows
  [range robot]
  (loop [i range]
    (when (> i 0)
      (do (tags/request-tag (str "ROBOT_" (:id robot) "_ROW_" i "_FISH_CELL_ID") 6000))
      (for [feeder (:feeders robot)]
        (do (tags/request-tag ((str "ROBOT_" (:id robot) "_ROW" i "_AMOUNT_LEFT_TO_FEED_"(:id feeder))) 6000)))
      (recur (dec i)))))
Blessed be you.


@ozfraier many clojure macros (including loop, fn, and when,) contain an implciit do


@ozfraier also do with only a single form in it is always redundant


(do x) can be replaced with x, in any case where (do x) is valid


Thank you, I will try to do without the do


on that note, try is usually not needed either :D

😂 4
Chris K00:04:57

Question about clojure's == equality test. so I get that it is equality check with an additional type check, but when I do:

(== 1 1.0) ;;true
(== "X" \X) ;; false
So does clojure consider all the number related data types as same type? But not for strings and chars?


@sunchaesk == is not exactly = with additional type checks. For example (== 0 0.0) is true, but (= 0 0.0) is false. There are three "numeric categories" that are never equal to each other for = . See

Chris K11:04:07

Thxs I'll look into it


That article is for Clojure specifically. And may differ from Clojurescript in ways I do not know


check (doc ==)


and i'm not trying to be snippy. i honestly didn't know the answer but trying to show you how i knew where to look to find an answer

Chris K04:04:01

Oh alright. I was just reading the examples on the clojure community website and I didn't quite get the documentation there, BUt still thxs for your answer


there's nothing in clojure or java that considers "X" and \X the same


you could make your own test function of course #(= (str %1) (str %2)) or more generally #(apply = (map str %&))

Chris K04:04:08

Oh alright. I was just reading the examples on clojure docs website, and just had this question while looking at the equality test examples. thxs for answering


@sunchaesk I don't get false, I get an exception from that second comparison:

user=> (== "X" \X)
Execution error (ClassCastException) at user/eval143 (REPL:1).
class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')
and the source of it shows it calling into clojure.lang.Number


As @dpsutton says, if you look at (doc ==) it says that it works with "nums" so I'm not sure how/where you're running that == between a string and a character?

Chris K04:04:10

Oops I meant exception. No I wasn't using any of the codes but I was just reading the examples on the clojure docs website and just had this question

Chris K04:04:13

Thxs for answering tho


likely cljs not clj


then even easier i suppose > Returns non-nil if nums all have the equivalent > value, otherwise false. Behavior on non nums is > undefined.


Yeah, same thing, just explains the lack of an exception


Ah, yeah, cljs having different behavior to clj makes sense(!).

Rob Aguilera01:04:27

Hello 👋, first time poster. Working my way through Clojure for the Brave and True and confused on the example below. Why does the vector need the extra braces?

(into {:favorite-emotion "gloomy"} [[:sunlight-reaction "Glitter!"]])
; => {:favorite-emotion "gloomy" :sunlight-reaction "Glitter!"}


(into existing-map [ [key-1 val-1] [key-2 val-2] ]


putting space around them to make them not bleed together. its a vector. each element of which is a two element vector, key and value


so the vector doesn't need extra braces, its a vector of two element vectors

Rob Aguilera01:04:37

Thanks for the quick reply! Clears it up perfectly.

👍 4

the way maps work, the input could have been [{:sunlight-reaction "Glitter!}] as well - you don't see this form as often though

Rob Aguilera01:04:24

Interesting, I feel like I see that all the time in JavaScript. An array of objects.


right, a map accepts either a map, or a vector (treated as a key-value pair, must be exactly length 2)


there's also assoc which takes a freeform series of keys and values

Rob Aguilera01:04:42

That's the difference and which is pretty mind bending for a humble JS dev learning Clojure.


user=> (assoc {:a 1} :b 2 :c 3)
{:a 1, :b 2, :c 3}


we use maps a lot, so we have many features and conveniences around them

Rob Aguilera01:04:17

Oh, I didn't know about assoc yet, wow.


@ozfraier even if when and for didn't have implicit do , your code still doesn't make much sense to me. Why are you wrapping single expressions in explicit do 's? do is only useful for grouping multiple expressions.


@jason358 I refactored a do block that had multiple expressions into a for block, Also, I felt like I needed the do to run the request-tags function immediately for side effects, but that may be voodoo programming.


"immediately" as in "not lazily"? do doesn't do anything for non-laziness, iirc


Clojure is not a lazy-by-default language anyway, it doesn't make sense


if your expr is in "lazy" context, wrapping it in do still does nothing, your do will now be in the same "lazy" context



user=> (do (for [a [1 2 3 4]] (println a)) 1)
user=> (do (for [a [1 2 3 4]] (do (println a))) 1)
see, println isn't evaluated either way, because it's in for 's lazy sequence that doesn't get forced


I think I confused do an doall in that case. What about

(doall (for [a [1 2 3]] (println a)))


I wonder if there is a clojure repl for the smartphone :P


There is one called replete for anyone interested :)


doall does the trick, but what you are really looking for is doseq 🙂 as in (doseq [a [1 2 3]] (println a))


cool, thank you for explaining simple_smile


If I have code like this,

{:x (f)
 :y (g)}
Is f guaranteed to run before g?

Ewa Trzemżalska11:04:23

I dont think so, cause at map order is random.


yeah, before (f) runs, it is read (to create the symbol inside a list, which eval then compiles and runs), and the reader is what creates the hash map and is allowed to change the order


@hindol.adhya on the other hand: (hash-table :x (f) :y (g)) will always call f before g, since the map is created after eval


@hindol.adhya I would not rely on that. I would go for (let [x (f) y (g)] {:x x :y y})


Okay, then I will use let as well.


@teodorlu it's fine to dislike it on style grounds, but it's an explicit promise that args are evaluated left to right

👍 4

@noisesmith I was referring to @hindol.adhya’s example


Sidenote: if you rely on the order of f and g, they probably have some side effect. I'd consider renaming to f! and g!.


@teodorlu I have considered that. They both read from a reader. I ended up not adding ! mostly due to preference.

👍 4

Yeah, in that case I might agree. Hard to come up with a general rule. Clojure internally isn't 100 % strict on this either, both slurp and spit are missing !


and read and println and ...


Hello everyone, I wanted to use `comment` macro  as java `/ /` analog

but I got
1. Caused by java.lang.NumberFormatException
Invalid number: 1todo
Is there a way I can workaround this ?


I don’t think there is. The reader has to read the complete (comment ... ) form, and it can’t do that if there’s an error inside the form.


Okay, got it , thank you!


If I have a collection of a collection of strings, let's say

[["hello" "world"]["hello" "mars"]["hello" "moon"]]
And I want to call str on them so I get a collection of strings instead
'("hello world" "hello mars" "hello moon") ;;I don't really mind the type of collection
How do I get there?

Alex Miller (Clojure team)13:04:09

(map #(clojure.string/join " " %) [["hello" "world"]["hello" "mars"]["hello" "moon"]])


Cool 🙂 actually, the space was not really part of the problem 😅 I came up with (map #(apply str %) [["a" "b" "c"]["d" "e" "f"]])

Alex Miller (Clojure team)13:04:23

yep, that'll work too if you don't need the space


@e2d4f6 I use #_ instead of comment, it's more powerful

user=> #_(1todo)
it consumes the next item without caring as much about syntax, and doesn't even return anything (where comment returns nil) edit: this was something nrepl accepted but a real repl does not


user=> #_(1todo) 
Syntax error reading source at (REPL:1:9). 
Invalid number: 1todo 
Syntax error reading source at (REPL:1:10). 
Unmatched delimiter: ) 
user=> *clojure-version* 
{:major 1, :minor 10, :incremental 1, :qualifier nil}


@noisesmith Unfortunately it does not work on my side. What clojure version do you use ?


*clojure-version* evaluates to {:major 1, :minor 10, :incremental 1, :qualifier nil}


also, that error message makes me think you are using nrepl, I bet what you are seeing is an nrepl or reply lib bug


if you are using lein, try lein run -m clojure.main instead of lein repl - that won't start a server, but will give you a direct repl, and I bet the form works there


I tried running: - clj - cider - lein run -m clojure.main unfortunately the same result

Syntax error reading source at (REPL:1:9).
Invalid number: 1todo


oh... maybe I had it backward and only my nrepl accepts that :/


yeah, sorry, #_ doesn't accept invalid forms, it looked like it did due to a corner case nrepl behavior for me locally


okay, thank you very much for helping !


it doesn't even care about classic issues like unbalanced hash-maps:

user=> #_{1todo}
and just like comment , #_ does inline / multi-line commenting
user=> #_{1todo} 42


hi, I don't know what is wrong with this:

  (defn destructure [aa] (println aa)
    ;(let [{:keys [c d]} :as aa]
    ;  (println c " " d " ")
    ;  )
  (let [xx {:a 3 :b 4}] (destructure xx))


I get Unsupported binding form: :a


it's realy frustrating since I am reading the docs and I can't figure out whare I'm going wrong


In your let, you don’t want the :as there.


yeah, the :as belongs inside the hash-map, but aa is already bound, so you don't want it at all


well, the code as it is does not work


please ignore the ; comments


(let [xx {:a 3 :b 4}] (destructure xx)) i think is the only code under discussion?


the answer is do not call destructure at all


pretend that doesn't exist


another issue is clojure.core/destructure exists and wouldn't like your map most likely

👍 4

but not clear what you are trying to achieve

Andy Heywood14:04:12

I think the code as pasted is fine?


I'm trying to learn how to destructure function parameteres


it would be, if that destructure was the one he was calling when he called destructure :D

Andy Heywood14:04:49

yeah exactly 😄


my goal is to have (defn [a b {:keys [x y]}) (println ))


the error message in repl could be more helpful


you can use *e to see the full previous error, including stack trace etc.


that's often enlightening


oh i'm sorry. i didn't put together your destructure . my mind went to clojure.core/destructure. that's why i said pretend that doesn't exist. i'm sorry for my confusion


eg. the stack trace would have revealed that clojure.core/destructure was called, and not the one in your ns, most likely


ok, so thanks to your help I figured it out - I was loading the whole file in repl - but my function was inside (comment ) so it was not created


that is why it failed and was harder to get to the cause


thanks for your time

👍 4
Alex Miller (Clojure team)16:04:38

that seemed surprising to me :)


haha I saw you almost replying before, I am glad I got the chance to correct the misinfo

Alex Miller (Clojure team)16:04:08

#_ works by reading then discarding so seemed like it still has to read just as much as comment

Yuriy Malenkiy17:04:25

Hey everyone. Could I please get some guidance on how to organize projects and deploy them to a cloud platform? For a simple scenario, I would like to have a client (re-frame) and server (some REST Api framework) projects. AWS Beanstalk SE platform seems to assume these would be bundled/deployed as one application which isn’t ideal - it would be nice to treat these as components that can be updated separately. Beanstalk Docker adds additional overhead. Any help would be very appreciated. I’m open to other cloud platforms. Thank you!


if you don’t intend to bundle the client and server, how would you like to serve the client resources (html/css/js/etc.)?

Yuriy Malenkiy18:04:15

I don't exactly know 🙂 This may be part of the question. Beanstalk can serve static files but I don't have it working nor do I know if this is a good way to do it.


if you can serve them as static files, that’s typically the way to go


i’ve only used beanstalk with python. are there clojure oriented docs? otherwise i would follow the java focused docs and deploy as an uberjar

Yuriy Malenkiy18:04:23

It's, and I'm experimenting with uberjar. In Beanstalk, server and client would effectively be bundled/uploaded together (unless I create multiple Applications). Perhaps this would be easier in EC2? Is it more typical to bundle such components in one project? Just trying to understand what the options are and what's more common. Thank you.


if you have two repos, you could create two environments


if they’re in the same repo, then I would just bundle them together unless there’s a compelling reason not to


it kinda depends on how much experience you have with devops


I’ve heard good things about heroku, but I’ve been doing devops for a long time so I prefer something more flexible like beanstalk


for hobby projects, I use digital ocean and just deploy an uberjar. the uberjar also serves the static assets needed on the clientside


it means that my dev and production environments are very similar


the reason I use beanstalk for production is that it provides a deployment process pretty similar to one I would write myself out of the box. it’s fairly flexible and has sane default for auto scaling, rolling deployments, security groups, load balancing, etc.

Yuriy Malenkiy19:04:57

This is helpful. Thank you.


are you required to run on AWS?

Yuriy Malenkiy19:04:37

No. This is for personal project(s). Just trying to figure out cloud deployment.


yea, for personal projects, I just use digital ocean and deploy an uberjar. (by deploy, I mean I rsync the jar to the server and then start the server in an emacs daemon).


if you’re open to other options, it may be worth asking the chat what they recommend for deploying personal projects (without narrowing it down to AWS). I seem to recall heroku being one of the easier options to get started with.

Yuriy Malenkiy19:04:33

I am open to other options, and re-phrased original post slightly. Thanks!


I'm using clj -m namespace-name to call the -main function from the command line. It seems to not just run -main, but all functions in the namespace? Is there a way to run just -main?


clojure doesn't have a "compile only" mode, unlike many other languages. the solutionis to put everything that shouldn't run on file load into defns, and bind them with let inside main


(or some other more sophisticated variation, but that's the basic one)

Alex Miller (Clojure team)18:04:47

clj -m will load the file and evaluate every top level form


I'm calling a 3rd party library that seem to generate side-effects that I'd like to not execute until I call it in -main. I'm using def instead of defn on the 3rd party lib. I'm not sure how to best go about this

Alex Miller (Clojure team)18:04:40

a) don't use def or b) use delay


don't use the lib, or change it to not put side effects in def, there's no other solution without forking the compiler(?)


I think I understand now, thanks @noisesmith and @U064X3EF3!


hello all! I am working with clojure spec. Does anyone know the best way to express s/def for a value that must either be a non blank string or nil? For then non-blank part I have (s/and string? (complement str/blank?))

Alex Miller (Clojure team)18:04:02

you could wrap that in (s/nilable ...)


yes.. thought about that but that but then I must wrap both predicates?


(nillable (complement str/blank?))


and (nilable string?)

Alex Miller (Clojure team)18:04:29

I would do (s/nilable (s/and string? (complement str/blank?))) I think

Alex Miller (Clojure team)18:04:17

nilable is optimized to cover the nil case so it's better at the top level


Thanks. I'll try that out 🙂

Alex Miller (Clojure team)18:04:40

I've come to believe it would probably be useful to have a pred/spec specifically for non-blank strings

Alex Miller (Clojure team)18:04:02

given that it's both common and cumbersome


Well there you have my little contribution to clojure spec 😉

Alex Miller (Clojure team)18:04:30

well it wouldn't be that - we can optimize the impl

Alex Miller (Clojure team)18:04:03

I think the example above should gen


Yes. Worked fine 🙂


love your talks by the way 🙂

Michael W18:04:57

(def d {:one {:two {:three 3 :four 4 :five 5}}})

Michael W18:04:13

How to get :three and :five from that?


Use get-in

Michael W18:04:06

So 2 get-in?


Ya, one call to get-in for each


You can also use destructuring if you prefer


Something like (-> d (get-in [:one :two]) (select-keys [:three :five])) should work as well.


With destructuring: `(let [{{{:keys [three five]} :two} :one} d] [three five])`


But my personal favourite is just two get-in with a let, or a let where you get the inner map and then use that to extract three and five using keywords. Simple and readable


I also prefer to use -> over get-in


Reasonable points for sure.


`(let [two (-> d :one :two) three (:three two) five (:five two)] [three five])`

Michael W19:04:55

Where did inner come from?

Michael W19:04:01

Is that arbitrary?


there's also (-> d (get-in [:one :two]) ((juxt :three :five))) though most people hate to see (( inside ->


Clojure, where "there's always many and --preferably even more-- non obvious ways to do it"

😛 8

if that weren't true, what would we do with all the unused capacity for aesthetic opinions?


Make up a bunch of useless design patterns :man-shrugging:


Although all ways will be obvious to you once you're told about them hehe

Michael W19:04:59

Ok thanks for that juxt is going to solve some other stuff for me too, what a useful but non-obvious function, no idea it was there


I find it pleasing, writing a function that has juxt in it is almost like writing a haiku


people get enamored with juxt


which is reason to be suspicious of it, of course :D


has a tendency to be overused when you first discover it 😉


It's way more fun to use a juxt, but much more pleasant to read code using a let


I'm reminded of that old copypaste which I can't seem to find now, where the beginner haskell programmer uses '1 + 1', the grad student uses a Peano arithmetic implementation, the phd does the Peano calculation in the type checker, and the expert uses '1 + 1'

😂 12

meanwhile, juxt is like the first 'functional programming thingy' I ever remember learning, and I just remember thinking 'ok, so this this is it, juxt, map, reduce, monads...', I even implemented it in PHP for some ghetto web 'framework'. And then never used it again

Mario C.20:04:34

Is it possible to use lein checkouts without having to restart the repl everytime?


once the checkout is set up, you should be able to reload the library files as you edit them


using the :reload or :reload-all arg to require, or whatever shortcut your editor offers


even without a checkout load-file will redefine the ns from a file, and the checkout makes sure you see the changes if you restart too

Mario C.20:04:36

I must not be doing something right because the changes are not taking effect


sometimes what I do is insert a deliberate error (like a garbage token at the top of the file) to prove whether the file is loaded from what I'm editing or not

Mario C.20:04:30

I have some printlns that I am doing to see if the changes took effect but the outputs is still the same


but there are things that would prevent the printlns if they are not at the top level (eg passing a function isntead of var to a long lived process, attempting to redefine a defmulti...)


@mario.cordova.862 what does (io/resource "m_ns/my_file.clj") return - it will show the actual path that clojure uses when reloading


(that is, the path to your ns, without the containing directories)



scratch=> ( "clojure/core.clj")
#object[.URL 0x25d61f8d "jar:file:/Users/justin.smith/.m2/repository/org/clojure/clojure/1.10.1/cloju


which shows I get clojure via a jar in m2


with a checkout that's set up properly, it should return the path to the file on disk in the original project

Mario C.20:04:55

The dependency file I want reload is called corefuncs.clj and its namespace is myns.corefuncs and when I call ( "myns/corefuncs.clj") I get #object[.URL 0x3e4eea1 "jar:file:/path/.m2/reporistory/path/myns/corefuncs.clj]

Mario C.20:04:01

And then it returns the actual file contents


so your checkout isn't set up

Mario C.20:04:00

Hmm its because its looking into the .m2 folder?


it actually has jar!/... in the middle there


I know that because the URI is a jar URI


you can't hot reload from the jar, because you didn't change the jar


what the checkout should be doing is setting your classpath so asking for that file gives you a path to the file on disk, and it's not doing that

Mario C.20:04:43

I created a checkout dir and in there I ran ln -s ~/Projects/my-dep

Mario C.20:04:05

Shouldn't that have set it up?


oh wait, lein now has built in checkouts separate from that plugin...


so the symbolic link should have worked (after just one initial restart)


@mario.cordova.862 one gotcha, the magic dir is checkouts not checkout

Mario C.20:04:48

I did create the checkouts with the write name, made a typo up there


and you restarted the repl at least once since adding the link in checkouts? if so I can't tell you what the issue might be


another gotcha is that this checkouts scheme only works for other lein projects

Mario C.20:04:15

Yea the dependency is a lein project as well

Mario C.21:04:58

Hmm going to keep searching, thanks though appreciate the help!

Mario C.21:04:12

Figured it out

Mario C.21:04:18

It was the way I was starting my repl


damn I was just about to chip in ahahaha

Mario C.21:04:45

> Make sure not to override the `base` profile while using checkouts. In practice that usually means using `lein with-profile +foo run` rather than `lein with-profile foo run`

👍 4

is it possible to use with-redefs to wrap a function? that is, can I refer to the original function that is being replaced, from the function I am replacing it with?

Darin Douglass23:04:30

Yep, just store a reference to the original function in a let. I would give an example but clojure + phone = not good.


this did exactly the trick. thanks 🙂


gotcha, that's enough to go on. thanks 🙂

Darin Douglass23:04:50

alter-var-root is a thing too


if you refer to a var by name in a function, it's resolved at runtime each time it's used, by using a different name in a let, you "capture" the value at one point in the execution