This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-07-31
Channels
- # announcements (2)
- # babashka (145)
- # beginners (260)
- # calva (17)
- # chlorine-clover (7)
- # clj-kondo (9)
- # cljsrn (1)
- # clojure (88)
- # clojure-dev (65)
- # clojure-europe (31)
- # clojure-france (4)
- # clojure-nl (4)
- # clojure-uk (61)
- # clojuredesign-podcast (1)
- # clojurescript (31)
- # code-reviews (1)
- # cursive (32)
- # data-science (2)
- # datascript (9)
- # datomic (39)
- # docker (3)
- # events (1)
- # figwheel (95)
- # figwheel-main (4)
- # fulcro (17)
- # kaocha (2)
- # keechma (1)
- # malli (1)
- # meander (35)
- # nrepl (4)
- # off-topic (1)
- # pathom (8)
- # re-frame (4)
- # reagent (8)
- # reitit (3)
- # releases (1)
- # remote-jobs (2)
- # shadow-cljs (182)
- # sql (30)
- # tools-deps (89)
- # xtdb (31)
> https://www.stuttaford.me/2018/02/18/a-clojure-learning-journey/#no-leiningen > Since the release of Clojure 1.9, https://clojure.org/guides/deps_and_cli. As I've been happily ensconced in my fortress of `project.clj` files these past six years, I've had no reason to get to grips with this tool, or any of its implications. > I've decided to apply a simple forcing function for this project: do it with `clojure` and https://clojure.org/reference/deps_and_cli / `deps.edn` alone — no Leiningen! > https://www.stuttaford.me/2018/02/18/a-clojure-learning-journey/ Is it possible to achieve what Lein does without it? I get that there is no specific need or benefit for it, since Lein does many things out-of-the-box, but just asking out of curiosity.. 🙂
@thegobinath At work we switched from Leiningen to Boot back in 2015 and then to the Clojure CLI in 2018. We do everything with the Clojure CLI. A 100K line monorepo, with thirty subprojects, building a dozen apps.
Do you have some resources or recommendations regarding using clj CLI for a monorepo? How do you manage it? does it require any special tooling? Why did you choose a monorepo over polyrepo?
I would be interested in this as well. Sounds like good fodder for a blog post.
This is quite a good article on the topic: https://www.happycoders.eu/java/monorepos-advantages-disadvantages/
Some folks really don't like monorepos tho'.
We just use the Clojure CLI and deps.edn
. The only "unusual" feature we leverage is the CLJ_CONFIG
environment variable to point the "user-level" aspect to a specific subproject in the monorepo that contains our "master" deps.edn
which has all the tooling aliases for the monorepo and all the "pinned" versions of libraries (where we use a library from multiple subprojects, we tend to pin it to a specific version via :override-deps
under a :defaults
alias in that master deps.edn
file).
So our usage is the equivalent of
$ cd <repo>/subproject
$ CLJ_CONFIG=../versions clojure -A:defaults:other:aliases:here
and we have a small shell script, called build
, that wraps that and also allows us to invoke one command across multiple subprojects:
$ build test worldsingles test api uberjar api
which expands to roughly this:
$ cd <repo>/worldsingles
$ CLJ_CONFIG=../versions clojure -A:defaults:test
$ cd <repo>/api
$ CLJ_CONFIG=../versions clojure -A:defaults:test
$ cd <repo>/api
$ CLJ_CONFIG=../versions clojure -A:defaults:uberjar
(the latter invokes depstar
)Dependencies between subprojects are just {:local/root ".../subproject"}
in each subproject's deps.edn
file.
(and there is a little bit of alias expansion involved, to compensate for the lack of composable aliases -- so test
actually expands to :test:test-deps:runner
, where :test
is a general test-related alias in versions/deps.edn
and :test-deps
is empty that file but can be overridden in each subproject to provide extra testing dependencies etc and :runner
is in versions/deps.edn
and runs Cognitect's test runner)
Yes, we use Component very heavily in all our projects.
I've seen this being linked to a few times: https://8thlight.com/blog/colin-jones/2010/12/05/clojure-libs-and-namespaces-require-use-import-and-ns.html
That’s good, I also point people at https://stuartsierra.com/2016/08/27/how-to-ns as well (except I think you should use vectors for import, not lists)
@U064X3EF3 - That!! Exactly that! My OCD flares every time I see require with vectors followed by import with lists...
and fyi, Rich had the exact same feedback when he read Stuart's post so I can second it with a voice of authority :)
sorry, I guess https://stuartsierra.com/2016/clojure-how-to-ns.html is the better link
I never understood Stuart's reasoning for lists: "Because the first symbol, the package name, is special."
But he also provides a bit more clear reasoning: "because both ns and import have historically been documented this way." Although, ns
doc also has an example of using prefix lists with :require
, which I think nobody really does nowadays.
Maybe if the documentation was changed, there would be less contention on the topic?
FWIW I use lists for import
myself simply because I started doing so exactly after reading that article just when I started learning Clojure. Now it's just a matter of habit.
I'm the Stuart Sierra camp (and my team mate seems to be as well) but I understand Rich's (and Alex's) position too.
It's just another one of those places where the early implementation didn't proscribe the syntax very strongly and now it would be hard to change without breaking a lot of code. After all, when ns
was spec'd so that require
and import
were illegal (don't use symbols, use keywords instead) that broke quite a few libraries, including some of the Contrib stuff.
(but I'm pretty sure the symbol-based ns
form was never documented -- it just happened to work when folks accidentally omitted the :
?)
@U04V70XH6 Do you have any particular reasons for preferring lists over vectors for :import
? I mean less ambiguous than "because the first item is special".
No, but Stuart's point on that resonates with me. And I've never seen the prefix form used in :require
so they feel different to me.
That's why I also sympathize with Alex/Rich/etc that some people feel it's a rather arbitrary stylistic decision.
If I started work at a company that uses vectors with :import
, I'd just follow the house style (but after a decade of using lists with :import
I suspect I'd get called out in code reviews quite a bit at first!).
What would be the common way of looping through the result of a java.lang.Process
's .children
and calling .destroy
on that? (Java 11)
something like:
(run! #(.destroy %) (iterator-seq (.iterator (.children proc))))
?(-> (.children proc)
(.forEach
(reify Consumer
(accept [_ process]
(.destroy process)))))
the run!
style will also be supported by babashka (it has some missing classes for it now, but will be fixed) whereas reify
isn't supported at all yet
Can I not use for
loops within macro definitions? A minimal example here https://stackoverflow.com/q/63198344/1412255 fails with undecipherable errors
it's missing a ~
(defmacro foo []
`(list ~@(for [i [1 2 3]] (+ 1 i))))
clojure uses ~
for syntax unquote rather than ,
An interesting variation:
(defmacro foo []
(println "hi")
(for [i [1 2 3]] (println i))
(println "bye")
'())
This outputs
test> (foo)
hi
bye
()
I expected it to also output the 3 numbers. Does anyone know why?This is what originally led me to think that for
is silently failing inside macros. But something more nuanced seems to be going on here.
for
creates a lazy sequence of values. If you are doing side effects use doseq
or if you need to tie together producing the sequence with performing a side effect you can use doall
on the result of a for
;; won't do side effects until "realized"
(def l (for [i [1 2 3]]
(println i))
;; I put the def because if you try this
;; directly in the repl it will print
;; the list and realize the lazy sequence
;; If you really need it, but lazy seqs
;; with side effects are a code smell
;; i'd say
(doall (for [i [1 2 3]] (do (println i) i)))
I wonder, if clj -X
was designed for function that take 1 map argument, doesn't it make sense for such functions to take 0 args (as if empty map) and variadic kvs?
Sort of like that:
(defn foo
([] (foo {}))
([m] m)
([k v & kvs]
(foo (apply hash-map k v kvs))))
Not that I have any use for that...It's for the same reason opts are passed as a map rather than varargs more and more, so you don't need to use the non-existent core function mapply
everywhere
we've kicked around actually turning all kwarg functions into also taking a map arg at the language level so (defn foo [& {:keys [a b]}]) could be invoked as (foo {:a 1}) or (foo :a 1 :b 2)
Oh, that would be a very nice enhancement!
needs some research but Rich came up with that a while back and we spent like 20 minutes on it and decided it wasn't crazy :)
I don't think so - kwargs take even counts, this would be a 1-count in that spot
and I don't think you can write a variadic that would collide
but that's the research part :)
it might lead to a weird error message if you only provided a single key and forgot the val "keyword can't be cast to ilookup" :D
right, that's more a joke about the error message backward construction to features we already do
we'd want to figure all that out
maybe it could be part of the destructure
function so you can also use this in destructuring - or maybe not 🙂
I think it would have to be deeper than that
I'm thinking specifically of something like function that logs key value pairs, where the order of the arguments is important
kw-arg order is not a thing
they are unordered
:face_palm: never mind me. Sounds like an excellent change, I've worked on a few projects which had an apply-to-map
function for working with kwarg-happy libraries, would be great to ditch that
I have a function that is normally called like this
(attmd :_att1 (string-type) :label (custom-label :_att2 :_att3) :category [:initial])
its defined as (defn attmd [att-name & options])
.
I am trying to write a macro that sort-of wraps that function such that I can use it like
(dynamic-attmd :_att1 (string-type) :label (custom-label :_att2 :_att3) :category [:initial])
but I can't figure out how to work with &
arguments within a macro.Here is what I have
(defmacro dynamic-attmd [att-name & values-expr]
(let [used-atts (vec (common/expression-attributes values-expr))
st-att-name (name att-name)]
`[(defm/dependency ~att-name ~used-atts)
(defm/trigger (comp (modtransform/without-attribute (keyword ~st-att-name))
(modtransform/with-parts [(apply attmd (keyword ~st-att-name) ~values-expr)])))]))