What's people takes on shadowing global vars with local ones? Like say calling a local binding str
?
donโt do it
been bitten more times than i can count by naming things type
and then messing up a reference somewhere in the file
I only shadow when I intentionally want to make a var unavailable
Perfectly fine, I do it all the time
(e.g (fn [a] (let [a (normalize a)] โฆ))
)
And I assume here you didn't want to rebind a to the normalized a ?
Haha, so I guess its a controversial issue ๐
I'm in the camp of it being totally fine as well. I've never really gotten confused by it, though I guess it would be confusing if it binds to a function. Like a function that took an arg called str
which also happened to be a function, that be confusing. But I've never done that.
It's bitten me once or twice but I still do it. There are just too many nice, short, useful names in clojure.core
to avoid them all for local bindings.
type
and name
are probably the two most common global names that I shadow @didibus -- I have str
as an alias for clojure.string
but I've never found a need for it as a local symbol. A generic string might just as well be s
๐
(it would be interesting to scan the codebase to see what else we shadow... is that something clj-kondo
can detect @borkdude? I suspect it is... but off by default?)
Currently there is a bug in the linter in that it only works when you use either :include
or :exclude
certain symbols, but not without those options. This has been fixed on master.
I will release probably soon, but when using either of these options, the linter should work. The default level is :off
.
See https://github.com/borkdude/clj-kondo/blob/master/doc/linters.md#shadowed-var
It seems to be at least partially working already in the extension for VS Code, based on my brief experiments last night.
When you use either :include
or :exclude
it will work as expected
Even that I'm not sure of now. There was a pretty stupid bug. Let me just roll a release.
I remember him asking about this recently. It might be recently added or abandoned based on feedback
Iโve been bitten by name a few times. I get nervous each time I use it as a var
:shadowed-var
-- off by default. Just looked it up.
Maybe I'll turn it on at work and see what we get ๐
I donโt want to allow access to un-normalized a
i.e. i donโt want to allow access to what came into the fn
at some point, the number of experts who have been bitten by it says all you need to say
probably ๐
t
and n
are fine for type and name, imo
I realized you can freely mix vars and namespace, so even when I alias clojure.string as str
its still fine to do: (fn [str] (str/upper-case str))
Even clojure code uses s
for strings
extremely common abbreviation
*clojure core
But I alias clojure.spec.alpha to s
๐
alias/var names donโt overlap
never had a single issue with that ๐
(because itโs impossible)
Anyway, I was just using it as an example. The example that prompted me to ask was we have a thing called Agent in our app, and so the bindings are often (defn [agent ...] ...)
and our team were discussing not shadowing the core agent
function. So maybe that's a better example?
Heh. I never use agents so shadow away. Thereโs basically no ambiguity for me :)
Oh ok, this was an example of when you want to explicitly shadow I see
Yes, clj-kondo has a linter for that. I fixed an issue with it on master last weekend
Humdum, trying to remember the name of a tracing debug library for Clojure that came about recently (not sayid), but my memory fails me
https://github.com/jpmonettas/flow-storm was announced pretty recently, at least.
ah that was it, thank you for aiding my fading memory! ๐
hi, I have a quick question: I have this list of strings that are all numeric. Before I process it, I map over the list to get numeric values: (map (Integer/parseInt %) my-list) I compile it with no complaint. However, when I run the code, the first time I run it I get a number format exception for the value of "2622048027". When I run it the second time without changing code or recompiling or anything, the code works fine. I understand that Integer/parseInt is supposed to give number format exception. What I do not understand though is why it works the second time I run the code. Anyone has any idea what is happening here?
you forgot #
in front of (Integer/parseInt %)
is this lambda inside of another anonymous function declaration?
no, I just didn't put it as it triggers slack features
map is lazy so if you're not using all of the result, you won't actually do the parsing
what in the code is: (def in (map hash(Integer/parseInt %) my-list))
@alexmiller I get that, but what is getting "2622048027" to be a number the second time I run the code?
the first time I need that value, map is evaluated, and that is when (Integer/parseInt "2622048027") is called, and it gives the exception. The second time I run the code, Integer/parseInt has already been called, so it is not called... buut what got "2622048027" into 2622048027?
I think that number is too big to be an Integer
Integer/MAX_VALUE is 2147483647
in general, I almost always use Long/parseLong (because that's the default integer size in Clojure)
it is, and I understand why Integer/parseInt must throw an exception. But I do not understand how it is turning into a number in spite of the eception being thrown
can you share the actual code?
from your description, doesn't make sense so must be missing something
yes sure. the code is my solutions to advent of code. I am giving url directly to day 9 solution https://github.com/bibiki/aoc-2020/blob/main/src/aoc_2020/day_9/solution.clj
if you fetch the code and want to reproduce the bug, just run (solution-second-part) twice after compiling the code
I get not found on that link. Might be on my end.
oh, I think I have my repo private for aoc 020
just a second
@andy.fingerhut @alexmiller I just made my repo public, please try again
well, in
is lazy so you could use it for a while before discovering an issue
@alexmiller I am not sure that explains why it works the second time I run the code, why is that string turning into a number the second time I run the code?!?!
that does explain why I get a problem after having read many values from in, not immediately, but not why it succeeds the second time...
stepping away for a meeting
๐ thanks for your time
I can try to evaluate (count in)
and the first time it throws an exception, but the second time it returns 608 with no exception.
If I change Integer/parseInt to Long/parseLong and do (count in), I get 1000.
So I would not say it is "working" when it returns 608, except to say it is returning the 608 elements out of the 1000 before the exception occurred.
This is surprising:
(case (int -1)
-1 true
nil false)
Execution error (IllegalArgumentException) at .../eval21818 (form-init2370325502731369149.clj:398).
No matching clause: -1
(case (int -1)
-1 true
-2 false)
true
lazy sequences do cache their results when realizing elements, i.e. they actually allocate memory for elements of the lazy sequence as it is being realized. I suspect when it is realizing it the first time, it is creating the first 608 elements, getting the exception on the 609th, and then leaving the lazy sequence in a state where after 608 elements that is a normal end of the list, rather than a "try to realize this next thing" the way it was when the exception occurrred.
in cases like this, all bets are off - we don't try to protect you from "broken" lazy seqs
hmmm
that is a bug and we have a ticket for it
well, maybe it's not the same thing - you might be seeing mismatch between int and long too?
you guys are right, as measured against the expectation that (count in) should be 1000, it is not working. as measured against the expectation of giving an answer to second part of the puzzle... it does produce the correct answer. I need to think further why it gets to evaluating "2622048027" before finding an answer to the puzzle in the first run
nah, it's probably the same thing as that bug
I think I got it
or maybe not.. hmm
Yeah, this works:
(case (int 1)
1 true
nil false)
true
Thanks!I guess (partition ... is ran against the 608 elements the second time, and the correct answer to the puzzle is producible with those 608 elements. yeah, I think that is
thanks @alexmiller @andy.fingerhut
What's the best way to synchronize a function in Clojure? For all its callers?
locking
is the canonical answer
functions are not synchronized in Clojure, but you can add that semantic inside the function if you really need to
(but I'd try hard to not need to :)
locking
lets you use the low level object monitors that synchronized
blocks and methods in java use, but you also have access to https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/package-summary.html
What's the best thing to lock on if using Locking?
Can I lock on the function itself?
I wouldn't
common case is to def/let an (Object.)
to act as the lock
Would this be considered weird:
(let [o (Object.)]
(defn ...
(locking o ...)))
no, that looks ok
no, but I'd put that around the defn
not in the defn
yeah, have done
Sorry, updated it, pressed enter instead of shift+enter first time around
np, that is a pattern for sure