Fork me on GitHub
#beginners
<
2020-02-05
>
hindol06: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"

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

šŸ‘ 8
hindol08:02:52

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

hindol13:02:03

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

šŸ‘ 4
hindol09: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.

Frederik09: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

Piotr Brzeziński10: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?

FiVo11:02:07

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

Piotr Brzeziński11: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.

Piotr Brzeziński11:02:15

Oh, ok, thatā€™d make sense.

Piotr Brzeziński11:02:20

Good to know, thanks!

jsyrjala11:02:12

That stopped being allowed somewhere around clojure 1.9 or 1.10

FiVo12:02:40

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

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

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

FiVo12:02:10

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

Alex Miller (Clojure team)13:02:43

Up to 8 entries, but you shouldnā€™t rely on that

FiVo14:02:28

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

Alex Miller (Clojure team)14:02:35

maps and sets are not ordered, full stop

Alex Miller (Clojure team)14:02:57

relying on implementation details will make you broken

Alex Miller (Clojure team)14:02:27

what problem are you actually trying to solve?

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

Alex Miller (Clojure team)17:02:52

you described a context not a problem...

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

Alex Miller (Clojure team)18: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

Alex Miller (Clojure team)18:02:53

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

Alex Miller (Clojure team)18:02:08

Really what youā€™re doing here is parsing, not reading

Alex Miller (Clojure team)18: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)

Alex Miller (Clojure team)18:02:33

Those are similar but different goals

FiVo18:02:27

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

Alex Miller (Clojure team)19:02:50

The analyzer May be more than you need

Alex Miller (Clojure team)19:02:18

Other people have written parsers for tooling

FiVo12:02:12

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

Michael J Dorian15: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.

Alex Miller (Clojure team)15:02:29

usually, you should use require to initiate loading

Alex Miller (Clojure team)15:02:00

and not use load or load-file directly

Michael J Dorian15: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

Alex Miller (Clojure team)15:02:43

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

Alex Miller (Clojure team)15: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

Alex Miller (Clojure team)15: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

Alex Miller (Clojure team)15:02:46

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

Michael J Dorian15: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.

chocksmith18: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?

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

Alex Miller (Clojure team)19: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?

chocksmith19:02:30

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

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

Alex Miller (Clojure team)19: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

chocksmith19:02:50

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

Alex Miller (Clojure team)19:02:00

any chance you have a user.clj file somewhere?

chocksmith19:02:25

I do not. Should I?

Alex Miller (Clojure team)19:02:38

no, but that affects early initialization

Alex Miller (Clojure team)19:02:59

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

Alex Miller (Clojure team)19:02:23

is ClojureHandler source somewhere?

chocksmith19:02:41

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

ghadi19:02:56

ClojureHandler isn't something in the clojure distribution

Alex Miller (Clojure team)19:02:10

com.nitryx.brkmopt.util.ClojureHandler

chocksmith19:02:20

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

chocksmith19:02:22

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

Alex Miller (Clojure team)19: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.

Alex Miller (Clojure team)19:02:29

more than the line would be helpful

Alex Miller (Clojure team)19:02:57

presumably the line is loading the Clojure class :)

chocksmith19:02:30

there it is:

chocksmith19: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;

chocksmith19:02:19

the line that throws the Exception is:

chocksmith19:02:27

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

bfabry19:02:05

my guess: you need to require that ns

āž• 4
bfabry19:02:09

well, load

Perry Fraser19: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)?

Perry Fraser19: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

Alex Miller (Clojure team)19:02:40

well, first - what ns / function is it

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

chocksmith19: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?

chocksmith19:02:44

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

Alex Miller (Clojure team)19: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");
}

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

chocksmith19:02:25

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

Alex Miller (Clojure team)19: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..."

Alex Miller (Clojure team)19:02:03

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

chocksmith19: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 ?

Alex Miller (Clojure team)19:02:53

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

Alex Miller (Clojure team)19:02:43

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

šŸ™‚ 4
chocksmith19:02:57

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

chocksmith19: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

12
chocksmith19: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)

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

šŸŽ‰ 4
Alex Miller (Clojure team)20:02:03

finally, something to boost my stackoverflow rep

šŸ˜‚ 12
Alex Miller (Clojure team)20:02:33

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

Michael J Dorian20:02:49

Do they accept karma?

Alex Miller (Clojure team)20:02:38

nor github stars

šŸ˜ž 4
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"

Perry Fraser21: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?

Perry Fraser21: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.

Andrew21:02:20

You could use a promise

Perry Fraser21:02:18

Huh. Was looking through https://clojure.org/reference/ā€¦, didnā€™t think that that wouldnā€™t include core.async. Thank you.

Perry Fraser21:02:53

Yeah, this seems to be what I want. šŸ™

šŸ‘ 4
jwoods21: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))

jwoods21: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 %))

šŸ‘ 4
jwoods21: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

Piotr Brzeziński22: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.

Piotr Brzeziński22: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.

šŸ‘ 12
thanks 4
Ivan Koz23:02:26

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

thanks 4
practicalli-johnny14: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

Piotr Brzeziński14: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 šŸ˜„

Piotr Brzeziński14:02:05

Thanks for the links, Iā€™ll give them a look!