This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-08-22
Channels
- # beginners (143)
- # boot (18)
- # chestnut (5)
- # clara (1)
- # cljs-experience (1)
- # cljsrn (13)
- # clojure (290)
- # clojure-austin (4)
- # clojure-italy (13)
- # clojure-nl (21)
- # clojure-russia (14)
- # clojure-spec (49)
- # clojure-uk (59)
- # clojurebridge (7)
- # clojurescript (54)
- # core-logic (2)
- # cursive (22)
- # datomic (149)
- # fulcro (31)
- # graphql (14)
- # hoplon (59)
- # keechma (24)
- # lambdaisland (1)
- # lumo (86)
- # off-topic (3)
- # om (19)
- # om-next (1)
- # onyx (4)
- # portkey (20)
- # re-frame (41)
- # reagent (63)
- # ring (1)
- # ring-swagger (1)
- # spacemacs (9)
- # sql (10)
- # yada (13)
I have a vector of 8760 values (one for each hour in a non-leap year). I want to calculate the average of each day. How can I map over the vector in increments of 24?
@stuartrexking partition
@bfabry perfect thanks.
Hi, this code is supposed to delete all files in a directory and then repopulate it with previews. (defn build-previews [] (clear-directory "resources/public/preview") (map (partial spit-ad "resources/public/preview/") (filter #(= "true" (:preview %)) (vals (:ads @db)))) (println "debug build-previews done")) all the lines work if I eval them individually, the files get deleted and the previews get written. But when I execute them together the only the println gets executed. If I wrap the three in a do it won't help. Could someone please give me a hint?
My guess is that you aren't seeing any effects in your "resources/public/preview" folder because you're using map
which is lazy and meant for pure functions.
When you evaluate that line by itself your evaluation will consume the map and show you the result, but when you have it as one of the statements you are building a lazy seq without any code that consumes it, meaning that it won't have an effect. Try using doseq
instead.
When using gen-class, how do I override this method:
public abstract void start(Stage primaryStage) throws Exception;
Stage is javafx.stage.Stage.
-start-javafx.stage.Stage?
Hi! I am using Gradle to build a multiproject big project, and on of the projects is in Clojure, which I am depending on from a Java project so I AOT compile it. So I tested the dependent project, and everything works in Gradle, but IntelliJ cannot find the generated classes and it is pretty annoying. I am using the nebula.clojure
Gradle plugin. The projects are imported from Gradle. Do you know a way to fix this?
Not familiar with that plugin but I guess this boils down to a classpath problem. I'd try to find where (in the filesystem) the clojure classes are being emitted, and what your intellij classpath is set to.
it turns out Cursive uses source code inspection to add this functionality, which only work for simple cases but not for macros. So I have to work around it, and add the output to the classpath 😕
How can I execute a core.logic
goal repeatedly until exhausted, and use the result as a list in my logic program? I'm looking for the equivalent to "nesting" a run expression, so to speak
@pupeno you don't have to override it; add (defonce force-toolkit-init (javafx.embed.swing.JFXPanel.))
to initialize the toolkit, then simply create a new Stage
and use it (see ClojurefX) 🙂
@zilti I managed to get it to work… I’d rather not use the Swing part if I can avoid it.
Why does the default lein template for an app include skip-aot for the main function/namespace?
Aot causes bugs that don't happen without aot, and it is rarely needed.
Basically what I have now is:
clojure
(defn next-free-license [ltype]
(run 1 [q]
(fresh [licid tid link links license]
(datascript= [tid :type/name ltype])
(datascript= [licid :license/type tid])
(datascript= [licid :license/key license])
(datascript= [licid :license/user link])
(membero link links)
(== q [links licid license]))))
Which gets me `[(138 . _0) 6 "532f16c2c9b13540eb6a"]Which gets me [(138 . _0) 6 "532f16c2c9b13540eb6a"]
but I need [(138 143 151 153) 6 "532f16c2c9b13540eb6a"]
Can anybody confirm whether creating fields/members in a Java class in Clojure is impossible?
At that point you should think if forcing that into Clojure instead of using a Java class is really the way to go, though
Let me see about deftype.
@zilti I’ll evaluate that later if it’s too ugly or impossible; but having a Java class will also be very ugly.
@bronsa but deftype cannot add new instance methods, can it?
I’m not sure if it’d be cleaner. I’d have to deal with the mismatch of data types between Java and Clojure.
But if Clojure cannot do it, then, there isn’t much of a choice.
@bronsa keyword indexed maps of various different types that are impossible in Java.
@bronsa: have done that? have an application half done in Java and half done in Clojure? I have a Clojure server and just writing a Java client for that was a pain due to the lack of dynamic types. You have to define everything in advance and cast/convert.
I mean, just having to call Clojure functions from Java is horrible: https://clojure.org/reference/java_interop#_calling_clojure_from_java
yeah sure, I wasn't suggesting half an app in java invoking clojure, just that one class you need
@bronsa that one class is the controller for the UI… possibly 20% or 25% of the app.
It is doable but then the advantage of using Clojure at all is going down.
What do you mean by just use java?
class Foo extends Bar { public Object myField; void myMethod () { RT.var("my-ns", "my-method-impl").deref().invoke()) };}
Yes, that’s what we’ve been discussing. I don’t believe the proxying would be that simple.
alternative: create an abstract java class with all the stuff you need, than proxy it in clojure
First, things in the real world rarely are simple. Second, is not going in one direction, it’s going in both directions. The Java class would have to pass its instance to the Clojure functions, so they can keep a reference to call back. Because Clojure won’t be able to interact with JavaFX directly, I’d have to make a lot of proxying methods/functions so that the code can be written in Clojure or just write it in Java.
I haven’t tried it yet, so, this is just what I’m coming up from the top of my head.
right, i don't know how you're going to need this in your real world code, I'm just answering the question of whether or not it's possible to do it in clojure
Yeah… this is sad.
"Because Clojure won’t be able to interact with JavaFX directly" There's two JavaFX/Clojure libraries that do exactly that
There's nothing really that special about JavaFX, it's just Java classes and Clojure interop works quite well with it.
@tbaldridge there are three, fn-fx, fx-clj, both of which cannot do fxml which is what I’m trying to do. ClojureFX does it, but I’m not very happy with its code, it seems quite experimental and handling fxml require some horrible hacks which I was trying to stay away from.
yeah, fxml is something I haven't looked into adding to fn-fx
mostly because I've coded my UIs through the years without fxml or xaml designers. I should look into that sometime perhaps
@tbaldridge what’s special about JavaFX or rather, fxml, is that you have an xml that points to a class and when you load the fxml, it instantiates that class and sets member/fields on that class. I’m trying to generate those classes from Clojure but this looks impossible.
@tbaldridge are you fn-fx’s developer?
yeah, I mean fxml is more-or-less the same as fn-fx's EDN based data.
Cool. You should check out ClojureFX’s solution for inspiration.
Except for any class extension/inheritance stuff, and I haven't dug into that enough to know if it's really needed.
The reason why I’m trying to use fxml is not for XML but for the UI designer.
right
it's probably possible to write a fxml importer or converter
Yes, I've come up with a pretty neat FXML loader and an accompanying generator for the controller classes
the problem would be with the binding and event models. JavaFX's model of binding components via listeners and observers is a pattern I disagree with. I'll have to see if these UI designers can support fn-fx's React-esque model
Also, I’m trying to reduce the amount of experimental/unmaintained stuff I have. I wrote the app on Electron+ClojureScript and there’s a lot of very new, very experimental stuff there and the app is not stable enough. Hence why I’m analyzing re-writing it on the JVM.
I wasn’t sure if fn-fx was actively maintained: https://github.com/halgari/fn-fx/pulse/monthly
it kindof is...I just don't have a lot of uses for it, so I don't make updates often.
I understand.
ClojureFX is actively maintained right now: https://bitbucket.org/zilti/clojurefx Since I now have the main importer, I'm open to recommendations
@zilti: I looked at the code to interface with fxml, and I bet it’s very clever, but I couldn’t understand it and I don’t know how stable it is; so, I wouldn’t want to deal with instability coming from it. Also, I couldn’t figure out how I’m supposed to access members. I read in the manual that it is possible but not how.
fxml is good feedback though, and that's what I need at the moment, more user feedback
(and to be fair, I also wanted to do this manually to understand fxml/javafx a bit better and know what I’m getting into if I choose it)
@pupeno: Class members? Just like normal Java class members. The FXML loader generates a Java class. Everything with an fx:id is represented as a Java class member in the generated class.
So, if I have an fx:id that says label, I can just do (.label whatevs) to access it?
ok… let me give that a try.
What were you referring to when you said you wanted feedback about the binding stuff?
pupeno: feedback about how to improve how to bind to Stuff happening in the GUI (yes, there isn't much at the moment). Right now you'd do
clojure
(.addListener someListenableObj
(fx/fi javafx.beans.value.ChangeListener [ovable oldV newV]
(when-not (= oldV newV)
(debug "Selected row" newV))))
to e.g. listen for a row selection change, which is obviously somewhat cumbersomeThat's something I'd like to see different in ClojureFX. As a user (not speaking as the author a of a competitor lib here), why use ClojureFX if I'm still using Java Interop?
But I guess that's the point of the lib? it's mostly a syntax wrapper?
Well, I'm planning on resolving that listener "issue", but I haven't decided on how to do it yet
@tbaldridge I think my attempt at trying to use JavaFX shows why 😉
Especially because I don't want it to be a framework, I can't stand frameworks (and ironically, JavaFX itself shows why - happy initializing without the Swing hack...)
It's an interesting contrast, and I suppose that's what you have to ask yourself @pupeno do you want to use a stateful OOP UI style, or a React-esque diffing model.
I think both are valid in different situations, but it is a design decision that affects consumers of the UI library
@zilti I got an error that I think it’s coming from the controller generator: https://gist.github.com/pupeno/4eb3fbc61d86e6a622cb81f494972653
@tbaldridge btw, is there any reason event handlers aren't functions in fn-fx? is it because you need to check if the event handler has changed?
@pupeno you have to give it the full-path symbol to the init function - it needs to extract the namespace, too, for the action handlers.
@cjhowe that's pretty much it, but more and more I think the fn-fx model is better. Functions can do anything. Defining functions as data forces users to stick with the circular event model of: state change -> differ -> ui -> event -> change state
In UI code I've noticed that the first thing people do with Event handler functions is go do some sort of side-effect
And the whole idea of fn-fx is to turn JavaFX into a functional interface, with few (or no) side effects in user code.
but it's still pushing the problem of state management and side effects to the handler, i think it could be useful to have more tools to work with side effects and the building of handlers themselves
true, the handler has to do something with the data. In fn-fx it simply passes that data to the function given to the renderer.
So yeah, maybe it's less about functional purity and more about not wanting users to put swap!, send, transact, etc inside a event handler
And I absolutely don't want an event hander mutating another control.
@zilti I’m sorry, to which function?
pupeno: when JavaFX initializes the FXML file and its controller, it has to call an init function. You have to tell ClojureFX the fully qualified name of that function. (defn init [controllerinstance] ...)
`
How do I tell ClojureFX that?
pupeno: e.g. (fxml/load-fxml-with-controller (io/resource "fxml/whatever.fxml") "my.companion.namespace/init")
oh, I have to pass the symbol instead of a function.
Because handlers for e.g. a clicked button also get called inside the same namespace as the init function
Got it.
It seems to want a string, not a symbol: Caused by: java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.CharSequence
For the spec (s/and vector? (s/cat :a int? :b string?))
, s/unform
returns a list. Am I doing it wrong or is this a bug? @alexmiller
currently, it is difficult to spec something that is both a regex and a vector and get the desired behavior for conform, unform, gen, and explain
it is possible to get some of those to work desirably via various workarounds
rich is still trying to decide on the best way to handle this, but it’s definitely a real need - this comes up frequently in macro specs in particular
for your specific example, I would use (s/tuple int? string?)
that should conform to a vector I think
sorry, unform to a vector
@alexmiller thanks, but i actually need the regex spec. I'm trying to transform garden stylesheets:
(s/def ::rule
(s/and vector?
(s/cat :selectors (s/+ ::selector)
:styles+rules (s/* (s/alt :style ::style
:rule ::rule)))))
ok, wasn’t sure
it is possible to fix the unform by using a conformer with an unform function (which has some downsides for gen, form, etc, so it depends which things you care about most)
something like (s/def ::rule (s/and vector? (s/conformer identity vec) (s/cat ...)))
Also, run!
is a drop in replacement for to arg map, and is for side effects
(run! println (range 10))
I've instrumented a middleware function using Cider and if I directly invoke this function the debugger stops fine. however, if I invoke it indirectly bu making a request to Jetty, it does not stop any more. Is this normal?
depends how you started jetty
if you passed the var of the handler to jetty, clojure should see and call the new definition on every invocation, if you only passed the function, it won’t see redefinitions of that function (it’s only resolved once when initially passed in)
@noisesmith hm good point. I'm actually using stuartsierra's jetty component, that is like this:
`(let [options (-> component (dissoc :app) (assoc :join? false)) handler (atom (delay (:handler app))) server (jetty/run-jetty (fn [req] (@@handler req)) options)] (assoc component :server server :handler handler))`
wow, an atom wrapping a delay?
that’s super odd
oh, right, you can put a new delay into the atom, looked up via the component
but a delay inside that?
@hiredman ah sorry you are right, I remembered wrong. it just uses component, but the jetty component is not written by stuart. here is the source:
https://github.com/weavejester/ring-jetty-component/blob/master/src/ring/component/jetty.clj
Could this lib make Clojure faster? https://github.com/jgpc42/insn
not likely - clojure already does byte code generation
i'm running a dev server from my repl with (jetty/run-jetty handler/app {:port 3001})
but running into cors issues. is there a way to set the jetty instance to allow all CORS? I'm not seeing it in the code for that
but you can totally use that lib to avoid writing java for stuff that needs to be optimized
@noisesmith thanks.
@dpsutton - Can you wrap handler/app
in ring middleware? If so, that's how I'd do it.
was hoping i could do it at the jetty level since that's in a ns loaded only in the dev profile
I’ve often felt like the cleanest thing is to have a middleware stack directly driven by the configs - I think the way pedestal interceptors work might allow this? I have also in the past used a wrap-config
middleware that takes a handler, a tergent env, and the current env, and either wraps the handler or returns it unchanged based on matching the env
(defn wrap-config [handler current-env target-env middleware] (if (= target-env current-env) (middleware handler) handler))
I think I found a bug in spec regexes: https://gist.github.com/anonymous/a60c40e84d4995f400dab0122432eabe
@alexmiller ticket welcome?
there are a couple of tickets in this area already, so probably a dupe of those
and https://dev.clojure.org/jira/browse/CLJ-2003 (although some of that is unform)
Anyone familiar with IntelliJ’s “New -> Clojure namespace”? I’m in the process of creating my first Clojure library to push to Clojars. I started with a simple “lein new foo”. This template creates a source structure like “src/foo”, but I would like it to be “src/clj/foo” (in preparation for cljs and cljc being added later). My issue now is that whenever I create a new namespace in IntelliJ, it insists on calling it “clj.foo.newns” instead of “foo.newns”. Inconvenient, and the running “lein test-refresh” complains right away about a missing namespace. I can’t seem to figure out where this might be configured, project.clj, IntelliJ itself or possibly by Cursive. Sidenote: For my regular projects I use the Luminus template that has my desired source structure, and then IntelliJ works the way I would like. Any help appreciated.
how you define :source-paths in your project.clj?
strange, i saw the same problem in emacs when source paths was defined like “src”
:thumbsup:
@mokr It sounds like src/clj
wasn’t marked as a source root, perhaps your lein project wasn’t synced?
@mokr try this: select src folder, right click, unselect as Root SRC Folder, select clj folder, right click, select as root source
anyone know which types are not comparable using =
? The only thing I have found so far is java.util.regex.Pattern
i.e (= '#"a" '#"a")
returns false
.
I am comparing quoted expressions so for example even though java.util.regex.Matcher
is not comparable and (= '(re-matcher '#"a" "a") '(re-matcher '#"a" "a"))
returns false, this can be compared by compaing by (map = '(re-matcher '#"a" "a") '(re-matcher '#"a" "a"))
which gives (true false true)
meaning that it is only the Pattern
that can not be matched
So I guess I could re-phrase the question. Which types that can be represented in source code can not be compared?
@beoliver From the docstring for =
:
Equality. Returns true if x equals y, false if not. Same as
Java x.equals(y) except it also works for nil, and compares
numbers and collections in a type-independent manner. Clojure's immutable data
structures define equals() (and thus =) as a value, not an identity,
comparison.
So, effectively everything that can compare equals
in Java under the hood.So java.util.regex.Matcher
's equals()
method doesn't behave the "intuitive" way I guess.
boot.user=> (instance? java.lang.Comparable "")
true
boot.user=> (instance? java.lang.Comparable #"")
false
But you can't exhaustively specify what "can not be compared" since reader literals let you create fairly arbitrary objects and the behavior of equals()
is going to depend on the underlying type.
(and, strictly speaking, they can be compared but the answer might not be what you expect)
And there's also stuff like (= Double/NaN Double/NaN)
producing false
-- is that what you're expecting?
"DFA"?
No, I'm talking in general about source code expressions that look equal but =
will produce false
for.
the thing is that I am comparing quoted expressions so (= 'Double/NaN 'Double/NaN)
is true
as we are just comparing symbols. The only problem that I have encountered is that of the Pattern
. I guess this has more to do with the dispatch macro?
Yes, if you're just comparing reader-processed data -- hence my initial comment about reader literals #some-thing {:stuff 1}
Whether two of those will compare equal (`=`) will depend on the type instance that it expands to.
@delaguardo Comparable is about ordering, not equality -- and the definition of Comparable specifically states that x.compareTo(y) == 0 does not necessarily have to mean x.equals(y) (although that is normal)
Java equality is a strange beast, unfortunately.
They are becoming more common. They were only introduced a few versions ago.
I guess the other way to compare for equality would be to fully macro expand both expressions and then compare strings factoring that two expressions may use different locally bound names
I guess the question we should be asking is: what problem are you actually trying to solve?
Given an top level expression E in namespace N (source file). Assume that you have run an expensive test on E and it passed. Now, when the code is changed, I only want to run the test if the code is different. I don't want to run all tests for that namespace. So I locate the expression that has been changed and decide if the changes actually "change" the expression. Eg variable renaming should be ignored.
Hmm, a very difficult problem in the general case...
I wrote a proof of concept where i just used a special case test against Pattern
... but I would prefer to have something that is provably correct.
Something to consider is that a reader literal can generate a different value every time it is read, depending on what it does.
I’d say that if you do that, you deserve what you get
and also, don’t do that
I will make no attempts to justify it beyond "convenience" 🙂
does not seem convenient :)
This particular case is actually just an "include" syntax for an EDN configuration file so the reader is local to "reading in the file". It was more meant as an example of just how #foo/bar 0
can "do stuff" behind the scenes and make code level equality harder to detect.
literals should be values, comparable by equality. the end. :)
what do you think this is, OO? ;)
yeah, this I what I am trying to fully understand. When is it "safe" to consider something symbolically equal etc
For example, we have a reader literal we use in our configuration library that "includes" a file (reads an EDN file) -- so the value it returns depends on an external file's contents at the time of processing.
Also, reader literals can have different meanings when the (external) data_readers.clj file is changed. So two separate test runs could have #foo/bar 0
turn into different values.
I think it's undecidable in the general case. But tractably complex in a more realistic case, i.e., with a few caveats since worst case you say code has changed when it hasn't, so assuming unequal is "safe" but less than optimal.
"Provably correct" is definitely undecidable tho'. "Mostly correct" is tractable.
seems reasonable, although I wonder if it is the macros or the calls to java that would make it so. I can imagine most expressions where the user calls out java would fall into the "I'm going to assume that the world has changed"
I guess that anything that is not the same object can not be said to be equal for eternity. Even with a regex, perhaps the reference to one of them was changed etc.
Well, Java's Matcher is stateful as I recall so it has to use non-equal objects. A bit surprised about Pattern but maybe it's also stateful (ages since I dug into that low level).
As for var renaming, to be honest, if I do a refactoring that is just a renaming, I'd probably still want my tests to be re-run -- just to be sure.
And then of course there's always the question of "Why is your test so expensive?" so I might find ways to make the test cheap enough that I don't care if it runs all the time.
I must admit that the genesis for this case comes from the fact that tests for our project take me between 5 and 30 mins to run (lots of db access) while for the rest of the team they are consistently fnished after 5. Thought I could be smart (although I appreciate that this is not fixing the problem!) On the other hand I am thinking about a masters thesis... as I am working with clojure I thought it could be an interesting space (static analysis, code/project/complexity managment etc)
Is there a way to see what ring middleware is configured in a runtime way? to my surprise, a middleware is in use, which I've not configured myself, and I'm curious where/how it got included
usually people have something like wrap-defaults
that throws a bunch of middlewares onto your app
because functions are opaque, and middleware each return a new function, analyzing the middleware based on the runtime value of handler would be tedious (involves checking which namespace the function was made in, figuring out where in that ns the middleware was defined, then figuring out who called it and where the inner function came from etc.)
yes exactly.. I'm surprised why middleware was designed in this way. Having a bit more structure would make it less error prone IMHO. At the moment it is just a pile of higher order functions on top of each other.
this is why pedestal interceptors were invented btw
Hi all, I can't seem to figure out how to define a value as a valid schema as per the plumatic/schema
library. Can anyone familiar with the library give me a hand? I need an tuple that takes an array of variable numbers of keywords or ints and a string. So: [[:keyword Int :keyword ....] "string"]
Example: [[:users 0 :names] "Steve"]
Thanks for any help!
@fedreg that would be
[(s/one [(s/one :keyword "explanation of this keyword")
(s/one Int "explanation of this int")
(s/one :keyword "another explanation")]
"explanation of this vector")
(s/one String "explanation of this string")]
(or something a lot like it)as you can probably see, schema doesn’t really like the usage of heterogenous vectors, and pushes you toward hash-maps which are much more elegant to specify
also, I have no idea what the ....
in your example stands for so I ignored it
but you can probably extrapolate
@noisesmith Awesome! Thank you!! The ....
just meant there could be more keywords or ints (it's a path) but I can definitely work with your example. Thanks again!
yeah- if they are arbitrarily mixed with no pattern you probably want something more like [(s/either Keyword Int)]
to describe the whole collection
or s/conditional
with cases for matching each
hi, i'm trying to connect to my database with clojure.java.jdbc
, and get-connection
keeps throwing No suitable driver found for jdbc:
, where my DB is tracer. i'm not really a java person and i have no idea how to solve this, does anyone know what the issue is?
@cjhowe do you have the postgresql dependency defined in your project?
[org.clojure/java.jdbc "0.7.0"] ;; sql library
[org.postgresql/postgresql "42.1.4"] ;; database driver
[com.zaxxer/HikariCP "2.6.2"] ;; connection pool
the connection pool is optional, and you have to set it up manually, but the driver is required
btw, I find it easier to create a JDBC "DataSource" and then pass it to clojure.java.jdbc with {:datasource the-ds}
as the connection map. DataSources are kinda like connection factories.
I've been working on a us gov patient matching competition, and I instantiate my connection pooling datasource like so: {:datasource (-> "resources/db.properties" HikariConfig. HikariDataSource.)}
my db.properties looks like this:
dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
dataSource.user=postgres
dataSource.password=REDACTED
dataSource.databaseName=pmc
dataSource.portNumber=5432
dataSource.serverName=localhost
I don't use any clj wrapper for the Hikari connection pool. Just the tiny little bit of interop i already pasted above
i was considering using heroku so i would have to get the DB connection string from an environment variable
that works. you can pass the postgres database url directly to the HikariConfig. Untested:
(-> (doto (HikariConfig.)
(.setJdbcUrl (System/getenv "DATABASE_URL"))
(.setPasswordOrWhatever foo)
(HikariDataSource.))
Hi, I’ve been learning clojure for the past few weeks.
Two of the most recent tutorials (one for backend with pedestal, the other one for front end with reagent) are using boot instead of lein.
Is the community moving towards boot? Is everything that is possible with boot possible with lein?
Are they the same thing? Do I need to learn both of them?
They're in the same category. boot
takes a more library and code-based approach to scripting
a build. It is newer. There is less learning material available for boot, but the good people #boot are very responsive. lein
is older and stabler, and takes a declarative approach. It's a bit slow and despite some design warts, works well.
I would refer to the yearly State of Clojure surveys to see if there is any community motion, but I don't think there is an en masse migration
thanks for the comments
which one would you recommend to newcomers?
i used boot to start, if you're somewhat experienced with functional languages i'd definitely recommend it
Yea it seems that way, I guess I’ll bite the bullet and setup boot since that’s what the tutorials use
There is a third option emerging that is distinctly not in the same category as lein and boot (build tooling) but in the smaller dependency management category, and that is the new tools.deps
+ clj
launcher
and disregard everything else until I gain more experience in clojure
clj is a little bash script that wraps tools.deps. All it does is fetch dependencies, shove them on a classpath and launch a clojure REPL
it's functionally equivalent to java -cp $(lein classpath) clojure.main ...
with the middle subshell cached intelligently
coming from node I need to read up on this classpath thing, in nodejs every dependency was shoved inside a folder in the projects directory
a classpath just a bunch of directory roots or jar files that contain the necessary code.
so when you declare deps in boot/lein, they download them to your project directory, add each of them to the classpath and then pass it as an argument to java when running clojure?
got it, do lein/boot come with a different repl? or is the repl independent from those tools?