Fork me on GitHub
#beginners
<
2020-02-05
>
hindol.adhya06:02:52

Am I missing something or is apply really this slow? What would be an efficient way to get the minimum from a collection?

(time
 (dotimes [_ 1000000]
   (apply max '(1 3 5 7 9 2 4 6 8))))

;; => "Elapsed time: 429.3991 msecs"

(time
 (dotimes [_ 1000000]
   (apply max [1 3 5 7 9 2 4 6 8])))

;; => "Elapsed time: 274.5674 msecs"

(time
 (dotimes [_ 1000000]
   (max 1 3 5 7 9 2 4 6 8)))

;; => "Elapsed time: 9.2664 msecs"

hindol.adhya06:02:07

Will try a reducer and post back.

andy.fingerhut06:02:36

(reduce max Coll) was in between those speeds in my testing

andy.fingerhut06:02:46

faster than apply by about a factor of 4 to 4.5

andy.fingerhut07:02:16

I don't know the details of exactly what JVM byte code the Clojure compiler turns it into, but max is annotated with nary-inline and that name might mean that (max 1 3 5 7 9 2 4 6 8) is in-lined by the compiler in some way.

andy.fingerhut07:02:10

I do not know yet, but the relationship between the time of using apply vs. not might differ as the collection sizes get larger, i.e. a lot of the time difference here might be one-time setup on calls. If you really want to use collections this small, then you care about that effect. If you really want to use much larger collections, you may want to benchmark with those instead. The Criterium library is recommended for benchmarking, as it will handle JIT warmup kinds of issues for you.

hindol.adhya08:02:52

@ Yes, reduce is in between.

(time
 (dotimes [_ 1000000]
   (reduce max [1 3 5 7 9 2 4 6 8])))

;; => "Elapsed time: 58.445 msecs"

andy.fingerhut08:02:19

Note that the time you show for (max 1 3 5 7 9 2 4 6 8) is only 9.2 nanoseconds per call, which seems maybe impossibly quick, e.g. perhaps the value is actually calculated once, and simply returned each time through the dotimes, and mostly what you are measuring there is the overhead of the dotimes iteration loop.

andy.fingerhut08:02:59

Try comparing against the time to execute (time (dotimes [_ 1000000] (+ 5 7))) , for example.

andy.fingerhut08:02:23

Or even (time (dotimes [_ 1000000] 5))

hindol.adhya13:02:03

@ Tangential question: will we at some point switch to Clojure 10 source code links in ClojureDocs?

andy.fingerhut18:02:06

I would guess yes, but when is up to the maintainer of the ClojureDocs site, which is not me.

andy.fingerhut18:02:17

and someone made significant progress in that direction, mentioned in a PR linked from a comment there.

hindol.adhya09:02:37

Thanks! Based on some of your earlier replies, I mistook you for one of the maintainers. At least you contributed extensively to ClojureDocs.

andy.fingerhut09:02:51

I am very glad someone else created it, and I refer people to there frequently.

frederikdieleman09:02:46

Thanks for all the help on my question to serialise stuff! I'll have a look at transit and nippy. Are there any preferences when the main criteria is easiness to extend to non-standard datatypes?

noisesmith18:02:00

they should be about equal - with transit you pass the data conversion rules as an argument to the write / read functions (which I like, it means you don't need to mess with a global state of registered readers / writers)

noisesmith18:02:00

all else being equal I opted for transit as it has the option of semi-readable format (useful when debugging a live env by eg. peeking a kafka topic or naiive logging), and transit is from the core clojure team

peb.brzezinski10:02:38

Hi! Following a tutorial, there’s this piece of code that should get me clojure.set as set in scope, but when I try to compile, I get an error.

(ns pegthing.core
  (require [clojure.set :as set])
  (:gen-class))

2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling src/pegthing/core.clj at (1:1)
   #:clojure.error{:phase :macro-syntax-check,....

1. Caused by clojure.lang.ExceptionInfo
   Call to clojure.core/ns did not conform to spec
what’s wrong with that syntax?

finn.volkel11:02:07

I think you are missing the : , require -> :require

peb.brzezinski11:02:45

That’s it! Nice, thank you. There’s a typo in the book then 🙂

jsyrjala11:02:59

I think that older clojure versions used to work with (require even though specifications/documentation didn’t allow for that.

peb.brzezinski11:02:15

Oh, ok, that’d make sense.

peb.brzezinski11:02:20

Good to know, thanks!

jsyrjala11:02:12

That stopped being allowed somewhere around clojure 1.9 or 1.10

finn.volkel12:02:40

Is there a way to modify the reader so that all literal maps are read as array-maps?

finn.volkel12:02:40

Also related. Is there something in the direction of array-set ? For context, I am working on some debugger and the order of things in maps/sets is importent.

finn.volkel12:02:47

Essentially I want to read the sets/maps as sets/maps or at least know that they were sets/maps and be able to traverse them in the order they were defined.

finn.volkel12:02:10

Ok I figured out that up to some threshold all maps are read as array maps.

alexmiller13:02:43

Up to 8 entries, but you shouldn’t rely on that

finn.volkel14:02:28

@alexmiller but there is no way to modify this threshold ? Is there a way to obtain a similar behavior for small sets?

alexmiller14:02:25

no, it's hard-coded

alexmiller14:02:35

maps and sets are not ordered, full stop

alexmiller14:02:57

relying on implementation details will make you broken

alexmiller14:02:04

(eventually)

alexmiller14:02:27

what problem are you actually trying to solve?

finn.volkel16:02:39

@alexmiller I am working on the cider-nrepl debugger and the instrumentation of map/set literals. I guess for map literals with more than 8 entries we have to use some heuristic.

alexmiller17:02:38

heuristic for what?

alexmiller17:02:52

you described a context not a problem...

finn.volkel18:02:24

@alexmiller The debugger reads normal clojure forms and instruments them for debugging. Currently it uses the nesting of the expressions (kind of location information) as a way to give back feedback to the user. The problems with maps/sets is that the order of the expressions is not preserved. For example something like (read-string "#{(inc 2) (inc 3)})" gives {(inc 3) (inc 2)} . I just would like to know if there is a way to avoid this problem. Hope it makes sense now. For literal maps, the order is preserved up to 8 entries as you mentioned.

alexmiller18:02:16

Yes, that helps

alexmiller18:02:32

I think to do it the best way, you would need to replicate the reader’s code and either change what’s happening or track more

alexmiller18:02:53

There are other Clojure parsers out there that might be a better match for your goals though

alexmiller18:02:08

Really what you’re doing here is parsing, not reading

alexmiller18:02:13

That is, the goal of the reader is to turn text into Clojure data. You really want to turn text into ast (parsed structure)

alexmiller18:02:33

Those are similar but different goals

finn.volkel18:02:27

So your suggestion is to use something like tools.analyzer?

alexmiller19:02:50

The analyzer May be more than you need

alexmiller19:02:18

Other people have written parsers for tooling

finn.volkel12:02:12

Also more of a meta question when should one ask here and when is it ok to ask #clojure ?

doby16215:02:54

Is (load filename) the correct way to pull in other files with their own namespace? If so, is there some kind of init function that needs to be present? I tried swapping load-files out for loads while breaking a project into namespaces and it told me I'm missing a init class.

alexmiller15:02:29

usually, you should use require to initiate loading

alexmiller15:02:00

and not use load or load-file directly

doby16215:02:27

Ah, cool. I didn't realize that would work for files that were hanging out in my project instead of being defined in the project.clj

alexmiller15:02:43

well, you haven't shared enough other info to determine whether you are in or out of "usually" here

alexmiller15:02:31

require will use the classpath to find the namespaces, so it depends on having your source files in the classpath, in a directory corresponding to the namespace

alexmiller15:02:58

if that's not true, load-file is probably most straightforward. load is classpath focused, but has rules about how things are found based on its doc string

alexmiller15:02:46

so just switching from one to the other probably requires changing the argument path

doby16215:02:21

Ah, ok. The file structure is src/project-name/several-files-each-with-a-name-space so project.core loads and expects to be able to load project.server, for instance. This is just how the project ended up, I can move things to get require to work.

rodrigo75918:02:52

Hey everyone, Sorry to interrupt. I've been struggling with a Clojure deploy error for more than a week. Please take a look at: https://stackoverflow.com/questions/59938002/java-lang-noclassdeffounderror-for-clojure-java-api-clojure I would really appreaciate any advise. I do not know what to do anymore. Tks!

bfabry19:02:37

did you ever manage to get the CLASSPATH output as suggested?

rodrigo75919:02:52

I did. Clojure classes are not at Tomcat's or system's own libraries. I also created a script to list all .class on the classpath and compared with my application libraries. No duplicated entries were found.

alexmiller19:02:21

Almost certainly something related to the context classloader. Also, there were some changes in Clojure 1.10.1 vs 1.10.0 to mitigate a Java issue introduced in Java 1.8.0 u201 (give or take, don’t remember precisely). Any Clojure version diff between them?

rodrigo75919:02:30

Good call. I am using Clojure 1.9.0. I will try 1.10.1 instead.

rodrigo75919:02:27

@alexmiller Sorry... Let me correct myself. We've tried 1.10.0 and 1.10.1 already and got the same error. It was the first thing we did. No luck.

alexmiller19:02:44

ok, good to check on that at least

ghadi19:02:38

your stack trace indicated that clojure failed static initialization, not that the class wasn't found

ghadi19:02:55

if the class wasn't present, you'd get a ClassNotFoundException

ghadi19:02:19

this exception indicates: Caused by: java.lang.NoClassDefFoundError: Could not initialize class clojure.java.api.Clojure

ghadi19:02:36

you are missing some trace elements in the output that might be useful

rodrigo75919:02:50

Yep. I've inspected the source code of clojure.java.api.Clojure but did not find anything unusual.

alexmiller19:02:00

any chance you have a user.clj file somewhere?

alexmiller19:02:16

on the classpath

rodrigo75919:02:25

I do not. Should I?

alexmiller19:02:38

no, but that affects early initialization

alexmiller19:02:59

just checking it wasn't on one but not the other

alexmiller19:02:23

is ClojureHandler source somewhere?

rodrigo75919:02:41

I've inspected the source code at Github. I am using the official distribution.

alexmiller19:02:49

can you point to it?

ghadi19:02:56

ClojureHandler isn't something in the clojure distribution

alexmiller19:02:10

com.nitryx.brkmopt.util.ClojureHandler

rodrigo75919:02:20

oh.. sorry. Yes. This is my program.

rodrigo75919:02:22

Let me copy the line that throws the error for you. Just 1 min...

alexmiller19:02:23

there is almost certainly an exception thrown during the static initializer of clojure.java.api.Clojure - whatever that is would probably point to the problem. the trick is getting to it.

alexmiller19:02:29

more than the line would be helpful

alexmiller19:02:57

presumably the line is loading the Clojure class :)

rodrigo75919:02:30

there it is:

rodrigo75919:02:32

public static Object invoke(String ns, String function, Object... parameters) {
        IFn func = Clojure.var(ns, function);
        Object ret;
        int n = parameters.length;
        switch (n) {
            case 0:
                ret = func.invoke();
                break;
            case 1:
                ret = func.invoke(parameters[0]);
                break;

rodrigo75919:02:19

the line that throws the Exception is:

rodrigo75919:02:27

IFn func = Clojure.var(ns, function);

bfabry19:02:05

my guess: you need to require that ns

bfabry19:02:09

well, load

me192619:02:33

(Sorry for the interruption) I have a global state that needs to be modified by multiple threads. More specifically, I have a map that will have a key/val pair added to it some amount of times (always a new key/val pair), but from a separate thread (or threads). What’s the most idiomatic way to implement this in Clojure? Should I be using transients, or some other type of thread-safe protection (e.g. atoms)?

alexmiller19:02:02

atom holding a map

me192619:02:12

Got it, thank you.

noisesmith19:02:55

transients are explicitly not safe to use in threaded code, and shouldn't really be passed between functions in most code

bfabry19:02:37

although that does not explain it working on one server and not the other

alexmiller19:02:40

well, first - what ns / function is it

rodrigo75919:02:11

@bfabry yes... thats the problem. The very same app. Just in case, I will print ns in the log just to be sure if we are not missing something.

rodrigo75919:02:26

This is the same .war. The same build. On different servers.

seancorfield19:02:43

Different loading order for classes, causing the ns to be required (and loaded) on one server but not the other?

rodrigo75919:02:44

Looking again the code. ns cannot be the problem. It is hardcoded.

alexmiller19:02:04

you could try loading the Clojure class earlier - maybe in the static initializer of ClojureHandler for example. like

static {
  Class.forName("clojure.java.api.Clojure");
}

rodrigo75919:02:51

@seancorfield ns is "com.nitryx.brkmopt.opt.scenario". I do not see how the order would make difference because it is something I created. But it was a good call.

rodrigo75919:02:25

@alexmiller trying your suggestion right now. I'll get back to you shortly. Thanks very much.

alexmiller19:02:33

I notice that Tomcat 8.5.50 includes a Catalina that has a big slew of "Correct a regression in the static resource caching changes..."

alexmiller19:02:03

perhaps Tomcat 8.5.49 is bad - have you tried bumping up? (or going back to 8.5.37)

rodrigo75919:02:33

Ohh... nice! I'll try it too.

jumar19:02:49

@rodrigo759 could you share the whole stacktrace? Isn't there really anything interesting apart from what you shared in https://stackoverflow.com/questions/59938002/java-lang-noclassdeffounderror-for-clojure-java-api-clojure ?

alexmiller19:02:53

something with resource caching/loading could certainly break Clojure, which loads clj files as resources

alexmiller19:02:43

I'm putting my money on Tomcat as the likely culprit

rodrigo75919:02:57

@jumar I am editing the stackoverflow post to include the full stacktrace

rodrigo75919:02:07

@alexmiller my team is changing Tomcat as we speak.

ghadi19:02:23

I have a reaction gif ready for if a critical detail was being elided from the stacktrace

rodrigo75919:02:58

Full stacktrace is there.

jumar20:02:13

I couldn't find anything more there. You could perhaps try to run the same tomcat version on both servers or, better, reproduce it locally. And perhaps -verbose:class JVM arg could give you some helpful details about classloading. Also make sure you're not running out of memory (just in case)

rodrigo75920:02:33

@alexmiller YES! Downgrading Tomcat to 8.5.37 did the trick. Not easy because other apps are running in production using version 8.5.49. I had to convince the customer's IT guys that I need this version. So far, so good. Thank you all very much. @alexmiller, do you have a Stackoverflow account? If you do, you might want to publish the solution. I will accept and upvote it.

alexmiller20:02:03

finally, something to boost my stackoverflow rep

alexmiller20:02:33

I keep sending my stackoverflow points to my mortgage company but the exchange rate for fake internet points is terrible

doby16220:02:49

Do they accept karma?

alexmiller20:02:35

apparently not

alexmiller20:02:38

nor github stars

andy.fingerhut21:02:38

Hah! I love that line originating this thread. I should copy and save it somewhere. Reminds me of a similar idea phrase I have sometimes used, when people compliment you for your abilities and knowledge, but what you really want is respect and/or better salary "That and 50 cents will buy me a soda"

me192621:02:52

I need to block in my main thread until another thread has a value that I need to continue—essentially just blocking until I get a message. How should this be done?

me192621:02:29

Or is it more likely I’m looking at this problem the wrong way, and there’s a better solution or something like that.

clojurians97121:02:20

You could use a promise

me192621:02:18

Huh. Was looking through https://clojure.org/reference/…, didn’t think that that wouldn’t include core.async. Thank you.

me192621:02:53

Yeah, this seems to be what I want. 🙏

jdwoodsy121:02:31

Noob question here. How to I get the value passed in because % doesn't seem to work here

(->  (GetResult url)
     (let [body (:body %)] (println body)) )))

noisesmith21:02:23

% is a special syntax inside #()

noisesmith21:02:00

just use a let for that:

(let [res (GetResult url)
      body (:body res)]
  (println body))

noisesmith21:02:16

or (-> url (GetResult) (:body) (println))

jdwoodsy121:02:41

okay think I get it now thanks

hiredman21:02:44

sometimes people use % with as->

noisesmith21:02:27

that's true (as-> (GetResult url) % (:body %) (println %))

jdwoodsy121:02:44

is (-> url (GetResult) (:body) (println)) the preferred way or (as-> (GetResult url) % (:body %) (println %))

noisesmith21:02:23

the first one - though you might prefer to move url over into the GetResult call (it expands to the same thing)

noisesmith21:02:53

most people would actually do (-> (GetResult url) :body println) - same result still

peb.brzezinski22:02:32

Any opinions about http://purelyfunctional.tv? Sorry for posting the link, let me know if I have to remove it. I’m thinking about getting a membership to learn clojure but would like to hear opinion of somebody that’s used it

shaun-mahood22:02:46

I regularly use it to get details on things that I haven't done before, though I wasn't a Clojure beginner when I first got my membership - over the years I've generally used it to get a bit more detail on specific topics rather than watching all the content. I recently went through a lot of the re-frame content and found it to be excellent - one of the best resources I've seen for that. I would totally recommend trying it out and seeing if it works for you. There is also https://lambdaisland.com/, which has a bit different style and quite a bit less content, but the content that is there is generally very good as well.

peb.brzezinski22:02:29

That’s an awesome review, thank you very much. I’m not new to functional programming concepts in general, but I just started learning clojure and am trying to figure out the most efficient way. Guess I’ll just give it a shot 🙂

shaun-mahood22:02:05

Yeah, if that's where you're coming from there should be a lot of great content for you. Hope it helps!

seancorfield23:02:35

The REPL-Driven Development course on http://PF.tv is definitely worth at least one month of membership. It's awesome.

k.i.o23:02:26

Eric also has a yt channel which is pretty good by itself

jr0cket14:02:43

http://purelyfunctional.tv and lambdaisiland have excellent quality for learning Clojure. I also have over 60 hours of Clojure videos, these are less polished but then they are free 🙂 https://practicalli.github.io/ I also find the https://clojuredesign.club/ an excellent resource for thinking functionally

peb.brzezinski14:02:00

Nice! The only thing I don’t really like about functionaltv is the organization of website. It was hard for me to find the “introduction to clojure” course and tbh I’m not sure what will my next steps after finishing it look like 😄

peb.brzezinski14:02:05

Thanks for the links, I’ll give them a look!