Fork me on GitHub
#beginners
<
2020-04-30
>
Oz00:04:39

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.

noisesmith00:04:05

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

noisesmith00:04:26

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

noisesmith00:04:56

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

Oz00:04:49

Thank you, I will try to do without the do

noisesmith00:04:39

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

😂 1
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?

andy.fingerhut05:04:46

@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 https://clojure.org/guides/equality

Chris K11:04:07

Thxs I'll look into it

andy.fingerhut14:04:54

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

dpsutton00:04:04

check (doc ==)

dpsutton00:04:12

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

noisesmith00:04:22

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

noisesmith00:04:32

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

seancorfield00:04:44

@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')
user=>
and the source of it shows it calling into clojure.lang.Number

seancorfield00:04:43

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

hiredman01:04:05

likely cljs not clj

dpsutton01:04:48

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

hiredman01:04:08

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

seancorfield01:04:53

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!"}

dpsutton01:04:58

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

dpsutton01:04:21

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

dpsutton01:04:38

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.

👍 1
noisesmith01:04:24

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.

noisesmith01:04:57

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

noisesmith01:04:20

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.

noisesmith01:04:44

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

noisesmith01:04:07

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.

jsn08:04:20

@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.

Oz08:04:57

@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.

jsn08:04:47

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

jsn09:04:49

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

jsn09:04:57

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

jsn09:04:17

cf.

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

Oz09:04:56

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

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

Oz09:04:47

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

Oz09:04:19

There is one called replete for anyone interested :)

jsn09:04:47

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

Oz09:04:09

cool, thank you for explaining simple_smile

hindol11:04:03

If I have code like this,

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

EwaTrzemżalska11:04:23

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

noisesmith11:04:17

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

noisesmith11:04:08

@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

teodorlu11:04:47

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

hindol11:04:00

Okay, then I will use let as well.

noisesmith11:04:37

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

👍 1
teodorlu11:04:06

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

teodorlu11:04:03

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!.

hindol11:04:03

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

👍 1
teodorlu11:04:36

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 !

noisesmith12:04:23

and read and println and ...

Viktor13:04:24

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

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

manutter5113:04:07

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.

Viktor13:04:13

Okay, got it , thank you!

Oz13:04:56

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?

alexmiller13:04:09

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

Oz13:04:07

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

alexmiller13:04:23

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

noisesmith14:04:43

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

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

Eugen14:04:15

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

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

Eugen14:04:36

I get Unsupported binding form: :a

Eugen14:04:21

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

manutter5114:04:51

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

noisesmith14:04:27

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

Eugen14:04:55

well, the code as it is does not work

Eugen14:04:03

please ignore the ; comments

dpsutton14:04:08

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

dpsutton14:04:19

the answer is do not call destructure at all

dpsutton14:04:23

pretend that doesn't exist

noisesmith14:04:36

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

👍 1
dpsutton14:04:40

but not clear what you are trying to achieve

Andy Heywood14:04:12

I think the code as pasted is fine?

Eugen14:04:21

I'm trying to learn how to destructure function parameteres

noisesmith14:04:38

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

Andy Heywood14:04:49

yeah exactly 😄

Eugen14:04:01

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

Eugen14:04:56

the error message in repl could be more helpful

noisesmith14:04:51

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

noisesmith14:04:56

that's often enlightening

dpsutton14:04:57

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

noisesmith14:04:37

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

Eugen14:04:33

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

Eugen14:04:52

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

Eugen14:04:11

thanks for your time

👍 1
alexmiller16:04:38

that seemed surprising to me :)

noisesmith16:04:02

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

alexmiller16:04:08

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

j18:04:46

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?

noisesmith18:04:13

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

noisesmith18:04:25

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

alexmiller18:04:47

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

alexmiller18:04:04

then invoke -main

j18:04:47

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

alexmiller18:04:40

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

noisesmith18:04:02

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

j18:04:58

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

Marcus18:04:06

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?))

alexmiller18:04:02

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

Marcus18:04:39

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

Marcus18:04:57

(nillable (complement str/blank?))

Marcus18:04:17

and (nilable string?)

alexmiller18:04:29

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

alexmiller18:04:17

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

Marcus18:04:39

Thanks. I'll try that out 🙂

alexmiller18:04:40

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

alexmiller18:04:02

given that it's both common and cumbersome

Marcus18:04:10

Well there you have my little contribution to clojure spec 😉

alexmiller18:04:30

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

alexmiller18:04:42

and the generator

alexmiller18:04:03

I think the example above should gen

Marcus18:04:26

Yes. Worked fine 🙂

Marcus18:04:40

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?

didibus18:04:55

Use get-in

Michael W18:04:06

So 2 get-in?

didibus18:04:36

Ya, one call to get-in for each

didibus18:04:01

You can also use destructuring if you prefer

pyry19:04:53

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

didibus19:04:08

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

didibus19:04:39

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

didibus19:04:35

I also prefer to use -> over get-in

pyry19:04:33

Reasonable points for sure.

didibus19:04:48

`(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?

noisesmith19:04:58

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

didibus19:04:53

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

😛 2
noisesmith19:04:37

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

didibus19:04:35

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

didibus19:04:52

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

noisesmith19:04:27

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

ghadi19:04:32

people get enamored with juxt

noisesmith19:04:49

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

ghadi19:04:50

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

didibus19:04:42

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

noisesmith19:04:36

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'

😂 3
Cameron20:04:33

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?

noisesmith20:04:51

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

noisesmith20:04:33

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

noisesmith20:04:26

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

noisesmith20:04:35

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

noisesmith20:04:24

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...)

noisesmith20:04:49

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

noisesmith20:04:12

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

noisesmith20:04:03

eg.

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

noisesmith20:04:17

which shows I get clojure via a jar in m2

noisesmith20:04:32

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

noisesmith20:04:26

so your checkout isn't set up

Mario C.20:04:00

Hmm its because its looking into the .m2 folder?

noisesmith20:04:05

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

noisesmith20:04:16

I know that because the URI is a jar URI

noisesmith20:04:30

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

noisesmith20:04:01

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?

noisesmith20:04:32

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

noisesmith20:04:11

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

noisesmith20:04:31

@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

noisesmith20:04:31

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

noisesmith20:04:20

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

Cameron21:04:44

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`

👍 1
robertfw23:04:17

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.

robertfw23:04:52

this did exactly the trick. thanks 🙂

robertfw23:04:16

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

Darin Douglass23:04:50

alter-var-root is a thing too

noisesmith23:04:44

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