This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-01-03
Channels
- # aleph (2)
- # announcements (13)
- # babashka (7)
- # beginners (36)
- # calva (26)
- # cider (11)
- # circleci (13)
- # clj-kondo (15)
- # clojure (105)
- # clojure-europe (79)
- # clojure-nl (3)
- # clojure-uk (6)
- # clojurescript (17)
- # conjure (4)
- # core-logic (2)
- # cursive (10)
- # data-science (5)
- # datalevin (11)
- # datalog (14)
- # eastwood (6)
- # emacs (2)
- # figwheel-main (1)
- # fulcro (34)
- # google-cloud (1)
- # graphql (3)
- # introduce-yourself (7)
- # jobs (1)
- # leiningen (17)
- # lsp (46)
- # malli (2)
- # minecraft (3)
- # missionary (19)
- # off-topic (31)
- # other-languages (49)
- # polylith (2)
- # portal (5)
- # practicalli (1)
- # quil (77)
- # releases (1)
- # remote-jobs (1)
If we destructure a map with defaults, is there any syntax sugar for getting the entire map with the defaults?
(let [{:keys [k] :or {k 1} :as m} {}]
m)
;; Actual: {}
;; Desired: {:k 1}
I know we can simply merge
like this
(let [{:keys [k] :or {k 1} :as m} {}]
(merge m {:k k}))
but as the number of defaults grows it starts to look a bit redundant to me. Just curious if there is a way to avoid it.i'd probably make some kind of normalize
function that takes the m
and (merge defaults m)
and then you are done
Is there a way to get the defaults as a their own map, or do I just have to construct that map manually?
There is no way to do that, you should merge an explicit defaults map if you want that
@U7XR2PZFW What I tend to do is:
(let [:keys [k] :as m} (merge {:k 1} val)]
m)
but this feels less satisfactory when the destructuring would otherwise be in a function's argument, since you lose information in doc
for the signature unless you add in :arglists
and then you're duplicating what's already in the code 😐I'm having trouble with error messages (fro exceptions) not showing up. I have gotten used to error messages not showing when they happen in a sub-process (if that is the correct term). But in this case, I do not think being in a sub-proces should be the issue.
I have an application that makes use of multi-threading (starting babashka
processes). I have a main method that starts a babashka
process. The babashka process runs a bash process, and, upon completion, it updates an atom.
This atom has a watcher. I have included the following code in the watcher function:
(println "in watcher")
-code-that-intentionally-throws-error
(println "done with watcher")
"done with watcher" never prints. The sequence of events is simple:
1. call babashka process
2. after babashka process finishes an atom is updated
3. the atom watcher is made to fail
Since the atom watcher fails silently it seems to me that the watcher is still operating in the context of the babashka process. Am I correct in surmising this? If so, how do I fix it?You write contradicting things: • "The babashka process runs a bash process, and, upon completion, it updates an atom" • "after babashka process finishes an atom is updated" So where exactly does that atom live, what updates it?
A few other things:
• Threads and processes are completely different concepts (you mention multi-threading and right after you mention starting a process)
• Atoms and their watches aren't shared/moved between processes in any way
• "done with watcher" can never be printed regardless of the context if the form before it is just (throw ...)
outside of any (try ... (catch ...))
The babahska process wraps a java process. This java process is updated with a callback function like so:
(defn add-completion-dispatch [java-proc myvals]
(.thenApply
(.onExit java-proc)
(reify java.util.function.Function
(apply [this p]
(reset! myatom myvals)))))
So the callback function updates myatom
. myatom
lives in a different clojure file.> "done with watcher" can never be printed True. But this shows that the call was interrupted by an exception without any exception message showing.
.thenApply
returns another future. I'm pretty sure you'll find your exception there if you try to .get
it.
> Threads and processes are completely different concepts Yes, in this case, I mean processes since they do not run in the same memory space.
> I'm pretty sure you'll find your exception there if you try to `.get` it. I really appreciate your help. Thank you!
Hey everyone, I've been doing a lot of duct (sane integrant framework) lately and I can't help to wonder: "why such a great framework isn't heavily adopted by the community" to the point where libraries offer duct keys. It does so much in terms of development speed and state management, in addition to greatly decreasing the amount of repetitive code to say setup web api, integrate datomic database or have a pre-made pure module part of a running system. I'm using a sophisticated reitit duct-module that I'm currently under-development that really made setting up a production ready reitit ring app with a few lines of code and configuration key. I'm hoping to release that module soon to prove my point. Anyways, this question crossed my mind after seeing how little community activity is devoted to duct and how few stars it has on github and thought maybe I should share it here to create some type of awareness or get some feedback. Thanks
libraries aren't offering Duct keys in the very same way they aren't offering Component or Integrant components, even when those are used by most webapp setups out there, regardless of framework/libs choice Most times making a Component is fairly trivial, although it would certainly be nice to have some readily-available components. I think the reason for this absence is simple: most commercial development happens behind closed doors, in a private monorepo, possibly with just-enough quality and no particular reusability for people outside a specific organisation.
Other than that I'm not aware of any particular flaw from Duct's side and hear from time to time of great companies using it (and publishing compatible libs!). I think there's some favorable sentiment for web frameworks (despite the cliche among Clojurists), the next problem is which framework - i.e. why Duct in particular? A few other 'competitors' seem just as modular and capable. it's some sort of Lisp Curse perhaps - there's always one more alternative to choose from, or you can always make up your own alternative without much effort. Same for e.g. Malli vs. Spec perhaps, and various other examples
> it’s some sort of Lisp Curse perhaps - there’s always one more alternative to choose from, or you can always make up your own alternative without much effort. Unlike the JS ecosystem though, I think for a lot of stuff there are few enough options to make a “choose your adventure” type thing
The biggest problem is lack of a documentation IMHO. Luminus has got book Web Development with Clojure and enough articles. Duct is missing a single, consistent source of information and good examples - especially for beginners.
FWIW, next.jdbc
does provide support for Component out of the box (without having to depend on Component itself). I personally don't like Integrant and don't use it and therefore have no idea what it would take to "offer duct keys" @U02PHJ3C1QV?
I like Component because it's very small and focused and simple (and it is as well-documented as it needs to be since it is so small and focused).
@U45T93RA6 Yes, making components in the original sense is trivial I agree, but duct has totally different approach, instead of components you get modules, or straightforward duct keys. The case with duct isn't to define a large record and ask the user to require it and start using, it's more like hey here's a configuration key you can initialize (or don't) with this set of parameters to produce a customized value that can be referenced in other "modules" or "keys" by passing it to it's arguments. When you use a particular library over and over, you tend to write the same code over and over with small tweaks depending on the library configurability and domain. It's great to have an abstraction over a "feature" or "module" that would only require you to write a key definition instead of a code, for example https://github.com/duct-framework/middleware.buddy. You don't have to reinvent the wheel every time u use buddy, you can just define the keys inside your config and pass it's value around. The true value of duct is in-making the system extremely easy to reason with. It definitely isn't just another way to write or perceive components but it's all about integration and making it easy to build complex relationships between keys or modules without having to write a single clojure code to glow things up, say middleware and a router. > Duct in particular? I understand, I always say having too much alternatives doing the same thing (and really good) with minor differences break the community. Duct in particular, because it's made by the author of integrant, it's matured greatly, and once you get it and work with it feels sane. > Lisp Curse 😆 Yes, but it doesn't have to be that way with clojure, the more easy and simple the ecosystem is the more great tooling and system get build on top it. Just imagine of clojure.core had variants and clojure allowed for core to be directly replaced by the authors and libraries are compueting to show which their clojure.core has more and better performant functions. It would completely break the community development, and new memebers will just have to not only pass the stage of learning lisp, but also, evaluating 1000 different ways to develop with clojure.
@UBRV1HXPD true ture, I honestly, didn't quite get the whole duct thing until like few months later, but really it as worth it. My suggestion if you someone want to get into duct is to read through the code base and tests of duct modules and duct.core. Oddly, it is straightforward.
@U02PHJ3C1QV Isn't System meant to be a set of Duct modules?
Hmm, I can't even find that repo now...
Oh, haha, System is the Component equivalent of Duct I think. Never mind!
Hey @U04V70XH6, I've been using a lot of your work. Thank you ❤️
> next.jdbc
does provide support for Component out of the box
How so? you mean by how the code base is structured?
edit: oh I see there are actual component
key. nice
> "offer duct keys"
Meaning creating duct keys like next.jdbc/connection
which accept the same db-spec
through defmethod init-key :next.jdbc/connection
.
Here I inivited you on github to a reitit module I'm planning to release soon. It may make more sense or the value of duct may be better seen. feel free to DM with whatever.
Yeah, I'm not interested in adding dependencies to next.jdbc
to support such things -- I was able to add Component support because of metadata-based protocol extension, so I don't need the Component library in order to do that.
You can't call Duct's defmethod
without depending on Duct, right?
You can have arbitrary IMeta
objects that support the Component lifecycle without needing any dependency on Component itself.
> You can't call Duct's defmethod
without depending on Duct, right?
Not defmethod it an integrant thing, through a multimethod defined in integrant.core
> Yeah, I'm not interested in adding dependencies to next.jdbc
to support such things -- I was able to add Component support because of metadata-based protocol extension, so I don't need the Component library in order to do that.
Oh I see it makes sense. even worst for maintaining it
Yeah, supporting something that requires registration via defmethod
means n+1 libraries or else adding that something as a dependency to every library that wants to support it -- even if your users don't want/need it.
Or having additional namespaces that must be required by users to pull those deps in -- which also complicates the build/test process since you need to ensure those nses are not referenced anywhere by default when you don't have that dependency. It's part of what I don't like about Integrant.
System took that path: a large collection of independent nses with each one requiring a specific library, to hook it into Component. Back before metadata extension of protocols was a thing.
But then maintenance of System is tied to all those other libraries... 😞
(I declined the repo invite deliberately -- no need to reinvite me)
what’s the best way to check whether a string is a numerical string in clojure?
like “123”, or “342315" etc?
In Clojure 1.11 you will soon be able to use parse-long
but until then, calling Long/parseLong
would be best
let’s say I have a map: {"Leading zeros ids" "00121", "Test" "123", " test 1" "abc"}
and I want to change every value that’s a number string to be a number except those that have leading zeros, how would I do that?
do I have to convert to a vector of vectors and back or is there a better way?
this is what I got:
(into {} (map (fn [[k v]] [k (if #(and (every? (Character/isDigit v)) (not= (first v) "0")) (read-string v) v)]) {"Leading zeros ids" "00121", "Test" "123", " test 1" "abc"}))
But it gives:
{"Leading zeros ids" 81, "Test" 123, " test 1" abc}
use a regex to check all digits and doesn't start with 0, your not= is failing because the first of a string is character not a string
or (str/starts-with? v "0")
True, but I'm using 1.11 so I'd use parse-long
🙂
you need to parse in base-10. read-string
is dangerous and not up to the task for this (unless you explicitly are dealing with octal numbers)
(and (string? v) (not (str/starts-with? v "0")) (parse-long v))
🙂
That throws.
dev=> (Long/parseLong "abc")
Execution error (NumberFormatException) at java.lang.NumberFormatException/forInputString (NumberFormatException.java:67).
For input string: "abc"
dev=> (parse-long "abc")
nil
(just a difference to be aware of)
I don’t understand why it’s throwing since (every? (Character/isDigit v)) would be false in the case of “abc”
(into {} (map (fn [[k v]] [k (if #(and (every? (Character/isDigit v)) (not (clojure.string/starts-with? v "0"))) (Long/parseLong v) v)]) {"Leading zeros ids" "00121", "Test" "123", " test 1" "abc"}))
if you are parsing anyway, it is best to avoid the digit check if you can since that is what parsing does anyway
my suggestion of using every? was based on you asking to check if a string contained digits
no but I want to keep the numbers with leading zeros as strings
You have (every? (Character/isDigit v))
instead of (every? (fn [v] (Character/isDigit v)) v)
dev=> (every? (Character/isDigit "abc"))
Execution error (IllegalArgumentException) at dev/eval94029 (dev.clj:1).
No matching method isDigit found taking 1 args
user=> (into {} (map (fn [[k v]] [k (if #(and (every? (fn [v] (Character/isDigit v)) v) (not (clojure.string/starts-with? v "0"))) (Long/parseLong v) v)]) {"Leading zeros ids" "00121", "Test" "123", " test 1" "abc"}))
Execution error (NumberFormatException) at java.lang.NumberFormatException/forInputString (NumberFormatException.java:65).
For input string: "abc"
You have #()
not being called.
given the difficulty you are having with syntax maybe we should move to #beginners (or not, it will likely be the same people helping either way)
So you're asking if some-function ...
which is always true since some-function is truth
yes that fixed it
If you have a compound expression, the way to debug it is to try each little piece in the REPL.
That's what I did to work from the inside of your failing expression to the outside, identifying the bugs along the way.
the Character/ thing doesn’t work in cljs
Doesn't cljs already have parse-long
?
Or something similar. Since Long/parseLong
won't work on cljs either.
@domagala.lukas No portable way to do it?
cljs.reader/read-string
I guess? and clojure.edn/read-string
for clj
(and a read conditional on require/refer one or the other)
i’m using
(defn- str->int [s]
#?(:clj (Integer/parseInt s)
:cljs (js/parseInt s)))
you could do the same thing for longsHopefully, with parse-long
etc in clj, we'll get same-named functions in cljs core as well "soon"?
I mean Character/isDigit doesn’t work
Kind of important to specify whether you're trying to solve a problem in clj or cljs (or both) since Long/parseLong
isn't a thing in Clojure either -- both Character
and Long
are JVM classes.
FWIW I would definitely move this to #beginners Regardless of whether it's the same people helping or not - after all, the sets of readers are different.
use-fixtures :once seems to work once in the context of one namespace. Is there a way for it to run once in the context of all tests in all namespaces?
No, that's not a feature of clojure.test
I seem to have run into that a number of times. We have our own wrapper for Cognitect's test-runner
that does "per-suite" fixtures, and Polylith has added "per-project" fixtures (at my request) which is essentially "per-suite" since it runs the test suite for each project (and runs only tests that reflect code changes since the last "stable" point -- or whatever point of reference you want).
We are liking that incremental testing "feature" 🙂
we’re using use-fixtures :once to run db migrations in each namespace, but we only need to run the db migrations once before running all the tests. What’s the best way to do that?
@U01F1TM2FD5 What are you using to run your tests?
We have our DB migrations as a separate step in our build.clj
script and run that prior to running tests -- so we have a build task that is "run DB migrations; run whatever tests you want"
We're using lein test command
How to do this with lein?
I don't know if there's a way to do it with Leiningen -- I haven't used it for years.
If you can run your DB migrations via a lein
task, then you could specify :prep-tasks
in the :test
profile I guess. See if https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#task-specific-profiles has anything that helps
(Leiningen's rigidity was a big part of why we switched to Boot back in 2015 and then the Clojure CLI / deps.edn
in 2018)
I tried: :prep-tasks ["run -m user"]
but that gives: 'run -m user' is not a task. See 'lein help'.
@U01F1TM2FD5 I think you need to create an alias for the DB migration task, and then you can specify that alias in :prep-tasks
. Those :prep-tasks
strings need to be just task names or alias names -- not command-lines.
I see Dan helped you in #leiningen and suggested :prep-tasks [["run" "-m" "user"]]
-- did that work for you?
Is there an idiomatic way to do some side effect over a list? Would I just use the map
function?