This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-12
Channels
- # announcements (1)
- # aws (1)
- # babashka (63)
- # beginners (108)
- # calva (12)
- # cider (6)
- # cljdoc (2)
- # cljsrn (33)
- # clojure (150)
- # clojure-europe (28)
- # clojure-nl (13)
- # clojure-spain (1)
- # clojure-spec (8)
- # clojure-uk (25)
- # clojurescript (16)
- # conjure (7)
- # cursive (7)
- # datomic (15)
- # duct (2)
- # eastwood (2)
- # figwheel (1)
- # figwheel-main (1)
- # fulcro (6)
- # graalvm (1)
- # graalvm-mobile (1)
- # helix (6)
- # honeysql (23)
- # integrant (6)
- # introduce-yourself (4)
- # jobs (10)
- # lsp (132)
- # malli (4)
- # meander (1)
- # membrane (1)
- # off-topic (223)
- # pathom (23)
- # pedestal (3)
- # re-frame (18)
- # reagent (13)
- # releases (1)
- # remote-jobs (2)
- # shadow-cljs (68)
- # tools-deps (217)
- # vim (19)
- # xtdb (79)
Anyone ever saw this?
In the repl because it is a trivial def, the compiler doesn't bother generating bytecode
The length limit for strings in bytecode is, uh, either Short/MAX_VALUE or something shorter (I think 16 bit number is yes for strings in dataoutputstream, but the jvm spec may further restrict it, and the library clojure uses to generate bytecode may also restrict it
Huh. I didn't know that!
Thank you @U0NCTKEV8
When publishing an internal clj library with maven, should it be distributed as a jar or an uberjar?
@seancorfield Seems like a thin JAR requires the library’s consumer to list the library’s dependencies in the consumer’s own project.clj
… how can I not require the library consumer to list those?
@aaron51 No, the library's dependencies would be automatically fetched by the the CLI / lein
etc, as part of dependency management. That is automatic.
An uberjar is generally an application, not a dependency that others would use.
is there already a macro which lets me mix let
and letfn
without excessive indentation? Or do I need to write this myself. not very difficult of course
there is! https://github.com/reducecombine/with I never bothered promoting it much - I have enough on my plate with other initiatives. But the principle seems sound and simple to me.
I tend to just use (let [f (fn [_] ...)] ...)
. I don't tend to find letfn
any more readable. I'm not aware of anything that does that. But like you say, it's probably not too hard to write 😉
the difference between let
and letfn
is bigger than syntax
(let [f (fn [counter]
(when (< counter 10) (f (inc counter))))]
(f 0))
this code ^ is broken because f
in function’s body is unresolved.
if you switch to use letfn
- it will be bounded
(letfn [(f [counter]
(when (< counter 10) (f (inc counter))))]
(f 0))
so letfn
is useful if you want mutual recursionif you need to recurse you can write
(let [f (fn f [counter]
(when (< counter 10) (f (inc counter))))]
(f 0))
I just wanted to point out that there is a significant difference between let and letfn beside readability)
fair enough ... guess that I went for readability because the op mentioned indenting rather than anything else 😉
recurse? do you mean recure?
def: recure: do do something recursively.
recurse: to say a naughty word multiple times.
yes, then that is correct.
yes I also use (let [f (fn ...
probably because the same (or worst - different information) could be presented in “content-type” header
the difference between let
and letfn
is bigger than syntax
(let [f (fn [counter]
(when (< counter 10) (f (inc counter))))]
(f 0))
this code ^ is broken because f
in function’s body is unresolved.
if you switch to use letfn
- it will be bounded
(letfn [(f [counter]
(when (< counter 10) (f (inc counter))))]
(f 0))
so letfn
is useful if you want mutual recursionSay I have a map {:id value}... I need to add to this map where the text for key and the value are dynamically generated... so how do I add to this map such that {:id value :<dyn key> <value} ?
beautiful!!
muchos gracias!!
Or do you mean that you have a string that you need to turn into a keyword? Then keyword
.
If you have a map literal right there, you can just {:a 1, (keyword "b") 2}
.
oh yeah this'll work too. Thanks much!!
I have a Linux server on a mixed Linux/Windows internal network. A remote client is sending me a UNC path like "\\\\10.50.90.18\\ITS Tool\\xml\\foo.xml"
, and I want to read the file at that address. Do I need something like jCIFS (https://jcifs.org), or should I just bug our sysadmin to set up an smbmount volume on the server?
If you know for sure that that path can be accessed with SMB, then the answer is "depends". By using SMB from your code, you have (or should have - I don't know the specifics of the protocol and its implementations) the ability to time out over a request. Even local networks are not 100% reliable given that a server can simply be rebooted or misconfigured. But at the same time, using SMB from your code would tie you to that particular protocol. By using an SMB volume, it's the opposite - you no longer have any control over how long you should wait for something to happen. I have seen quite a few times when a program just hangs there for hours when the share suddenly becomes unavailable mid-transfer. But as an upside, you don't depend on a protocol anymore.
And if you don't know whether it's SMB, then definitely bug the admin to at least figure out what should be used. :)
That’s valuable information, thanks!
Hi gals and guys, I have a question about protocols and records. I defined several records based on a protocol like so:
(defprotocol PaymentFrequency
"Protocol for payment frequencies"
(^String toString [_] "Returns a human-friendly string describing the payment frequency")
(next-payment-frequency [_this options] "Returns a payment frequency set to next start date"))
(defrecord Weekly [^Temporal start-date]
PaymentFrequency
(toString [this]
(obj-to-str this))
(next-payment-frequency [_this options]
(-> (-> start-date (jt/plus (-> 7 jt/days)))
(adjust start-date options)
(->Weekly))))
(defrecord BiWeekly [^Temporal start-date]
PaymentFrequency
(toString [this]
(obj-to-str this))
(next-payment-frequency [_this options]
(-> (-> start-date (jt/plus (-> 14 jt/days)))
(adjust start-date options)
(->BiWeekly))))
Is there a way to not to have to write the toString
implementation for every defrecord
?
I tried to do something like this but it won't work:
(defprotocol Stringable
(^String toString [_] "Returns a human-friendly string describing the payment frequency"))
(extend PaymentFrequency
Stringable
{:toString #(obj-to-str %)})
I get this compilation error:
java.lang.ClassCastException: class clojure.lang.PersistentArrayMap cannot be cast to class java.lang.Class (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.lang.Class is in module java.base of loader 'bootstrap')
Any idea please?Some ideas: • Make it a regular function. If it's doing the same thing everything, you don't need protocol dispatch. • If you really need a default, and want to override it sometimes: ◦ Make another protocol for toString ◦ Implement it on Object, as the default ◦ Then implement it on our more specific records as required ◦ Not 100% sure on this, it's been awhile since I've tried something like this.
you're extending PaymentFrequency
which is a protocol. And that var of PaymentFrequency
is a map holding some information (:on :on-interface :sigs :var :method-map :method-builders)
. That's the tangible error there. But you cannot extend a protocol with another protocol like that.
Thank you for the ideas @U7ZL911B3!
Thank you for the explanation @U11BV7MTK!
also, in case toString
is your actual method and not a random placeholder: every class extends Object which has toString, and it's not a good idea to make your own special toString on a different class
Yeah, thank you @noisesmith, I think I read that somewhere.
Something else to consider here is to make your to-string
into a multimethod that dispatches on type. If you want to have multiple different records be converted to strings in the same way, you can either have a :default value for the multimethod, or you can use clojure's hierarchies.
@U01EGMY9273 the fix is easy
(defprotocol PaymentFrequency
"Protocol for payment frequencies"
(next-payment-frequency [_this options] "Returns a payment frequency set to next start date"))
(defrecord Weekly [^Temporal start-date]
Object
(toString [this]
(obj-to-str this))
PaymentFrequency
(next-payment-frequency [_this options]
(-> (-> start-date (jt/plus (-> 7 jt/days)))
(adjust start-date options)
(->Weekly))))
@noisesmith the override of toString in my first snippet works as is. I just wanted to not have to write it for every record.
@U050ECB92 when you convert those records to string, without the toString override, you get something that is not meaningful: [email protected]
for instance.
With a custom toString I can do things like:
(str (->Weekly (java-time/local-date 2021 7 12)))
=> "Weekly (start-date: 2021-07-12)"
right, but you want the toString on Object, not some new method of that name, AFAIK what you have there is only working by accident
@noisesmith interesting! But then I don't want to change the Object toString for the reasons we know. Would it help if I use an interface instead of a protocol for PaymentFrequency?
"for the reasons we know" - what reasons?
the intended usage of toString is that every class would implement it as appropriate, it's meant to be overridden
it's a bigger problem to use the same method name and arg list on a different interface
@U5NCUG8NR the thing is - it's overkill to define a clojure multimethod or hierarchy here - the jvm already dispatches toString based on class and expects every class to have a definition
if two classes want to have the same custom logic, a function is a perfect abstraction for that IMHO
> "for the reasons we know" - what reasons? (edited) For the one you mentioned above: https://clojurians.slack.com/archives/C03S1KBA2/p1626104811350100?thread_ts=1626104099.348100&cid=C03S1KBA2
I'm more or less assuming (possibly incorrectly) that the name of the method matching the one on java.lang.Object is coincidence @noisesmith
It was intentional actually 😉
@U01EGMY9273 you are supposed to extend Object toString, that's what it's for - the problem is adding a toString method with the same arg list on some other interface
I see.
(defmacro define-schedule-record [& stuff]
`(defrecord [email protected] Object (~'toString [this#] (obj-to-string this#))))
thx @U3JH98J4R that sounds like a good option! 🙂
@U3JH98J4R btw you don't need the quote/unquote on Object, the resolved version is going to be correct
@U01EGMY9273 I will stress that you prob. don't want to use toString for a representation intended to be seen by users
yup thx
Does anyone have any experience running Etaoin inside a docker container? Seem to be running into an issue with the default timeout. I see that there are some wrapper functions that should be able to change the default timeout but not quite sure how to get it to work. This is the error
{:type :etaoin/timeout, :message nil, :timeout 7, :interval 0.33, :times 22, :predicate #object[etaoin.api$wait_running$fn__5604 0x6464f017 "[email protected]"]}
{:type :etaoin/timeout, :message nil, :timeout 7, :interval 0.33, :times 22, :predicate #object[etaoin.api$wait_running$fn__5604 0x6464f017 "[email protected]"]} {:type :etaoin/timeout, :message nil, :timeout 7, :interval 0.33, :times 22, :predicate #object[etaoin.api$wait_running$fn__5604 0x6464f017 "[email protected]"]}
The chrome portion of it seems to be working okay?
Time out happens on both:
(def driver (web/chrome))
and
(def driver (web/chrome {:headless true}))
which maybe means the chrome portion is not actually working okay 🙂
hi everybody, are the sources for the clojure cli tool public? I can't find them on github
Ah, master
seems outdated.
Here's a recent version, at a different path: https://github.com/clojure/brew-install/blob/1.10.3/src/main/resources/clojure/install/clojure
nice! thanks, yeah I got confused by the brew prefix
In case you're interested in a port to Clojure of that bash code: https://github.com/borkdude/deps.clj
what’s the best practice for running a leiningen project in production: making a jar and running the jar or lein run? Are there pros and cons to each approach?
lein is a build tool and best practice is to use lein to make a jar, then use java to run that jar
for starters, lein run starts two jvms
various factors might make lein run indeterminate for the same code, having a jar is very good for reproducibility and debugging
also there's a wealth of existing tooling and industry knowledge about using a jvm in prod, by eliminating the middle man you can leverage it directly
as far as I can tell the only argument for using lein on a prod machine is that it's the only way you know how to run clojure code, and that's easily fixed in a five minute hands on session
Yeah making jars was just taking longer than lein run so I thought why not lein run
that's with your m2 cache already filled I assume
also, that likely means you had :aot
set, you probably don't need :aot
I’m talking about while creating a docker instance
what’s the point of :aot :all?
OK you make a jar first, then put that in your docker
:aot can speed up startup, and allows java code to invoke your namespace as if it were an object
since I want to run the tests too, wouldn’t it be better to copy the whole source and create the jar in the container?
and then run the tests with lein test inside the container?
and also want to run cljs tests
you shouldn't even publish the jar if the tests fail • run tests • make a jar • push that jar to a repo • build a docker image using that jar • publish that image
building a jar before running tests is probably a waste of resources
every message here has extended the complexity: 1) make a jar 2) docker 3) aot 4) testing 5) cljs. Simplify and focus on a single task at a time. Learn how to make a jar. Don't let any other considerations get introduced into that learning until you know how to make a jar
I already know how to make a jar
I have already deployed the jar on docker and also done it through lein run
what if the whole point is to run the tests in your container to see if it actually works? @noisesmith
that's a separate testing step in that case
I mean, if you have infinite CI budget/resources you can combine that tests, but IMHO it's better to have a fail fast before building
@ps going back to your initial question - using a jar is currently the undisputed best practice, arguing for using a build tool in prod requires some sort of use case or advantage, and it sounds like you are trying to convince me it's acceptable
I find it convenient to use a docker container as the build environment - sometimes even GNU/Linux package manager provided deps are not reliable/reproducible, I encountered some problems with Ubuntu and dependencies with openjfx - but that'd still look like 1. Build jar (in docker or otherwise), 2. Ship jar/run jar (in docker or otherwise)
@m131 what about the tests?
lein test?
One difference in execution between an (uber)jar produced via lein and using lein run as your entrypoint could be anything you produce during macro expansion at compile time - for example, if you expanded at compile time something that printed the current date as, say, "boot up date of this app" - the uberjar is gonna snapshot it at the time of jar creation - lein run is going to "snapshot" it as the app starts up
or a separate uberjar?
(but that'd be a real weird practice) - yes, I would lein test && lein uberjar
or w/e on your build env
(not sure if that works since test probably pulls in more deps than you would like with the uberjar... never used it like this :)
lein test / lein uberjar as separate steps likely gives better error output from your build system
@borkdude it works, uberjar doesn't put everything in the jar, just what's resolved as needed for the jar
+1 using lein
to run your application in prod is a bad idea. Lots of additional failure domains, and it's slow
Regarding reproducibility - assuming the network is perfect and all dependency versions are fixed, is there a way for clj -Srepro -M:run
to produce different results in different setups?
another advantage of a jar: if there's a failure / odd behavior, I can download the jar and know I'm looking at exactly the code that the vm in prod sees
@p-himik for starters, you could have a different git checkout, or a dirty branch, or a reference to a relative directory with different code checked out
and if you have something in the maven cache dir it will be used. i've heard of people installing modified libs and forgetting about it affecting stuff like this
right, I've been bitten by that when doing local dev on some lib, and doing a local install to cache without changing the version string
(there's a few things there I don't do any more for obvious reasons :D)
My projects are probably rather niche to draw any robust conclusions from my observations, but in 5 years I have never been bitten by anything like that - in part because the deploys always happen automatically, nobody tinkers with the env on prod servers. So, I had to spend exactly 0 hours on such issues. However, I had to spend multiple hours figuring out: • How to build an uberjar • What tool to use • Why sometimes the tool of my choice produces obscure errors and sometimes it doesn't • Why the resulted jar sometimes just works but sometimes fails with other obscure errors Select a new tool, rinse, repeat. I heard good things about depstar, but by the time I discovered it, I was already exhausted.
I didn't even take clj / clojure cli seriously until I found a reliable and simple way to make an uberjar
Our process at work is that our CI system builds uberjars (using depstar
) if-and-only-if the complete test suite passes, and those uberjar files are automatically deployed to our staging "QA" server for final testing/approval by QA/management, and we have a web UI that lets QA/management release the uberjars to production and that's all automated.
Many years ago we used to do source deploys and lein run
(and, later, boot run
) but, yeah, that's just a bunch of pain you don't need 🙂
Since we use depstar
as a critical part of our workflow, it gets plenty of love in terms of maintenance 🙂
my usual CI process: run tests make jar stuff git SHA info into edn file in jar (for health check endpoints / ops-y things) optionally make and push container image built around the jar
I once saw a macro which grabbed git sha info, so it would pick it up at aot time. I was not very impressed.
depstar
puts the git SHA into the manifest pom.properties
-- revision=...
with the output of git rev-parse HEAD
and I see tools.build
has a git rev count function for versioning the project.
GIT_SHA=$(git rev-parse HEAD)
cat > artifact/service.meta.edn <<EOF
{:git/sha "$GIT_SHA"}
EOF
^ I used to do that then do jar -u
to update the artifactAt work we have a -X
wrapper for depstar
which puts a git SHA/tag combo into a generated (ns ws.uberjar.release)
as a version
def
. And all our apps know they can get the version from there to report in health checks etc.
(I was slightly tempted to add something similar to depstar
itself but I resisted!)
I often put a file under resources
for this so I can use io/resource
to get the version
git describe --tags
is a nice alternative to raw shas, particular if you tag releases often with date based tags
(I think that's what we use at work, right? I don't have that open right now since I'm on vacation 🙂 )
Hi! I'd like to know if I could use Datascript "reactively" linked to a Postgres DB. I explain it in more details https://www.reddit.com/r/Clojure/comments/oiz3vb/datascript_automatic_persistency/. To sum up, changes get cached in-memory in an instance of a Datascript DB for being persisted later on. Besides, the application should always query the Datascript DB instead of directly reading from Postgres. P.S.: I'm doing an integrator MVP to connect Jira projects to another project management service. This will be my first Clojure project to run in production 🙂 EDIT: It's still in early stages of development, any architecture tips and advices are also welcome
@seancorfield Seems like a thin JAR requires the library’s consumer to list the library’s dependencies in the consumer’s own project.clj
… how can I not require the library consumer to list those?