clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
2021-04-21T04:32:28.340100Z

hello, newbie here 🙂 quick question about developing a restapi server with clojure. I’m currently using http-kit and compojure, and I wonder if there is any clojure-specific conventional structure for api server? Typically with Java (springboot, for example), I’m accustomed to dividing the project into controller, service, and dao. I’ve seen some github projects in clojure that did almost the same; but I wonder if there’s any better way (or clojure way) of doing so.

seancorfield 2021-04-21T04:35:54.341400Z

@tlonist.sang The only real guidance in Clojure is to try to keep side-effecting code from pure code but folks organize their web apps in all sorts of different ways, depending on which approach to design they follow.

seancorfield 2021-04-21T04:38:18.342800Z

You won't find things like DAOs really because we have no objects 🙂 but for basic CRUD most folks will just traffic in hash maps going in and out of the DB via next.jdbc (which supersedes clojure.java.jdbc).

seancorfield 2021-04-21T04:38:59.343600Z

Separating your pure business logic out from both the HTTP side of the house and the persistence side of the house is fairly common.

2021-04-21T04:42:41.345500Z

@seancorfield Wow, from the author himself! I’ve been using next.jdbc throughout my project this year. Thanks for the guidance. I like how there is no set-rule!

seancorfield 2021-04-21T04:45:45.346800Z

If you've read Clojure Applied, I suspect that's going to be some of the best "guidance" you'll find. Perhaps a few of the Domain-Driven Development talks around Clojure (I haven't really followed that path but some people like it a lot).

👍 1
🎉 1
heyarne 2021-04-21T05:39:33.348200Z

is anyone using deps.edn on nix(os) and got jogl to work? it seems i can't for the life of it figure it out and could very much use any help 🙂

Timofey Sitnikov 2021-04-21T10:56:06.351800Z

Good morning Clojurians, can anyone point me to a good way to handle passwords in an edn file? I do not want to push passwords into repo, is there a common way to handle it? For example in configuration like this:

:postal/email-config {:host  "<http://smtp.gmail.com|smtp.gmail.com>"
                        :email "<mailto:email@gmail.com|email@gmail.com>"
                        :pass "password"
                        :port 587
                        :tls true}}

👀 1
flowthing 2021-04-21T11:23:11.352100Z

Aero handles this kind of thing quite well, I think: https://github.com/juxt/aero#hide-passwords-in-local-private-files If you don't (want to) use Aero, you could just add a tagged literal (https://clojure.org/reference/reader#tagged_literals) that does something similar, I guess.

1
➕ 1
p-himik 2021-04-21T11:24:04.352400Z

For GMail specifically, you should use an application token instead of the full email account credentials.

p-himik 2021-04-21T11:24:56.352700Z

On that particular section of Aero documentation, see some discussion here: https://github.com/juxt/aero/issues/73

1
p-himik 2021-04-21T11:26:55.353100Z

FWIW, Heroku uses this approach: https://12factor.net/config Some more in-depth description of the Heroku approach specifically: https://blog.heroku.com/twelve-factor-apps

Timofey Sitnikov 2021-04-21T11:30:00.353600Z

@p-himik, assuming that the machine is secure and I am the only user of the machine, being able to read the env data is not a concern?

p-himik 2021-04-21T11:31:40.354600Z

When a machine is indeed secure, it is not a concern. When a machine is not secure, everything is a concern - you simply cannot store sensitive information there, at all.

Yehonathan Sharvit 2021-04-21T11:33:38.356900Z

I am looking for a way to check whether two huge maps (around 600K entries) are equal. The issue is that some of the values are of type org.joda.time.DateTime and the values are not always equal, although they refer to the same time. I would like to find an implementation of a function like clojure.data/diff that allows to customize the definition of equality. I could use clojure.data/diff but the performance is not good enough for my huge maps.

marciol 2021-04-22T18:26:27.451700Z

@mauricio.szabo and I had this same problem recently

mauricio.szabo 2021-04-22T19:08:08.452600Z

mauricio.szabo 2021-04-22T19:08:14.452800Z

Yes, the way I ended up doing was to implement a deep-compare, above

mauricio.szabo 2021-04-22T19:08:48.453Z

You can add an (instance?...) check on the cond above, and add the data comparision

mauricio.szabo 2021-04-22T19:10:54.453200Z

(the map that I return is mostly because I used it on a test, so this would become the custom message so I could know, when things failed, what's wrong. Not really that useful when I had to sort collections, to be honest, but it helped compare two 3.5mb-sized data structures)

Yehonathan Sharvit 2021-04-21T11:34:13.357700Z

Maybe someone knows a lib out there that provides such a function. I’d prefer avoiding to write an implementation of my own, if possible

Timofey Sitnikov 2021-04-21T11:34:32.357800Z

OK, this makes sense.

2021-04-21T11:38:02.358Z

One possibility would be to replace all occurrences of org.joda.time.DateTime in the two maps you want to compare with a new type of value that does return true for clojure.core/= when you compare them, and then compare those maps using clojure.core/=

2021-04-21T11:38:20.358200Z

or clojure.data/diff

2021-04-21T11:40:02.358400Z

Or if there is some kind of method you can call on instances of org.joda.time.DateTime that returns a "canonical" value for each instance, such that the canonical values are clojure.core/= to each other when they are the same time, then you don't need a different type.

2021-04-21T11:43:17.358600Z

An example of the "wrapper type" approach would be to create a custom Clojure deftype, and define your own custom equiv method (which is what clojure.core/=calls under the JVM hood) that calls the isEquals method of joda.time instead of equals: https://www.joda.org/joda-time/apidocs/org/joda/time/base/AbstractInstant.html#isEqual-org.joda.time.ReadableInstant-

Yehonathan Sharvit 2021-04-21T12:44:19.360100Z

@andy.fingerhut Is equiv a method of a protocol in Clojure?

Yehonathan Sharvit 2021-04-21T12:44:34.360500Z

I know that in ClojureScript it is

Marc O'Morain 2021-04-21T12:50:45.362500Z

A question about reading Clojure docstrings:

(dotimes bindings &amp; body)

bindings =&gt; name n
 Repeatedly executes body (presumably for side-effects) with name
bound to integers from 0 through n-1.
Where is the form of the bindings documented? Is there anywhere that says that bindings should be a vector?

Marc O'Morain 2021-04-21T12:52:04.362800Z

I always have to look on http://clojuredocs.org to see an example usage for functions like this, I find the dosctring doesn’t give me enough explanation to know how to write the form.

2021-04-21T12:53:14.363Z

It is not in Clojure, no. ClojureScript was implemented years after Clojure, and some have asked whether going back to reimplement Clojure using protocols, similar to how ClojureScript does, would be useful, but it would be a big churn in the code base for probably not many advantages, and would break any libraries that rely on the Java class structure in Clojure's implementation today.

thheller 2021-04-21T12:53:50.363200Z

don't know where it is documented but bindings are vectors yes

nilern 2021-04-21T12:55:02.363400Z

For dotimes, it just isn't documented

nilern 2021-04-21T12:55:47.363600Z

&gt; (doc let)
-------------------------
clojure.core/let
  (let [bindings*] exprs*)
([bindings &amp; body])
Special Form
  binding =&gt; binding-form init-expr

  Evaluates the exprs in a lexical context in which the symbols in
  the binding-forms are bound to their respective init-exprs or parts
  therein.

  Please see <http://clojure.org/special_forms#let>
Spec
  args: (cat :bindings :clojure.core.specs.alpha/bindings :body (* any?))
  ret: any?

nilern 2021-04-21T12:56:57.363800Z

More common stuff like let has Specs for this purpose

2021-04-21T12:58:01.364400Z

Note: I haven't actually tried writing the deftype that defines its own equiv myself, and could be forgetting some nuances, but I can point you at existing libraries that do this successfully, e.g. priority-map https://github.com/clojure/data.priority-map/blob/master/src/main/clojure/clojure/data/priority_map.clj#L325

restenb 2021-04-21T12:58:19.365Z

is there some built-in utility to "parse" error traces of type clojure.lang.ExceptionInfo ?

nilern 2021-04-21T13:00:47.366300Z

It's just a normal exception? https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html#getStackTrace--

2021-04-21T13:01:54.366500Z

Hmmm, looking at the definition of clojure.lang.Utils/equiv again to remind myself, it looks like overriding equiv will work if your type implements the IPersistentCollection interface, but otherwise your custom type would need to override equals

vlaaad 2021-04-21T13:01:57.366700Z

there is also Throwable-&gt;map to convert exception to data

👀 1
🙏 1
vlaaad 2021-04-21T13:02:34.367Z

and clojure.main/ex-triage to analyze that exception

restenb 2021-04-21T13:04:04.367200Z

not sure it's necessarily just a normal exception. clj-http generates this whenever a request bounces, for example: clojure.lang.ExceptionInfo: clj-http: status 401 {RESPONSE BODY HERE}

restenb 2021-04-21T13:04:18.367400Z

i'd just like to parse the response body directly

restenb 2021-04-21T13:04:51.367600Z

perhaps it's just (.getMessage ex) then

restenb 2021-04-21T13:05:10.367800Z

at least that should give me status 401 or the body

nilern 2021-04-21T13:05:47.368100Z

Hopefully ex-data gives you the body

nilern 2021-04-21T13:06:00.368300Z

I thought you wanted to traverse the stack trace

nilern 2021-04-21T13:11:29.368500Z

clj-http seems to use Slingshot, so I don't know whether ex-data works like I would like but yeah at least ex-message/`.getMessage` gets you something

borkdude 2021-04-21T14:40:01.369700Z

In a Clojure project, it doesn't matter if I AOT with Java 11 or Java 8, because the Clojure compiler only ever emits Java 8 compatible bytecode, is this correct? I understand it matters for Java classes but not for compilation of Clojure units, true?

dpsutton 2021-04-21T14:45:29.370300Z

the reflector might choose methods that exist in java 11 that do not exist in java 8 if you build in 11 and then run in 8 right?

borkdude 2021-04-21T14:46:33.370900Z

True, but then you're programming against JDK 11 specific methods anyway, so AOT isn't the culprit

dpsutton 2021-04-21T14:49:16.373Z

i thought there was some "first acceptable method" type thing to the reflector as well. Doesn't that mean it could emit a call to something that won't exist in java 8? Even though you aren't explicitly using methods from java 11?

borkdude 2021-04-21T14:50:39.374900Z

Hmm.

dpsutton 2021-04-21T14:51:22.376200Z

(I'm way out of my depth here so perhaps I'm stretching, but i bet the nature of any issues that pop up will be quite annoying to diagnose, so perhaps stretching is called for)

nilern 2021-04-21T14:51:27.376300Z

At least theoretically they could add some overload Object get(Object) when previously there was only Object get(long) and now you are linked to the new one without reflection whereas if you AOT in Java 8 you get a reflective call

dpsutton 2021-04-21T14:54:16.378400Z

i suspect ghadi or alex would have some experience in this if they see this conversation

nilern 2021-04-21T14:55:48.379400Z

javac can mess this up too: > Note: Merely setting the target option does not guarantee that your code actually runs on a JRE with the specified version. The pitfall is unintended usage of APIs that only exist in later JREs which would make your code fail at runtime with a linkage error. To avoid this issue, you can either configure the compiler's boot classpath to match the target JRE or use the http://www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin/ to verify your code doesn't use unintended APIs. In the same way, setting the source option does not guarantee that your code actually compiles on a JDK with the specified version. To compile your code with a specific JDK version, different than the one used to launch Maven, refer to the https://maven.apache.org/plugins/maven-compiler-plugin/examples/compile-using-different-jdk.html example. > https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html

borkdude 2021-04-21T14:57:00.381100Z

@nilern in javac you can set --release

alexmiller 2021-04-21T14:57:08.381300Z

from JDK perspective, there is some danger of resolution using newer JDK methods if they are available in the JDK you are using

alexmiller 2021-04-21T14:57:41.382300Z

this happened with Clojure in fact when they added a new Collection method override in Java 10 (11? don't remember) that made existing Collection impls ambiguous

borkdude 2021-04-21T14:57:49.382500Z

so tl;dr it's probably always better to use a jdk 8 if you target jdk 8 in your CI?

alexmiller 2021-04-21T14:58:26.383300Z

if you want it to be compatible with Java 8+ that is probably safest

alexmiller 2021-04-21T14:59:09.384200Z

you can actually tell javac to use a different version of the jdk, I guess you could run the Clojure compiler with a jvm using a bootclasspath set to an older jdk possibly

borkdude 2021-04-21T14:59:26.384500Z

do you mean --release ?

alexmiller 2021-04-21T14:59:43.385100Z

I don't know what the modern affordance is, maybe that's it

borkdude 2021-04-21T14:59:48.385300Z

but the Clojure compiler doesn't need / use javac right?

alexmiller 2021-04-21T14:59:56.385500Z

right

nilern 2021-04-21T14:59:57.385600Z

Hmm I see they added that https://www.mojohaus.org/animal-sniffer/

alexmiller 2021-04-21T15:00:04.385900Z

was just making an analogy

borkdude 2021-04-21T15:00:33.386100Z

thanks y'all

alexmiller 2021-04-21T15:01:05.386400Z

--release is for javac?

nilern 2021-04-21T15:01:30.386800Z

Yes it is a javac option

alexmiller 2021-04-21T15:02:20.387100Z

I guess replaces the old source/target stuff

nilern 2021-04-21T15:02:43.387300Z

http://openjdk.java.net/jeps/247

dazld 2021-04-21T15:27:28.389600Z

somewhat related, and realise this is an open question, but what kind of JVM settings do people apply when running clojure code specifically - thinking of things like tuning GC, string compression..? are there any settings that are more relevant for clojure code compared to straight java..?

jumar 2021-04-21T15:32:58.389800Z

I think it may matter in rare cases like ByteBuffer.flip() method - I saw errors like this when an uberjar was compiled with Java 11 but run with Java 8: https://stackoverflow.com/questions/61267495/exception-in-thread-main-java-lang-nosuchmethoderror-java-nio-bytebuffer-flip

jumar 2021-04-21T15:33:41.390200Z

I guess there's no workaround for that or? (javac seems to have --release parameter for cases like this)

seancorfield 2021-04-21T15:34:15.390900Z

@dazld I think that’s going to be very dependent on the particular application you’re running.

seancorfield 2021-04-21T15:34:58.391500Z

Are you going to tune for response times, throughput, fast startup? etc.

jumar 2021-04-21T15:36:16.392200Z

Ah, I didn't notice the rest of the conversation in the main channel - so I suppose that this can become a problem and there's no workaround other than using an older java when building the uberjar

dazld 2021-04-21T15:37:17.393300Z

I suppose I had at the back of mind that clojure code might not look like typical java code, when it's been compiled, and as such the default settings might not be quite right. If that's not true, then that answers the question.

alexmiller 2021-04-21T15:38:00.393700Z

Clojure is designed so that the default settings should be sufficient

alexmiller 2021-04-21T15:38:31.394200Z

if you're running an app in production, you should probably think harder about which GC you're using and how it's tuned

👍 1
alexmiller 2021-04-21T15:38:40.394400Z

but that will be application specific

ghadi 2021-04-21T16:02:13.395500Z

gc throughput -> parallel collector gc latency -> G1 or ZGC speaking broadly

☝️ 1
ghadi 2021-04-21T16:03:04.396200Z

in terms of HotSpot/compiler - leave the defaults

seancorfield 2021-04-21T16:21:41.398600Z

@dazld As a follow-up, specifically on Ghadi’s response, at work we’re on JDK 11 in production and using the G1 collector and that works great for interactive apps and services where latency is important.

dazld 2021-04-21T16:23:51.400Z

@alexmiller @seancorfield @ghadi thanks a ton, that was all really helpful

nilern 2021-04-21T16:25:40.400600Z

I've only used the parallel collector for batch imports and such

2021-04-21T16:29:05.402Z

Hello! Which symbols can be a word separators in Clojure(script) exept of all the types of brackets and spaces?

2021-04-21T16:30:05.402700Z

Editors default is ~!@#$%^&*()-=+[{]}\|;:'",.<>/?` but seems have to be excluded most of them

flowthing 2021-04-21T16:34:04.403Z

You can probably glean the answer from this page: https://clojure.org/reference/reader

✅ 1
2021-04-21T16:35:34.403300Z

Thanks

alexmiller 2021-04-21T16:53:16.403500Z

note that there are things that are explicitly allowed, things that are not allowed, and things left intentionally ambiguous for possible future expansion

Yehonathan Sharvit 2021-04-21T17:24:33.403700Z

Bummer!

2021-04-21T18:03:53.404200Z

Why a bummer? The same approach of defining your own deftype as a wrapper object for times, with your own custom definition of equals, should work, I think.

2021-04-21T18:04:32.404400Z

I'm just clarifying that the method you need to override is not equiv, but equals. clojure.core/= calls equiv on some objects, typically ones defined in Clojure itself, but falls back to Java equals for all others.

Janne Sauvala 2021-04-21T18:14:41.407400Z

I’m watching Nada Amin’s “Programming should eat itself” Strange Loop talk and the concept of meta levels was interesting. Has anyone built something similar on top of Clojure?

alexmiller 2021-04-21T18:28:07.409100Z

you can start nested clojure.main/repl's from inside the Clojure repl - they can be set up to support exiting the nested repl, so you can get some small part of this. what she demonstrated is probably beyond that though

alexmiller 2021-04-21T18:29:13.409900Z

(talk: https://www.youtube.com/watch?v=SrKj4hYic5A for reference)

👀 1
🤘 1
Yehonathan Sharvit 2021-04-21T18:30:36.410100Z

Is there a way to re-implment equals for DateTime or is it sealed?

Janne Sauvala 2021-04-21T18:35:17.412400Z

Cool! I was just thinking how to launch nested repls:ok_hand::skin-tone-2:

flowthing 2021-04-21T18:37:26.414600Z

Note that nREPL currently doesn’t support nested REPLs, though. Need to use socket REPL or prepl.

Janne Sauvala 2021-04-21T18:42:28.414900Z

Right, I’ll start playing with those repls then

2021-04-21T18:49:28.415200Z

nesting clojure.main/repl's is also a neat way to support remote repls. a nested repl takes over the io streams until it exits, so for remote repls you do the same thing, but instead of taking over the io streams and running a repl, you take over the io streams and wire them to a repl running somewhere else

flowthing 2021-04-21T18:55:17.415400Z

I'm kinda interested in everything related to nested REPLs. When would you do something like that instead of just starting a socket REPL in the remote process and connecting to that?

vemv 2021-04-21T19:03:48.415600Z

haven't watched the talk but https://github.com/gfredericks/debug-repl might be considered a nested one?

2021-04-21T19:12:07.416Z

https://gist.github.com/hiredman/1789849d21be38310694dbf214d60d34 is a poc for interrupting prepl evaluations, but it shows kind of half of the remote repl thing, connecting and sending things, but not wiring up the current repls io to the remote repl

👀 1
2021-04-21T19:13:53.416300Z

https://gist.github.com/hiredman/a2630ea6153d06840a2723d5b2c9698c let's you open a repl at a point in your program and "send it" to connect to your existing repl

dpsutton 2021-04-21T19:17:16.416600Z

i made a history saving repl recently. and i want to add support to saving history and annotating each subform with it's evaled result, similar to the CIDER debugger

dpsutton 2021-04-21T19:18:45.416800Z

made a question on http://ask.clojure.com for cool things done with subrepls: https://ask.clojure.org/index.php/10425/what-cool-things-do-you-do-with-clojure-main-repl

borkdude 2021-04-21T19:27:47.417100Z

This is/was also one of those: https://github.com/technomancy/limit-break

dpsutton 2021-04-21T19:29:22.417500Z

that's awesome!

borkdude 2021-04-21T19:29:50.417700Z

it even works with bb :) (that's how I discovered it, someone told me he used it with it)

2021-04-21T22:27:58.418200Z

This page of Java doc says that the class org.joda.time.DateTime is final, meaning no subclassing in Java: https://www.joda.org/joda-time/apidocs/org/joda/time/DateTime.html?is-external=true

2021-04-21T22:28:55.418400Z

And there is no way to override a method for an existing class in Java that I know of (others might). Wrapping the class in another type and delegating all methods but equals is always possible regardless of final or not.