clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Tanel 2021-02-04T03:52:11.180900Z

What's the best way of debugging why an application takes so long to start? I'm using Clojure component along with a jetty HTTP server, a HikariCP instance and a few RabbiMQ listeners:

$ lein start
2021-02-04 05:50:48.848 INFO  [org.eclipse.jetty.util.log] - Logging initialized @17043ms to org.eclipse.jetty.util.log.Slf4jLog
start alias:
:aliases {"start" ["with-profile" "dev" "trampoline" "run"]}

2021-02-04T18:13:36.198700Z

FWIW there's also a "LEIN_FAST_TRAMPOLINE" flag that makes lein use a cached classpath for the trampoline task trampoline makes lein use a single jvm (like clj does) and fast trampoline makes it reuse a cached classpath (like clj does)

p-himik 2021-02-04T03:55:36.181100Z

FWIW I have found significant improvements in startup time when I got rid of lein altogether. It's been quite some time ago and I didn't try to dig deeper.

Tanel 2021-02-04T04:02:35.181300Z

Are you using deps.edn? How does it compare to lein in your opinion?

seancorfield 2021-02-04T04:05:34.181500Z

The Clojure CLI and deps.edn is focused on building a classpath and running some code. That's it. Leiningen is a full-on build tool that does "everything".

p-himik 2021-02-04T04:06:10.181700Z

I find it much simpler and much more pleasant to work with in my use cases. The only hiccup for me was working with Java sources that are shipped within the same project. But there are ways around that.

seancorfield 2021-02-04T04:06:24.181900Z

That said, there are lots of community-provided tools for the CLI that provide all the same "features" as Leiningen but a la carte instead of bundled.

seancorfield 2021-02-04T04:07:05.182100Z

We run a 100K line codebase with the CLI. A monorepo with three dozen subprojects and we build over a dozen production services from it.

seancorfield 2021-02-04T04:07:56.182300Z

(we switched away from Leiningen to Boot about five/size years ago, then switched to the CLI/`deps.edn` back in 2018 I think)

seancorfield 2021-02-04T04:10:51.182500Z

As for your original Q @tanel.kriik building an uberjar with AOT compilation will really help startup time.

seancorfield 2021-02-04T04:11:47.182700Z

We had several processes that took 10-20 seconds to start up from source but only take up to about five seconds to start as AOT'd uberjars.

Tanel 2021-02-04T04:12:57.182900Z

I see. Thanks for the suggestions!

vemv 2021-02-04T04:20:49.183100Z

> What's the best way of debugging why an application takes so long to start? How much time are we talking about? deps.edn can shave a few seconds (1 fewer JVM + cached classpath calculation) but that might not make a difference. My app at work takes ~4m to boot from source, it basically boils down to how many clojure namespaces are there. The clj compiler isn't particularly fast

seancorfield 2021-02-04T04:22:55.183300Z

@vemv See the OP -- it showed about 17 seconds to get to initializing the logging.

πŸ‘ 1
Tanel 2021-02-04T04:25:07.183600Z

@vemv So 17 seconds is quite fast compared to yours. πŸ˜…

vemv 2021-02-04T04:27:32.183800Z

@ Sean whoops, didn't squint hard enough πŸ˜‡ Yeah in this case deps.edn can make a difference. To be sure, you can ps aux | grep java and copy the java command that Lein generates (`lein run` is a JVM program which ultimately simply builds a java invocation to be executed in a new process). Then you can run that java invocation from scatch, measuring your app with zero Lein overhead If you perceive a difference, sure go ahead with deps! (remove trampoline for extra accuracy)

πŸ‘ 1
seancorfield 2021-02-04T04:35:05.184300Z

@tanel.kriik If it's a fairly straightforward process, it's probably just going to be clojure -M:dev -m your.main.namespace where :dev is a deps.edn alias that is equivalent to whatever special stuff you have in your dev profile in project.clj and your.main.namespace would be, well, your main namespace.

πŸ‘ 1
West 2021-02-04T06:04:19.186Z

How do I use a java dependency that’s not on maven central repository? I want to use https://www.w3.org/Style/CSS/SAC/ in my Clojure project.

phronmophobic 2021-02-04T08:13:52.189700Z

https://clojure.org/reference/java_interop

West 2021-02-06T04:51:07.267800Z

I’m having a lot of trouble understanding this document. I have no idea how java works at all. I have no problem using java.lang stuff, but this I can’t seem to wrap my head around.

West 2021-02-06T04:51:20.268Z

West 2021-02-06T04:54:34.268300Z

At the very least my completion engine caught this information about the class/method.

beders 2021-02-06T05:02:25.268500Z

are you familiar with OOP? Calling something in Java requires referencing a method, which only exists in two or three contexts: a static method of a class/interface or a non-static method. For that one you need an instance of a class - an object. CSSOMParser/parseStyleSheet refers to this: http://www.massapi.com/method/com/steadystate/css/parser/CSSOMParser.parseStyleSheet.html#Example0 and is a non-static method and can only be called on an object. The sample code linked shows you can create such an object. Does this level of detail help?

West 2021-02-06T05:17:38.268900Z

I’m not familiar with OOP at all. So I have to create an instance of a class. Would I do that using (new CSSOMParser)?

seancorfield 2021-02-06T05:21:50.269100Z

@c.westrom What's your background? Maybe that'll help us help you better...

seancorfield 2021-02-06T05:22:58.269300Z

(if you already said and I missed it, sorry -- feel free to link me to an earlier post)

West 2021-02-06T05:23:40.269500Z

I’m honestly a beginner with clojure and clojurescript. I have no idea really how OOP works other than the basic idea, i.e. Classes are like compound variables and methods are just functions.

seancorfield 2021-02-06T05:24:48.269700Z

And your previous languages are...?

West 2021-02-06T05:24:49.269900Z

The functional simplicity of clojure gave me a lot of confidence in building stuff early on, but I’m having trouble leveraging all these java libraries other than http://java.io and things like that.

West 2021-02-06T05:25:19.270100Z

Before I did shell script and python.

West 2021-02-06T05:26:02.270300Z

I couldn’t wrap my head around objects in python either, unless I was straight up calling a method to do something with strings.

seancorfield 2021-02-06T05:27:11.270500Z

Okay, yeah, I think quite a few people have come to Clojure from Python but yeah, if you're working with Java libs, they'll be a lot more OO than even if you'd been comfortable with OO in Python...

West 2021-02-06T05:28:10.270700Z

Aww man. I guess imma have to learn OOP after all.

seancorfield 2021-02-06T05:30:41.270900Z

You might also want to focus on asking in #beginners rather than in here in #clojure -- the folks who've opted in to help in #beginners are willing to put in a lot of time helping folks with all aspects of coming up to speed on Clojure including spending time working with you on getting more comfortable with OO.

seancorfield 2021-02-06T05:31:47.271100Z

In this channel, folks are likely to make assumptions about what you know and that will probably include assuming you are comfortable with OO.

West 2021-02-06T05:32:48.271300Z

Got it, imma stay in beginners for awhile longer then. Thanks for your advice!

seancorfield 2021-02-06T05:34:03.271500Z

You'll find quite a bit of overlap between the folks who'll likely help you -- but they'll approach it with different assumptions there so hopefully that will help!

phronmophobic 2021-02-04T06:06:18.186100Z

are you sure it's not on maven? https://mvnrepository.com/artifact/org.w3c.css/sac

phronmophobic 2021-02-04T06:08:41.186500Z

to answer your original question, are you using deps.edn? if so, you can use a local jar https://clojure.org/guides/deps_and_cli#local_jar

West 2021-02-04T06:11:39.186700Z

I just found it on maven after searching.

West 2021-02-04T06:11:47.186900Z

That was dumb lol

West 2021-02-04T06:11:54.187100Z

But thank you.

West 2021-02-04T06:12:31.187300Z

Ok, so now I can use it in my deps.edn yeah?

:deps {org.clojure/clojure {:mvn/version "1.10.2"}
        clj-css/clj-css     {:mvn/version "0.1.0-SNAPSHOT"}
        garden/garden       {:mvn/version "1.3.10"}
        org.w3c.css/sac     {:mvn/version "1.3"}}

phronmophobic 2021-02-04T06:17:19.187600Z

yea, adding the maven dep should be the most straightforward

West 2021-02-04T06:53:28.187800Z

Ok, I’m probably missing something dumb here. What’s going on?

phronmophobic 2021-02-04T07:06:09.188Z

if it's a java class, then you need an import, rather than require

West 2021-02-04T07:06:56.188300Z

ah ok let me try that

West 2021-02-04T07:09:22.188500Z

I think I got it. I used a dot when I called one of the methods, then I got a different error saying it got the wrong type. I think this is closer to what I’m looking to do.

West 2021-02-04T07:12:24.188700Z

Thanks for your help!

πŸ‘ 1
mpenet 2021-02-04T07:19:52.189100Z

You might want to uberjar/aot the app if it is for prod, startup will then be faster too

West 2021-02-04T07:55:54.189400Z

Ok, I really don’t understand how to interop with Java libs at all. I’m trying to read http://cssparser.sourceforge.net/gettingStarted.html and get the methods working in clojure. net.sourceforge.cssparser/cssparser {:mvn/version "0.9.29"} Are there any resources on this topic you can recommend?

Helins 2021-02-04T11:36:46.190600Z

There isn't any way of knowing what a namespace currently requires just by using clojure.core, is there ? (ie. without resorting to tools.namespace)

borkdude 2021-02-04T12:12:17.191500Z

@adam678 You can use clj-kondo for this which determines this through static analysis. See https://github.com/borkdude/clj-kondo/blob/master/analysis/README.md

vemv 2021-02-04T12:22:30.191600Z

note that t.n.parse does not have any side-effect (unlike (refresh) and friends)

πŸ‘ 1
2021-02-04T13:04:56.193400Z

Hello all - anytime I try to run lein repl now anywhere, even if there is no project.clj, it throws the following error:

$ lein repl
OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
Retrieving fipp/fipp/0.6.23/fipp-0.6.23.pom from clojars
Retrieving fipp/fipp/0.6.23/fipp-0.6.23.jar from clojars
clojure.lang.Compiler$CompilerException: Syntax error compiling at (fipp/ednize.clj:1:1).
#:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source "fipp/ednize.clj"}
 at clojure.lang.Compiler.load (Compiler.java:7648)
    clojure.lang.RT.loadResourceScript (RT.java:381)
Has anyone run into this, or does anyone know how to get around it?

borkdude 2021-02-04T13:07:46.193900Z

@bendy This might come from something in your ~/.lein

Helins 2021-02-04T13:09:45.194Z

Good call but needs to be dynamic here

2021-02-04T13:10:00.194500Z

@borkdude legend, that solved it! No idea what was even in there πŸ˜‚

Helins 2021-02-04T13:29:19.195800Z

Almost! Unless I am doing it wrong, it won't show namespaces which aren't aliased nor referred. But it might be good enough, who knows... thanks!

vemv 2021-02-04T13:47:54.196200Z

root cause is https://github.com/brandonbloom/fipp/issues/72

Shantanu Kumar 2021-02-04T18:12:12.198600Z

Does mutating transient vector not change the root reference? E.g.

(let [t (transient [])] (dotimes [i 20000] (conj! t i)) (persistent! t))
appears to work fine

2021-02-04T18:14:24.199100Z

@kumarshantanu it's allowed to, but doesn't promise to

2021-02-04T18:14:35.199500Z

your code will break if you rely on it

dpsutton 2021-02-04T18:14:43.199700Z

> Note in particular that transients are not designed to be bashed in-place. You must capture and use the return value in the next call. https://clojure.org/reference/transients

2021-02-04T18:15:20.200800Z

"mutating a transient vector" - a conj! is allowed to mutate, doesn't promise to mutate

Shantanu Kumar 2021-02-04T18:15:22.200900Z

Because for transient maps the root ref updates, so I was a bit surprised at transient vectors.

dpsutton 2021-02-04T18:18:48.203300Z

another thing to remember is that reading code doesn't particularly help you. This is explicitly not in the contract so reading code has to be phrased as "the implementation right now does..."

jaihindhreddy 2021-02-04T18:18:56.203600Z

The conj! impl of transient vectors https://github.com/clojure/clojure/blob/140ed11e905de46331de705e955c50c0ef79095b/src/jvm/clojure/lang/PersistentVector.java#L579-L608, but as others have pointed out, the docs instruct us to capture the return.

2021-02-04T18:20:02.205Z

The supported contract is always to use the return value of any transient operation, just as you would for a persistent operation. It might be identical? to what you gave it, it might not. If you use the original reference for further operations, you are breaking the contract, and might never get any error or warning that you are doing so.

πŸ’― 1
2021-02-04T18:21:23.206300Z

and the confusion arises because in many cases, it works as you would hope, even if you break the contract

alexmiller 2021-02-04T18:21:41.207500Z

There is a pending ticket to make the doc string clearer about this

jaihindhreddy 2021-02-04T18:21:46.207700Z

I wonder why that is, however. Is the reasoning to keep code close to the functional equivalent (as the docs describe), or to have flexibility to return a different thing in the future (much like array-maps are switched out to hash maps)...

dpsutton 2021-02-04T18:22:16.208100Z

yeah i was disappointed that (find-doc "bash") didn't return any warning. glad there's a ticket

2021-02-04T18:22:47.208200Z

I would guess the second reason.

πŸ‘ 1
2021-02-04T18:29:06.208500Z

transients are sort of a fusion between a mutable datastructure and an immutable one. internally they build the same tree structure as the immutable datastructure, they just mutate the structure when they can, otherwise they return a new tree. so for example if each node in the tree can hold 32 elements, when you add elements 0 to 31 you just mutate in place, and then once you add the 32 it has to split the tree

πŸ’― 1
2021-02-04T18:29:15.208700Z

If Rich didn't want that flexibility, then why would the current implementation sometimes return a non-identical transient collection?

2021-02-04T18:30:06.208900Z

I have a very difficult time believing the answer "that was an accident of the implementation"

βž• 1
jaihindhreddy 2021-02-04T18:30:48.209100Z

> why would the current implementation sometimes return a non-identical transient collection? I didn't know this is the case.

2021-02-04T18:31:33.209500Z

but it is really two questions right? why does that behavior pattern exist, and why do transients expose it instead of hiding it

2021-02-04T18:32:02.209700Z

That is the reason that some people bring up the question of "hey, this code using transients isn't working" -- i.e. they wrote code that ignored the return value, and sometimes it works (when the implementation returns an identical object), but in other cases, it fails (when the transient operation returns a non-identical object)

2021-02-04T18:32:49.209900Z

@hiredman Agreed that an implementation of transients could guarantee to return an identical object always, if the implementer wanted to.

jaihindhreddy 2021-02-04T18:32:52.210100Z

> why do transients expose it instead of hiding it The docs being what they are, isn't it fair to say that transients do hide this?

2021-02-04T18:33:34.210400Z

it depends on what you think is being hidden

jaihindhreddy 2021-02-04T18:33:50.210600Z

"the fact that the output may be identical?"

2021-02-04T18:33:50.210800Z

they expose the fact that sometimes you get a new structure instead of mutating the existing one

πŸ‘ 1
2021-02-04T18:34:10.211Z

they could hide that if the whole thing was wrapped in a mutable reference

βœ”οΈ 1
jaihindhreddy 2021-02-04T18:34:19.211200Z

ahh, I'm coming at it from the other side.

2021-02-04T18:34:27.211500Z

The true answer to "why is it this way?" is truly only known by the implementer, if you want all the details and tradeoffs involved in the decision

2021-02-04T18:35:38.211900Z

While it would be possible to design transients that guarantee returning an identical object always, I believe it would involve an extra level of indirection near the root, in cases where the implementer wants to switch data structures, e..g from array-map to hash-map. That would have some performance cost. Maybe one that some implementers would find acceptable, and others would not find acceptable.

2021-02-04T18:36:11.212200Z

La mort de l'auteur for programming

πŸ˜‚ 1
wombawomba 2021-02-04T20:07:00.213100Z

What's a good lightweight library for making/unpacking tar files?

alexmiller 2021-02-04T20:09:42.213400Z

apache io probably has that

borkdude 2021-02-04T20:10:48.214300Z

@wombawomba clj-commons/fs also comes with this lib (the apache one): https://github.com/clj-commons/fs/blob/master/src/me/raynes/fs/compression.clj but note that tar is now available on all major OSes, including Windows, so shelling out isn't even that strange to avoid this dep

wombawomba 2021-02-04T20:12:19.215100Z

cool, thanks β€” will take a look πŸ™‚

ag 2021-02-04T21:42:56.217200Z

I need a get-in that works not only for sequence of keys but also for functions, I can probably spend some time writing it, but perhaps someone already has done that before. So I basically I want something like this:

(get-in* {:foo [{:bar 1}]}
         [:foo first :bar]) ; => 1
(get-in* {:foo [{:bar 1}
                {:bar 42}]}
         [:foo second :bar]) ; => 42
Can you share a snippet?

Ed 2021-02-05T09:38:38.227300Z

you could use reduce:

(let [get-in* (partial reduce #(%2 %1))] (get-in* {:foo [{:bar 1}]} [:foo first :bar]))

ag 2021-02-04T21:43:57.218Z

Please don't suggest to use Meander or Specter.

πŸ˜ƒ 1
dpsutton 2021-02-04T21:44:04.218200Z

its not fully general but you can use 0 here

dpsutton 2021-02-04T21:44:47.218900Z

it doesn't work for functions but in this tangible example vectors are associative with respect to index. so (get-in coll [:foo 0 :bar]) will achieve your result

ag 2021-02-04T21:45:30.219500Z

ah, right... that does it. thanks Dan!

3