Fork me on GitHub
#beginners
<
2020-11-10
>
Jim Newton11:11:27

is there a way to have the reader insert the line number in what it read? E.g., (this is a line which contains .#(get-reader-line-number) the line number in it)

alexmiller13:11:33

Not to my knowledge

Buidler13:11:04

Why is `(seq x)` the idiomatic version of `(not (empty? x))`?

Ben Sless13:11:36

I'm not sure this is a satisfactory explanation, but empty? is (not (seq ,,,)), so (not (not (seq ,,,))) isn't very idiomatic.

practicalli_john13:11:20

You can see from the source code how empty? is actually implemented (not (seq coll)) https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L6206 So as mentioned above, the not's really cancel themselves out...

Buidler13:11:10

Ah, that does make sense. Should the same be used for strings or is (not (clojure.string/blank? x)) appropriate? I suppose (complement clojure.string/blank?) is actually more idiomatic in that case..

Ben Sless13:11:35

If I use it pervasively I define it as a function.

Buidler13:11:58

Since (coll? "x") is false, my understanding is that (seq "x") is not the appropriate way to check for a blank string and I should use clojure.string/blank? as I showed above. I get that if I use it often it should be defined as a fn in my code so as not to repeat it often.

practicalli_john14:11:59

Regular expressions will give you a lot of expressiveness when checking strings and are often used with re-seq, re-find and re-matches There is a simple example here https://github.com/practicalli/exercism-clojure-guides/blob/master/bob/src/bob.clj This is a useful regex cheatsheet https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet

Buidler14:11:08

Thanks guys.

Steven Katz14:11:08

I’ve written a little betting squares app as an exercise for learning Clojure. Is it appropriate to ask members of this channel to critique it? Looking for feedback on FP, idiomatic Clojure and code organization plus any other feedback. https://github.com/git4skatz/footballsquares

Darin Douglass15:11:50

Small things I notice: 1. load is almost never what you want. instead do something like (:require [footballsquards.score :as score] ...) in your ns` form. 2. i'd personally recommend using vectors rather than lists where a you just need a generic sequential thing. 3. case rather than condp for compile-time-known values 4. -1 seems like a really odd exit code 5. use (ns footballsquares.*) instead of (in-ns '*) 6. the are cases where you use the generic symbol coll to indicate multiple "types" of things: players, squares, charities. use distinct, descriptive symbols to avoid confusion generally (aside from some other smaller whitespacing things that are more preference than anything), things looks pretty good to me! good job!

phronmophobic18:11:46

you can always try the #code-reviews channel for feedback too

Jim Newton15:11:55

can someone help me understand why the nil? function uses (clojure.lang.Util/identical x nil) rather than (= x nil)

dpsutton15:11:48

checking (source =) it would hit (clojure.lang.Util/equiv x y) and does a bunch of work depending on the type of x, when it just needs to compare identical x nil

Jim Newton15:11:30

great so semantically it is the same?

Jim Newton15:11:51

i.e., there are no values which are = to nil without being identical to nil?

andy.fingerhut15:11:50

I cannot think of any values x such that (= x nil) is true but (identical? x nil) is false. nil in Clojure is the reference null in the JVM, which I believe is effectively a null pointer.

andy.fingerhut15:11:17

That does not change the warning that identical?is rarely what you want to use in Clojure.

dpsutton15:11:57

(= (reify Object (equals [x y] true)) nil) it seems you can get there but its your own shenanigans that do so

Jim Newton16:11:22

does the clojure compiler have a clojure specific optimizer which for example sees an explicit call to (= nil x) and compiles it as (indential nil x) ?

dpsutton16:11:07

do you suspect there is?

andy.fingerhut16:11:35

None that I am aware of.

alexmiller16:11:42

There is no Clojure optimizer

alexmiller16:11:50

The compiler mostly tries to make really obvious bytecode and then relies on hotspot to be an amazing dynamic optimizer with 100s of person years of work in it

sergey.shvets19:11:36

Hi, two noob questions on cljs.core.async. 1. Is it ok to read all available channel values inside the go-loop? I have a buffered channel where UI updates are posted and I would like to bulk it together. Code example:

(go-loop []
      (let [v        (async/<! b)
            all-vals (loop [vals [v]]
                       (let [next-v (async/poll! b)]
                         (if (some? next-v)
                           (recur (conj vals next-v))
                           vals)))]
        (js/console.log (str "Read " (count all-vals) " from channel") all-vals)
        (recur)))
1. It seems that I'm able to put 2 more messages than buffer size on a chan. Am I doing something wrong? thanks in advance!

chaow20:11:35

quick question: is it possible to do a with-redefs but for an atom? On the clojure page it says that the change to the variable is visible in all threads. Id like a thread safe way of doing this

alexmiller20:11:25

with-redefs is not thread-safe

alexmiller20:11:04

unclear to mean if you are talking about a var referring to an atom or using an atom as an alternative to vars

chaow20:11:10

Here is my problem: I have large code where I realize at the end of my logic we need to add a check that requires adding an argument in a whole chain of functions. I was thinking if it was possible to just have a global variable and then set that global variable in a thread safe manner as the value to be passed? But this doesnt sound right at all on second thought 😕

chaow20:11:21

any solution to this?

chaow20:11:51

say i have fn a, b, c, d. a calls b, b calls c, c calls d. I realize i need an argument on d, so i have to change all the callers of d, and all the callers of the callers of d etc. In my scenario, this branches out a lot, and it doesn't seem ideal to add an arg everywhere potentially hundreds of places.

alexmiller21:11:02

@chaow one answer is to use a dynamic var and binding but using a dynamic var has all the downsides of global state so it's worth thinking very carefully about whether that's a worthwhile tradeoff

chaow21:11:06

yes thats the issue, the code i need to run is parallel

chaow21:11:56

so it seems like there's not a lot of other solutions, seems a bit against the DRY priniciple to have the args repeated everywhere only for one single use 😞

phronmophobic21:11:23

I’m pretty sure there are other options, but it’s difficult coming up with them based on the provided description

phronmophobic21:11:11

if you have a chain of functions, it’s likely you can chain them without having them directly call each other.

alexmiller21:11:32

depending what you're doing, dynamic bindings work fine with some parallel constructs in clojure (many things like future will propagate dynamic bindings to threads)

seancorfield21:11:39

@chaow If you're still at that point in evolving your design, perhaps consider passing a hash map down the call chain instead of individual arguments? That way only the top-level code has to change (to assoc in the new field that function d needs).

seancorfield21:11:42

Also, REPL-driven development would tend to lead you to develop d first and you would probably have its arguments locked in before you write code that calls it. Not always true, of course, but working bottom-up via the REPL does help reduce this kind of refactoring churn in my experience.

2
chaow21:11:17

the thing is im working in a company handling a lot of real time data so i really think dynamic vars aren't really safe to use. It was hard to do this at the design level because I wasn't there yet, im a fairly recent hire. I talked to my manager and we ended up dropping the feature because of how much code change we would need to do, guess this is a cost/benefit trade-off situation :man-shrugging:

Stuart22:11:44

I'm finding the error messages in Clojure pretty tough to track down what is actually wrong.

(ns clj-codewars.scratch2
  (:require [clojure.string :as str]
            [clojure.set :as set]))

(defn number->digits [num]
  (->> num str (map (comp read-string str))))

(defn disarium-number [number]
  (number->digits num) ; ***** error here, num should be number! *****
  )

(disarium-number 89)
When I try to send (disarium-number 89) to the REPL, the error I get is
Error printing return value at clojure.lang.Util/runtimeException
EOF while reading,
Not even any indication of where the error actually occured... Is their a package that can give me better help here for finding where the actual errors are in my code?

Stuart22:11:14

Is this something that clj-kondo would help with?

borkdude22:11:08

@qmstuart your error is unfortunate, since num is a function in clojure core apparently

borkdude22:11:52

so what happens is that in disarium-number you are not using your argument number, but you are passing the function clojure.core/num to number->digits

borkdude22:11:07

clj-kondo will warn you about an unused argument in this case, so that could have been helpful

👍 1
dpsutton22:11:33

i suspect there's some unbalanced parens going on based on the error being "EOF while reading". And at that point there's largely not much clojure can do to make the error message better

borkdude22:11:53

@dpsutton the function clojure.core/num is stringified and then read as code.

dpsutton22:11:45

ah i totally glossed over that read-string was involved. good catch

borkdude22:11:46

user=> (map (comp read-string str) (str clojure.core/num))
Error printing return value at clojure.lang.Util/runtimeException (Util.java:221).
EOF while reading
(c l o j u r e . c o r e $ n u user=>

noisesmith22:11:17

@qmstuart also, more generally, read-string is a powerful function (it can even cause arbitrary code to execute in some situations), and in your case Long/parseLong would be the safer (and weaker) option

Stuart22:11:13

oh, good to know! thanks!