Fork me on GitHub
#beginners
<
2021-08-26
>
ChillPillzKillzBillz08:08:41

I've been playing with clojure for a while now... so I would think I would know the answer to my question by now... but no I don't! When I execute the following:

clj꞉hello-world꞉> (prn "hello")
"hello"
nil
I understand the "hello".... what is the nil for and how to get rid of it?

flowthing08:08:25

nil is the return value. prn is a function that prints its argument(s) and returns nil. You can't get rid of it. I'm curious why you'd want to, though?

dharrigan09:08:15

tbf, when I was learning Clojure in the early days, I had precisely the same question. I couldn't understand why a nil was being output when I was simply "running" a function (coming from a java background, this confused me a lot!). It wasn't until later, with the help of others, that I learnt that everything is evaluated in Clojure and everything returns a value, it just that sometimes that value is nil and with the help of nil-puning, those nils are handled appropriately. So, @U022LK3L4JJ everything is fine, a nil is to be expected, it just means there's no "return" from the function, like a map, or a literal, or a vector or whatnot, the function evaluated, it did what it needed to do and then finished 😉

dharrigan09:08:34

No need to fear the nils 🙂 Not like in Java etc., where it's often a source of NPE's. Clojure uses a technique called nil-puning (which is a LISPy thing) to handle nils and reduce the chances of a NPE.

ChillPillzKillzBillz10:08:42

Thanks for that @U11EL3P9U!! @U4ZDX466T: I got stuck on this because I was trying to get a simple test function evaluated using lein test... like...

(deftest hello-world-test
  (is (= "hello" (hello-world/hello))))
... and because of the nil it kept on failing even though I was printing the "hello"... in the end instead of using prn I had to use str and then the nil disappeared!!

dharrigan10:08:13

Yes, since as @U4ZDX466T alluded to, the prn function is a function that outputs (to the console) a string. It does that, and nothing else (i.e., it has a side-effect, it mutates external state, with that state being the console 🙂 ). Once it has done outputting to the console, it returns nil (since it doesn't really know if that was successful or not, ultimately it just passes the request along all the way to the kernel for it to do something. Whether or not a string actually gets displayed on the console isn't it's concern!). So it returns nil. The str function, on the other hand, calls .toString() on the something, which does return a string, i.e., the str function evals and returns a String.

Cora (she/her)11:08:42

also the function that you're looking for, for testing that it prints something, is with-out-str

sova-soars-the-sora11:08:15

from with-out-str at http://clojuredocs.org

;; Instead of printing, the following will place the output normally
;; sent to stdout into a string.
neato.

noisesmith18:08:56

the core misunderstanding here is a common one when starting with the REPL - returning something and printing something look the same, because the Read Eval Print Loop implicitly prints all return values

noisesmith18:08:22

all return values are printed, not all prints are return values :D

eyalch12:08:15

I want to reverse words which have at least 5 characters. Is there a shorter way?

ghadi12:08:16

Look at cond->

emccue12:08:48

its not shorter but

(defn reverse-if [pred val]
  (cond-> val
    (pred val) (str/reverse)))

(for [name ["welcome" "to" "clojure"]]
  (reverse-if #(>= (count %)) name))

emccue12:08:56

reverse-if might be more legible

emccue12:08:27

or just using for instead of map

emccue12:08:02

(for [word ["Welcome" "to" "clojure"]]
  (if (>= (count word) 5)
    (str/reverse word)
    word))

emccue12:08:21

since there is nothing "wrong" with how you did it it all comes down to personal taste

sova-soars-the-sora12:08:49

I like the map implementation. Can you use when with reduce in this case?

sova-soars-the-sora12:08:06

Maybe reduce don't make no sense.

eyalch12:08:56

@U050ECB92 Do you mean like this?

v3ga15:08:57

I’m trying to pull values from a few keys here… what would be a good way to do this so that I can loop through or map over the values? For lack of better words. How would I go about placing ALL of my retrieved books titles and authors in a list on a website. Something similar to “for item in items”?

alexmiller15:08:58

if you want maps out, select-keys. if you want vectors, juxt

alexmiller15:08:41

(select-keys {:author "Haruki Murakami",
  :description "Really cool and something we should read",
  :genre "fiction",
  :id 3,
  :own true,
  :pages 500,
  :reading "Yes",
  :title "The Commendatore"}
  [:author :title])
{:author "Haruki Murakami", :title "The Commendatore"}

((juxt :author :title) {:author "Haruki Murakami",
  :description "Really cool and something we should read",
  :genre "fiction",
  :id 3,
  :own true,
  :pages 500,
  :reading "Yes",
  :title "The Commendatore"})
["Haruki Murakami" "The Commendatore"]

📚 3
alexmiller15:08:23

^^ examples on a single map

v3ga15:08:14

@alexmiller oh! Ok, that works. Much cleaner. (map (juxt :title) (db/query (db/get-all-books)))

alexmiller15:08:16

well if you've only got one attribute, then just (map :title ...)

alexmiller15:08:39

really depends what you want out

v3ga15:08:18

Alex, no there’ll be more…but that gives me the general idea. I believe I can get what I want done now. Thank you 😛

Oleg15:08:05

Hi there! I run my code using lein run and get a syntax error, but error message is not clear. For example, I don’t see the line number where error happened. I don’t understand how to debug such errors and scary to think about more big application in Clojure.

Syntax error (ClassCastException) compiling at (/private/var/folders/8z/4psycrbn67d1wsb2sy41sjzm0000gn/T/form-init14777450300486844710.clj:1:126).
class java.lang.String cannot be cast to class clojure.lang.IFn (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')

Full report at:
/var/folders/8z/4psycrbn67d1wsb2sy41sjzm0000gn/T/clojure-9691563272031495588.edn
How to debug such errors?

alexmiller15:08:46

java.lang.String cannot be cast to class clojure.lang.IFn most likely means you are somewhere trying to invoke an expression with a string for a function, like ("abc" 123)

alexmiller15:08:11

if you look at the full report /var/folders/8z/4psycrbn67d1wsb2sy41sjzm0000gn/T/clojure-9691563272031495588.edn it should tell you where that's happening

dpsutton15:08:55

the default for exceptions like this was changed to put them in a temporary file which is what is reported, rather than the stack trace

Oleg16:08:38

Full report points to Line 1, Column 126

Oleg16:08:08

seems the source is flattened in the single line

Oleg16:08:04

is it a regular thing for Clojure? Or may be there is some compiler flag to make it a bit easier ?

alexmiller16:08:37

generally, the way to avoid debugging things by running your program at the command line, and instead use a REPL-connected editor to write and debug your code interactively

👍 3
Oleg16:08:20

thanks, i will try!

Jimmysit016:08:02

If I have an unused binding in a defn what can I do, like what do I replace it for?

dpsutton16:08:47

what do you mean?

Jimmysit016:08:30

If I have (defn something [& unused-binding]) what can I replace it for?

dpsutton16:08:19

you can use an underscore (defn foo [_] ...)

dpsutton16:08:52

i'm a big fan of using (defn foo [_the-thing] ...) a name but prefixed with an underscore to keep the context of the thing you are ignoring. Note you don't have to do anything

dpsutton16:08:55

there is no error here

dpsutton16:08:10

most likely just your linter complaining but you can heed its advice or not

Jimmysit016:08:54

Thank you, using _ worked! I didn't get any error but I got a warning about the unused binding

seancorfield16:08:14

From the linter, I assume? Clojure itself doesn't care.

Jimmysit016:08:24

I get a wrong number of args error even if I'm not using the binding. Replicating this on a repl gives me the same error, how does it work?

seancorfield16:08:09

Can you show the code? Sounds like you genuinely have the wrong number of arguments.

Jimmysit016:08:34

Yeah sure, give me a second

Jimmysit016:08:40

Wrong number of args (0) passed to: testing.core/-main

Jimmysit016:08:14

Like this it works, but I get the unused binding warning

seancorfield16:08:22

(defn -main [& _args] ..)

seancorfield16:08:47

That expects zero or more arguments. [_args] expects exactly one argument.

👍 3
Apple17:08:33

Any idea why clj fails while lein/project.clj seems ok?

{:deps {org.clojure/clojure            {:mvn/version "1.10.1"}
        incanter/incanter              {:mvn/version "1.5.7"}
        org.clojure/math.numeric-tower {:mvn/version "0.0.4"}
        me.raynes/fs                   {:mvn/version "1.4.6"}
        org.clojure/tools.cli          {:mvn/version "1.0.194"}}
 :paths ["src" "data"]}
(defproject cljds/ch1 "0.1.0"
  :description "Example code for the book Clojure for Data Science"
  :url ""
  :license {:name "Eclipse Public License"
            :url ""}
  :dependencies [[org.clojure/clojure "1.10.1"]
                 [incanter/incanter "1.5.7"]
                 [org.clojure/math.numeric-tower "0.0.4"]
                 [me.raynes/fs "1.4.6"]]
  :resource-paths ["data"]
  :aot [cljds.ch1.core]
  :main cljds.ch1.core
  :repl-options {:init-ns cljds.ch1.examples}
  :profiles {:dev {:dependencies [[org.clojure/tools.cli "1.0.194"]]}}
  
  :jvm-opts ["-Xmx2G"])

hiredman17:08:20

what do you mean by fails?

Apple17:08:31

this is the error "Error building classpath. Could not find artifact bouncycastle:bctsp-jdk14:jar:138 in central ()"

seancorfield17:08:42

Looks like a Maven relocation, which tools.deps does not support yet https://search.maven.org/artifact/bouncycastle/bctsp-jdk14

Apple17:08:41

gotcha. thank you!

seancorfield17:08:36

@alexmiller There's a JIRA issue for supporting artifact relocation?

alexmiller17:08:36

sorry, I'm in debian clojure bof right now, but it's maybe the first TDEPS ticket

seancorfield17:08:46

https://clojure.atlassian.net/browse/TDEPS-8 (which actually references that specific artifact!)

alexmiller17:08:14

yep, that's the one

alexmiller17:08:14

there are a couple things that are going to require work in similar areas - relocations, support for libs in groupIds that start with a number, etc. at some point I will work on those as a unit

alexmiller17:08:39

despite its age, I am aware of this ticket and will get to it eventually

alexmiller17:08:20

as you can see by the votes, surprisingly few people have hit this (as relocations are relatively rare)

agigao18:08:42

Hey Clojurians, any pointers how to run tests sequentially (in a namespace)?

agigao18:08:02

One test depends on the result of another (the result is generated from 3rd party API)

dpsutton18:08:09

put them in the same deftest. ideally break this stateful requirement

6
Colin P. Hill18:08:06

Yeah, if one test depends on another test executing first, what you really have is one test with multiple act/assert stages.

agigao19:08:15

Yeah, 2nd option is definitely better, but I’ll stick with the first for now :P Thanks @dpsutton

lread23:08:20

If you have to do this, you can still describe your subtests within a deftest with testing blocks:

(deftest my-test
  (testing "my sanity"
    (is (= 4 (+ 2 2))))
  (testing "your sanity"
    (is (= 4 (* 2 2)))))

lread23:08:56

There is also test-ns-hook which allows you to control the order your tests are called. This is maybe a bit esoteric but you can read about in the https://clojure.github.io/clojure/clojure.test-api.html. Unless you really, really (really?) need it, I wouldn’t use it.