Fork me on GitHub
#clojure
<
2020-12-09
>
didibus02:12:35

What's people takes on shadowing global vars with local ones? Like say calling a local binding str ?

potetm02:12:54

don’t do it

potetm02:12:36

been bitten more times than i can count by naming things type and then messing up a reference somewhere in the file

potetm02:12:22

I only shadow when I intentionally want to make a var unavailable

Alex Miller (Clojure team)02:12:41

Perfectly fine, I do it all the time

9
😎 6
potetm02:12:51

(e.g (fn [a] (let [a (normalize a)] …)) )

👍 3
didibus03:12:40

And I assume here you didn't want to rebind a to the normalized a ?

potetm03:12:09

I don’t want to allow access to un-normalized a

potetm03:12:20

i.e. i don’t want to allow access to what came into the fn

didibus04:12:26

Oh ok, this was an example of when you want to explicitly shadow I see

didibus03:12:32

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.

seancorfield03:12:51

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.

☝️ 6
truestory 3
seancorfield03:12:56

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 🙂

seancorfield03:12:04

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

borkdude10:12:19

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.

borkdude10:12:30

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

seancorfield19:12:47

It seems to be at least partially working already in the extension for VS Code, based on my brief experiments last night.

borkdude19:12:29

When you use either :include or :exclude it will work as expected

borkdude19:12:59

Even that I'm not sure of now. There was a pretty stupid bug. Let me just roll a release.

dpsutton03:12:32

I remember him asking about this recently. It might be recently added or abandoned based on feedback

dpsutton03:12:57

I’ve been bitten by name a few times. I get nervous each time I use it as a var

seancorfield03:12:11

:shadowed-var -- off by default. Just looked it up.

seancorfield03:12:24

Maybe I'll turn it on at work and see what we get 🙂

potetm03:12:02

at some point, the number of experts who have been bitten by it says all you need to say

potetm03:12:05

probably 🙂

potetm03:12:21

t and n are fine for type and name, imo

didibus03:12:30

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

potetm03:12:02

Even clojure code uses s for strings

potetm03:12:12

extremely common abbreviation

potetm03:12:28

*clojure core

didibus03:12:54

But I alias clojure.spec.alpha to s 😛

potetm03:12:32

alias/var names don’t overlap

potetm03:12:38

never had a single issue with that 🙂

potetm03:12:06

(because it’s impossible)

didibus03:12:22

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?

dpsutton03:12:30

Heh. I never use agents so shadow away. There’s basically no ambiguity for me :)

borkdude07:12:29

Yes, clj-kondo has a linter for that. I fixed an issue with it on master last weekend

viesti08:12:03

Humdum, trying to remember the name of a tracing debug library for Clojure that came about recently (not sayid), but my memory fails me

flowthing08:12:56

https://github.com/jpmonettas/flow-storm was announced pretty recently, at least.

❤️ 3
viesti08:12:48

ah that was it, thank you for aiding my fading memory! 🙂

bibiki14:12:27

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?

delaguardo14:12:49

you forgot # in front of (Integer/parseInt %) is this lambda inside of another anonymous function declaration?

bibiki14:12:19

no, I just didn't put it as it triggers slack features

bibiki14:12:01

what in the code is: (def in (map hash(Integer/parseInt %) my-list))

Alex Miller (Clojure team)14:12:21

map is lazy so if you're not using all of the result, you won't actually do the parsing

bibiki14:12:19

@alexmiller I get that, but what is getting "2622048027" to be a number the second time I run the code?

bibiki14:12:14

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?

Alex Miller (Clojure team)14:12:41

I think that number is too big to be an Integer

Alex Miller (Clojure team)14:12:54

Integer/MAX_VALUE is 2147483647

Alex Miller (Clojure team)14:12:24

in general, I almost always use Long/parseLong (because that's the default integer size in Clojure)

bibiki14:12:32

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

Alex Miller (Clojure team)14:12:47

can you share the actual code?

Alex Miller (Clojure team)14:12:01

from your description, doesn't make sense so must be missing something

bibiki14:12:44

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

andy.fingerhut14:12:36

I get not found on that link. Might be on my end.

bibiki14:12:32

oh, I think I have my repo private for aoc 020

bibiki14:12:35

just a second

bibiki14:12:51

@U0CMVHBL2 @alexmiller I just made my repo public, please try again

Alex Miller (Clojure team)15:12:38

well, in is lazy so you could use it for a while before discovering an issue

bibiki15:12:41

@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?!?!

bibiki15:12:45

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

Alex Miller (Clojure team)15:12:30

stepping away for a meeting

bibiki15:12:44

👍 thanks for your time

andy.fingerhut15:12:02

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.

andy.fingerhut15:12:24

If I change Integer/parseInt to Long/parseLong and do (count in), I get 1000.

andy.fingerhut15:12:49

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.

andy.fingerhut15:12:47

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.

Alex Miller (Clojure team)15:12:34

in cases like this, all bets are off - we don't try to protect you from "broken" lazy seqs

bibiki15:12:45

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

bibiki15:12:25

I think I got it

bibiki15:12:33

or maybe not.. hmm

bibiki15:12:38

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

bibiki14:12:16

if you fetch the code and want to reproduce the bug, just run (solution-second-part) twice after compiling the code

jumar15:12:42

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

Alex Miller (Clojure team)15:12:19

that is a bug and we have a ticket for it

Alex Miller (Clojure team)15:12:35

well, maybe it's not the same thing - you might be seeing mismatch between int and long too?

Alex Miller (Clojure team)15:12:57

nah, it's probably the same thing as that bug

jumar15:12:33

Yeah, this works:

(case (int 1)
  1 true
  nil false)
true
Thanks!

didibus22:12:45

What's the best way to synchronize a function in Clojure? For all its callers?

Alex Miller (Clojure team)22:12:15

locking is the canonical answer

Alex Miller (Clojure team)22:12:00

functions are not synchronized in Clojure, but you can add that semantic inside the function if you really need to

Alex Miller (Clojure team)22:12:18

(but I'd try hard to not need to :)

hiredman22:12:20

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

didibus23:12:06

What's the best thing to lock on if using Locking?

didibus23:12:17

Can I lock on the function itself?

Alex Miller (Clojure team)23:12:19

common case is to def/let an (Object.) to act as the lock

didibus23:12:00

Would this be considered weird:

(let [o (Object.)]
  (defn ...
    (locking o ...)))

Linus Ericsson21:12:49

no, that looks ok

Alex Miller (Clojure team)23:12:12

no, but I'd put that around the defn

didibus23:12:39

Sorry, updated it, pressed enter instead of shift+enter first time around

Alex Miller (Clojure team)23:12:53

np, that is a pattern for sure

✔️ 3