Fork me on GitHub
#clojure
<
2020-04-24
>
potetm01:04:16

@defndaines friends don’t let friends shadow functions vars

😁 8
😂 4
murtaza5213:04:44

@U07S8JGF7 can you expand on what do you imply by shadow function vars

potetm13:04:57

for example

(let [map {:foo :bar}]
  (map :foo))

potetm13:04:19

here, the symbol map is said to “shadow” the var clojure.core/map

potetm13:04:06

Meaning: Ordinarily, the symbol map would refer to clojure.core/map, but because you re-bound it in a let, it now refers to {:foo :bar}.

potetm13:04:30

This is a common source of errors.

murtaza5213:04:05

thanks, got it

👍 4
potetm01:04:26

common shadowing mistakes: type num count val

potetm01:04:11

t n c (or cnt) v, respectively

andy.fingerhut01:04:15

Sound advice, but if it was in response to the (count @atom) code snippet earlier and the associated error message, there was no name shadowing occurring there.

andy.fingerhut01:04:58

They used atom in an expression when they should have used state , that is true, but they never shadowed the definition of atom

potetm01:04:29

oh, right, it’s related in that: If you often shadow atom you’re more likely to make this mistake.

potetm01:04:45

but touché 😄

telekid01:04:02

Is there an interface in the standard library that represents associativeish things like maps, but doesn’t include vectors? I’ve seen clojure.lang.Associative but that doesn’t fit the bill. clojure.lang.IKVReduce might be a good proxy for what I’m looking for? What is clojure.lang.MapEquivalence?

ghadi01:04:37

IPersistentMap

telekid01:04:50

Thanks! That did what I was looking for

seancorfield01:04:33

@jake142 Are you looking for a way to test things for being "map-like" or are you looking to implement something that is associative?

telekid02:04:16

The former.

telekid02:04:57

I’m using it to extend a protocol

telekid02:04:27

“For mappy things, do this”

potetm02:04:40

ah, then, more the latter I think 😄

telekid02:04:50

(Paste coming shortly)

telekid02:04:42

This is what I’m doin’. It didn’t work with clojure.lang.Associative , since vectors extend that but don’t return a [[][]] structure after the call to into

telekid02:04:49

(It would be interesting if you could extend a protocol based upon more than one class…)

telekid02:04:40

I’m being a bit lazy I guess, it would probably be more robust to dispatch off of clojure.lang.IKVReduce and rewrite my function to use reduce-kv rather than map.

potetm02:04:37

(it does the same check Ghadi suggested)

didibus03:04:33

What are the Java classes that are auto-imported? I know Math is, what are the other ones?

Alex Miller (Clojure team)03:04:14

Everything in java.lang

Alex Miller (Clojure team)03:04:35

Well, everything old. Hasn’t been updated in a while

didibus03:04:41

Oh, so like everything in java.lang for Java 8 ?

Alex Miller (Clojure team)03:04:07

Prob java 5 or something

didibus03:04:13

Does that include like multiple level down? Or direct?

didibus03:04:43

Like say java.lang.x.y would y be auto-imported?

andy.fingerhut03:04:51

If you know of an example of some class matching java.lang.x.y, you could try using it in a REPL without the full prefix and see what happens.

andy.fingerhut03:04:59

I don't happen to know such a class off the top of my head.

seancorfield03:04:32

user=> (sort (filter #(re-find #"^java.lang" %) (map #(.getName %) (vals (ns-imports *ns*)))))
("java.lang.AbstractMethodError" "java.lang.Appendable" "java.lang.ArithmeticException" "java.lang.ArrayIndexOutOfBoundsException" "java.lang.ArrayStoreException" "java.lang.AssertionError" "java.lang.Boolean" "java.lang.Byte" "java.lang.CharSequence" "java.lang.Character" "java.lang.Class" "java.lang.ClassCastException" "java.lang.ClassCircularityError" ...
Easy enough to look 🙂

seancorfield03:04:34

user=> (sort (filter #(re-find #"^java\.lang\..*\." %) (map #(.getName %) (vals (ns-imports *ns*)))))
()
No triple-dotted entries

andy.fingerhut03:04:49

java.lang.Character.Subset is Java syntax for what I believe is an inner class of java.lang.Character . I will put a sample REPL session in a thread on this message to show some results I found

andy.fingerhut03:04:55

Clojure 1.10.1
user=> Character
java.lang.Character
user=> java.lang.Character
java.lang.Character
user=> java.lang.Character.Subset
Syntax error (ClassNotFoundException) compiling at (REPL:0:0).
java.lang.Character.Subset
user=> java.lang.Character$Subset
java.lang.Character$Subset
user=> Character$Subset
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: Character$Subset in this context

andy.fingerhut03:04:10

Wow, I found an inner class inside of an inner class, too:

user=> java.lang.ProcessBuilder$Redirect$Type
java.lang.ProcessBuilder$Redirect$Type

didibus03:04:10

I'm confused about this

didibus03:04:41

Oh, ok, right, so you mean inner classes are not imported as well

didibus03:04:41

And you need to use the fully qualified for them

andy.fingerhut04:04:20

I'm concluding that from experiments, not from some kind of first principles of full knowledge of Clojure's implementation.

didibus04:04:56

Ya, its good enough for me. Makes things easier

seancorfield04:04:22

These two are auto-imported:

user=> (sort (filter #(re-find #"^java\.lang\..*\$" %) (map #(.getName %) (vals (ns-imports *ns*)))))
("java.lang.Thread$State" "java.lang.Thread$UncaughtExceptionHandler")
user=>

didibus04:04:39

😓 might ignore those for now 😛, I'm trying to add completion for java as well to anakondo, but inner classes are bit weird

seancorfield04:04:01

That made me curious about what is imported that isn't java.lang.*

user=> (sort (filter #(not (re-find #"^java.lang" %)) (map #(.getName %) (vals (ns-imports *ns*)))))
("clojure.lang.Compiler" "java.math.BigDecimal" "java.math.BigInteger" "java.util.concurrent.Callable")
user=>

didibus04:04:14

Don't think we'd want them to show up until someone typed $ and that's more work for me

seancorfield04:04:28

So there's a triple-dotted one! java.util.concurrent.Callable

didibus04:04:16

Interesting, I wonder what's the logic, are they coming from just the clojure.core refer, or like, why is there so many lol

Alex Miller (Clojure team)04:04:57

The Big classes are basically extended parts of the numerics in lang

Alex Miller (Clojure team)04:04:11

Functions implement Callable

Alex Miller (Clojure team)04:04:43

Iirc Compiler is in there almost by accident

Alex Miller (Clojure team)04:04:01

Not at a computer to check but it’s all explicit in the code

didibus04:04:16

I don't know if I should have them all completed, or I should just pick the most common ones

Ahmed Hassan08:04:12

> The Big classes are basically extended parts of the numerics in lang > What does this mean?

Alex Miller (Clojure team)12:04:58

The numeric types are in java.lang.Integer java.lang.Long etc and extended by java.math.BigInteger etc

👍 4
didibus03:04:45

@seancorfield Just the command I was looking for.

darnok09:04:13

Probably someone explained that already but I can't find it. Why it executes code inside when?

(when (Boolean. "false") "SHOULDN'T GO THERE")

xsc09:04:27

I could not find the in-code explanation but the behaviour is documented for if: > Note that `if` does not test for arbitrary values of java.lang.Boolean, only the singular value `false` (Java’s `Boolean.FALSE`), so if you are creating your own boxed Booleans make sure to use `Boolean/valueOf` and not the Boolean constructors. https://clojure.org/reference/special_forms#if So, I guess the answer is "because!" and "don't do that!". 😬

👍 4
🤪 4
darnok09:04:21

Thanks 🙂. That's interesting.

andy.fingerhut09:04:32

I have a strong suspicion (but haven't measured) that if every if/when/cond in Clojure supported checking for a new boxed Boolean false value as taking the false branch, it would have a measurable performance slowdown effect on Clojure execution.

👍 4
darnok10:04:13

Yes, that's true. With Java it is not a problem because of static typing.

Felipe Cortez19:04:20

Joy of Clojure has a part about that

darnok09:04:04

(true? (Boolean. "false"))
=> false

kirill.salykin09:04:52

> (false? (Boolean. “false”)) > => false

kirill.salykin09:04:09

dont create booleans like this use boolean

darnok09:04:48

(boolean "false")
=> true

😂 8
kirill.salykin09:04:13

you want to parse boolean

kirill.salykin09:04:41

> ;; Simply defined: Everything except false and nil is logically true in Clojure.

darnok09:04:28

I know, but I've just wanted to say that boolean is not the right answer. It looks like you should use Boolean/valueOf.

kirill.salykin09:04:39

(true? (Boolean/parseBoolean "false"))

kirill.salykin09:04:59

My point was you should not create Boolean every time

kirill.salykin09:04:21

for performance reasons clojure already has true and false created

kirill.salykin09:04:29

and it compares are objects identical

darnok09:04:31

I know, I used it just to parse configuration on the start of app.

kirill.salykin09:04:47

I am not following you

kirill.salykin09:04:08

what then you are asking?

darnok09:04:29

But I think it is not consistent, that when works only with Boolean.TRUE and true? works for everything.

darnok09:04:38

The question was about when and why it doesn't work properly for (Boolean. "false") . Then next line was about that true? handles it properly.

xsc09:04:30

@U1V7K1YTZ I'm sure you mean well but answering a "why?" question with "don't do that!" can be a bit aggressive.

kirill.salykin09:04:47

sorry for it did not mean it aggressive

kirill.salykin09:04:17

my point is - one should not create new Boolean

kirill.salykin09:04:40

because clojure compares with already created instance of true

darnok09:04:11

ok, I get it.

xsc09:04:56

agreed, that's absolutely valid. the discussion here seems to be more about that only false and nil are falsey in if/`when` but a lot more things are falsey in true? and that that seems inconsistent.

kirill.salykin09:04:29

well, true? is only checks if it Boolean.TRUE, right?

darnok09:04:30

yes, true. I haven't check that.

kirill.salykin09:04:41

if/when if it is truthy (hope I spelled that correclty)

andy.fingerhut09:04:32

I do not understand the statement "but a lot more things are falsey in true?and that that seems inconsistent". Do you have an example?

andy.fingerhut10:04:06

Oh, perhaps you mean that if treats (Boolean. true) as true, but true? returns false for that input?

andy.fingerhut10:04:41

Yeah, I believe everything is consistent if you avoid/ignore values created from the Boolean constructor. The Java docs for the Booleanconstructor also have a big bold warning that it is rarely appropriate to use: https://docs.oracle.com/javase/8/docs/api/java/lang/Boolean.html#Boolean-boolean-

xsc10:04:23

I really struggle to but this into good words. 🙂 I think it's about the mental model that one has about if. Is (if condition then else) the same as:

(if (true? condition) then else)
Or is it:
(if (or (nil? condition) (false? condition)) else then)
If you assume the first one (like @U0DN426JG), you are surprised that (Boolean. "false") passes the check.

andy.fingerhut10:04:27

I understand it can be surprising.

kirill.salykin10:04:20

user=> (false? (Boolean. "false"))
false
then this is also should be surprising 🙂

andy.fingerhut10:04:30

My only consoling words are: it rarely arises as an issue that affects your code's behavior. I agree it can make it all the more surprising in those occasions when it might arise, which if I understand correctly, are most often when interacting with Java libraries that box boolean values.

andy.fingerhut10:04:23

I found it surprising enough when I first came across it many years ago that I spent time writing a a fair amount about it on http://ClojureDocs.org: http://clojuredocs.org/clojure.core/if

vlaaad10:04:47

clojure treats as falsey only nil and Boolean/FALSE (checked with identity, not equality), everything else is truthy

vlaaad10:04:25

(Boolean. false) is equal, but not identical to Boolean/FALSE

darnok10:04:54

Thanks, for explanation 🙂. I was just curious.

darnok10:04:52

It seems reasonable from the performance perspective as @andy.fingerhut said.

hindol10:04:53

Yeah, don't ever create a new instance of Boolean using the constructor. If you must, use Boolean/valueOf. Then identity check works.

❤️ 16
hindol10:04:37

154:   public static Boolean valueOf(String s)
 155:   {
 156:     return "true".equalsIgnoreCase(s) ? TRUE : FALSE;
 157:   }

tvaughan14:04:12

Let's say I have a data structure that represents lines on a 2-D plane. Some of these lines overlap and can be joined together. I want to take pairs of lines, compare them, join them into one line or return them both unjoined. I need to repeat this process until all lines that can be joined have been joined. The trick is that this won't reduce to just one line. I also need to immediately remove the two lines that were joined from the list of lines and insert the joined line, or not. I've come up with a solution that's very stateful and it feels very wrong. I know that the process is complete when the "before" and "after" versions are the same, e.g. nothing was found to have overlapped. It's also not generic at all. Is there some pattern that I don't know about that could help with this?

andy.fingerhut14:04:46

This is vague, I know, but you could have some kind of data structure, call it a line-set, that represents "a set of joined lines", which could be as little as one line by itself. Then you could represent the state at every point of your computation as a set of line-sets. A line-set could be represented by a Clojure set, perhaps, and the top level set could be, too.

andy.fingerhut14:04:24

Joining two line-sets is removing those two line-sets from the top level set, taking a union of them, then putting that new line-set back into the top level set.

tvaughan15:04:21

Thanks. I hadn't thought of sets. Ideally I'd like to come up with something that's as simple as (reduce-as-much-as-possible does-pair-of-things-match-fn combine-pair-of-things-fn list-of-things) Currently the combine-fn is required to return a specific and stateful data structure. I thought about transducers that just work on pairs but couldn't make that work

Daniel Stephens01:04:32

I had a play for my own learning, may or may not be helpful, still does feel quite stateful in a way but I haven't figured out anything nicer yet:

;; Assumes
;; if (combinable? a b) and (combinable? b c)
;; then (combinable? (combine a b) c)
(defn combine
  [combinable? combine-fn xs x]
  (loop [combination x remainder xs]
    (let [{combinables true
           others false} (group-by (partial combinable? combination) remainder)]
      (if (seq combinables)
        (recur (reduce combine-fn combination combinables)
               others)
        {:combination combination
         :remainder remainder}))))

(defn combine-all
  [combinable? combine-fn xs]
  (loop [[x & more] xs result nil]
    (if x
      (let [{:keys [combination remainder]} (combine combinable? combine-fn more x)]
        (recur remainder (conj result combination)))
      result)))

Daniel Stephens01:04:41

Was testing a little with some 1d ranges and seems to work well:

(combine-all
  (fn [{amin :min amax :max}
       {bmin :min bmax :max}]
    (and (<= bmin amax)
         (>= bmax amin)))
  (fn [{amin :min amax :max}
       {bmin :min bmax :max}]
    {:min (min amin bmin)
     :max (max amax bmax)})
  [{:min 0 :max 1}
   {:min 1.5 :max 2}
   {:min 2.5 :max 3}
   {:min 1 :max 1.5}])

=> ({:min 2.5, :max 3} {:min 0, :max 2})

dpsutton14:04:26

(cheshire.core/parse-string "3blowup") -> 3 instead of an error is an incredibly surprising behavior to us. anyone know how to make this consider the string as a whole and not just whatever fragment it can parse?

Daniel Stephens15:04:18

Have you tried parse-string-strict? I can't test right now but might do what you want

donyorm16:04:53

So my program takes configuration via command line arguments or environment variables. Is there away to set either of those in deps.edn aliases?

Alex Miller (Clojure team)16:04:41

command line - yes via :main-opts

Alex Miller (Clojure team)16:04:50

env - no (not actually possible in java)

Alex Miller (Clojure team)16:04:19

it would be theoretically possible for the clj script itself to do that but nothing for that exists now

seancorfield16:04:09

System properties can be set programmatically, so that's a possibility instead of env vars.

Amir Mohammad17:04:58

Hi, is there anyone who has been work or experience with "vase"? Is it good for large application?

ryan echternacht17:04:28

sometimes my nREPL grinds to a halt (editor tooling starts failing, the repl itself seems slow). Any thoughts on what’s going on/what I’m doing wrong?

noisesmith18:04:24

if you are running the repl from a terminal, you can use Ctrl-\ to see all stack traces of all threads, there might be a clue hidden in that output

noisesmith18:04:41

you could also connnect a performance profiler like yourkit or visualvm to the jvm

ryan echternacht18:04:32

thanks, i’ll give it a shot

bfay18:04:19

If I have a vector of functions that I want to compose, it seems like the obvious choice would be to use comp It occurred to me that (apply comp fns) and (reduce comp fns) would both technically work... is there a good reason to prefer one over the other?

noisesmith18:04:48

comp already uses reduce1 (something clojure.core uses internally before reduce is bootstrapped from other functions) to its varargs

noisesmith18:04:21

I think apply is easier to read - there's less for me to look at (not that there's a big difference here)

noisesmith18:04:43

I see apply and just check the fn, with reduce there's more to look at, generally

bfay18:04:36

I like that argument, using reduce is a bit redundant

ag19:04:30

TIL you can transpose a 2D matrix by using apply map vector|list, e.g.:

(apply map vector [[1 2 3]
                   [4 5 6]
                   [7 8 9]])
=>
    ([1 4 7]
     [2 5 8]
     [3 6 9])
I feel stupid, can someone explain to me how this works?

myguidingstar20:04:51

@ag did you know that mapcan work on multiple collections, eg (map + [1] [2])

ag20:04:08

right... right.. I get it now. I immediately got it, once I wrote that message 🙂

😄 4
ag20:04:08

It's nicely explained in SO comment: > This works because (apply mapv vector m) is the same as (mapv vector (m 0) (m 1) ...). map/mapv accepts function (vector) in this case and any number of collections. If more than one collection is provided then map will apply the function (vector) to the first items in those collections, then to the second items, etc

lilactown20:04:44

map taking multiple collections is the most useful thing I keep forgetting

Lennart Buit21:04:28

On that topic, what are other useful things in clojure.core and friends that people keep forgetting?

vlaaad21:04:58

other stuff that takes multiple args: • fn returned from every-pred • fn returned from some-fn • fn returned from juxt

👍 4
noisesmith21:04:46

oh good ones - also fnil for up to 3 args

Lennart Buit21:04:35

oh thats interesting, with juxt your fs and gs do need to share an arity

Lennart Buit21:04:54

I didn’t realise because in my mind, juxt was always for functions of one argument :’)

vlaaad21:04:02

yeah, you can do fun stuff like (def separate (juxt filter remove))

vlaaad21:04:45

and then (separate even? (range 20))

👏 8
noisesmith21:04:57

tap> is underappreciated (see also add-tap and remove-tap)

👍 8
noisesmith21:04:18

idiom: (def debug-data (atom [])) (defn debug [x] (swap! debug-data conj x)) (add-tap #'debug)

noisesmith21:04:36

note the usage of the var, so that redef of debug can happen without losing the ability to remove the tap

vlaaad21:04:08

tap> with reveal is 🔥