Clojure builds as an amalgamation of orthogonal parts
blog.fogus.me
blog.fogus.me
I think the coolest thing in the pipeline is going to be `add-libs`: https://insideclojure.org/2018/05/04/add-lib/
I made a little orgmode literate demo that is selfcontained (no deps.edn) and even produces some inline SVG https://geokon-gh.github.io/literate-clojure.html
I hope this gets added into `core` and single file Clojure programs become a norm. Right now you still need to add tools.deps.alpha into your default user deps.ends for things to work. If this stuff gets into core you could finally send someone a file/gist and they could run it directly. I think that'd really improve the Clojure ecosystem. You can imagine sharing one-file issues/demos/examples and people don't need to reproduce your local setup or clone a whole repo
Clojure (seems to me at least) has always preferred to cater to professionals who want to learn languages, tools and concepts deeply rather than being quick to learn. So naturally, the ecosystem tends to lean towards simple tools you use as building blocks rather than one-size-fits-them-all ala Ruby on Rails. This does make it harder to get started with Clojure, but once you've taken the time you to learn everything, it really pays off.
It's not just about education (though that's important as well). I think the top two use-cases are scripts and issues.
Issues, .. well you want to be a nice dev and post issues with some sample code that reproduces the problem.. but you don't want to have make a whole repo (and then have it floating around on your github indefinitely?) just to show an issue/problem that takes 20 lines of code. So then most people end up copying the problematic code into the github issue text box.. and... it sorta kinda works? but not really. Neither you nor the library maintainer can actually copy and run the code now, so you both end up squinting at it and trying to guess what went wrong. It's just a mess. Even if you make a repo, then how do you get feedback? Other people need to fork and upload their own versions of your demo repo? It's just too heavy handed.
For scripts and quick hacks you want to bang out code quickly. Open up a new file and write .. slurp some files, massage some data, look at some values .. maybe spit out a plot etc. And you want to be able to quickly look over them 2 months later and see what the hell you did. You want to be able to send and share them easily. It really needs to be all in one file otherwise it's just too much of a hassle.
I think the final killer features of `add-libs` that I'm appreciating more as I use it - is that since you can add libraries right in the REPL dynamically you never need to modify your deps.edn and reboot CIDER! It seems minor, but restarting CIDER just always sucks.. and at least for me it breaks the "flow". You loose all the state you had (if you're poking around you can have lots of little variables around) and you loose all your command history. You can only modify your workflow so much to accommodate the inevitable occasional CIDER reboots.
Example gist: https://gist.github.com/puredanger/cbcdd9f54deb7fb7309fdd632...
Run it: clj -Sdeps '{:deps {a/gist {:git/url "https://gist.github.com/puredanger/cbcdd9f54deb7fb7309fdd632..." :sha "116ac594c922be6ed9d3366f5ac89ed2b0a082f5"}}}' -X foo/bar :name '"Alex"'
and thank you for the demo. With the`:paths ["."]` you flatten the project and minimize the example size. It would make demos look much better
In the end it's not quite as ergonomic as using `add-libs`, but it something that works today. Hope`add-libs` makes it into core some day. Having one-file programs would really take away the remaining friction of sharing code.
PS: thank you for your help on ask.clojure.org the other day. I got your book and am looking forward to reading it
But if you wanna have launchable scripts/code-snippets then having to ask people to append -Sdeps blahblah complicates things a bit
btw, I had a question about dependencies resolution that's always been a bit mysterious for me
from: https://insideclojure.org/2018/05/04/add-lib/ "Additionally, this code is not just blindly adding dependencies you ask for but is actually considering them in the context of the dependencies already on your classpath. So if for example, you already had a version of org.clojure/data.priority-map above, it would do the dependency resolution in the context of that version and not resolve to or add a new version to the classpath."
If you have libraries A and B and they depends on library C. Say A has it tagged at version v1.234 and B has it tagged at v1.345. From the doc's glossary it sounds like it will just get the latest version and hope nothing breaks. https://clojure.org/reference/deps_and_cli#_glossary
I haven't had it happened, but I could imagine a scenario where you add library B and library A starts to misbehave. And if I understand correctly, there is no way to have multiple versions of a library in the path. Is that right?
I know in C++ world this is a big unspoken issue. Package managers usually just mash things with the latest version and hope it works, while some build systems (like Hunter) will modify symbols so you can link to two copies.
{:deps {clojure.java-time/clojure.java-time {:mvn/version "0.3.2"}}}
to their deps.edn and they're good to go. Raising only the questions of:
1. What are the available keys of this map?
2. Why do I have to repeat clojure.java-time twice? Is this arbitrary? When are these different? (turns out it depends, if anyone is trying to figure that out)
3. What is a ":mvn"?
4. Is there a list of libraries I should be consulting to work out what versions mvn supports?
5. Is mvn my only choice here? Are there other repositories?
6. The map is one of Clojure's most flexible data structures because it can take arbitrary keys. Circling back to 1 - What other keys are supported?
Answering 1/6 is particularly interesting because the Deps reference [1] is long, undecipherable and frankly not-very-well written reference material. It starts off as more of a tutorial than a reference and requires the reader to engage with how Clojure programs run rather than how to download a library.
Someone coming over from Python would look at this, recall "pip install library" and then a lot of them would do the sensible thing and give up. Potentially never realising that the correct thing to do is go with leiningen. I'm not even sure if pip has command line flags, I've basically never been exposed to them.
Simple is all very good, I'm sure the people who struggle through to figure out how deps actually works are the better for it, but it would be better if adding a library were easy as well as simple.
[0] https://clojure.org/guides/deps_and_cli#_running_a_repl_and_...
I use Leiningen as it's convenient, but I check back on the tools.* ecosystem occasionally to see the progress being made. My suspicion is that tools.* will eventually supersede Leiningen and Boot, but that it will take two to three years at least.
Currently it's easier to use "lein new", but I can imagine a Babashka wrapper around tools.deps and tools.build eventually supplanting this. Being able to write, for example:
bbb new foobar; cd foobar; bbb add clojure.java.time
to automatically generate and update a deps.edn would be convenient, and potentially with far lower latency than Lein.At the moment deps feels more of a 'make your own build tool' kit. Simple, sure, but not easy to use: you need to tell it how to build jars or run tests, which is IMO essential functionality of a build tool.
I don't want to tell my build tool how to achieve these essential things, and risk ending up with a mess of projects that all use different aliases to run different tests runners and different jar building plugins. Build tooling is one place where convention over configuration matters to me, and why I still prefer leiningen.
I'm hoping deps and tools.build will evolve from a 'make your own build tool kit' to a build tool that exposes the kit it was built on, so that most projects don't require anything custom, and be flexible enoug so that one special project can leverage the 'simple orthogonal parts' in the kit.
I see tools.build as a step forwards, but it's not there yet IMO, the user still needs to configure a bunch of stuff to get essential functionality.
I hope the core team will make a build tool on top of the 'make your own build tool kit' that is deps and tools.build, that comes with good defaults and essential features for the common projects, while exposing the 'simple orthogonal parts' in the kit so these can be used for complex projects.
> Ideally a multi-purpose tool like leiningen should be built on top of those simple parts
tools.build is 2 weeks old according to the announcement.
Leiningen is nearly 12 years old.
I haven't looked into tools.build, so not arguing for or against it.
Disclaimer: Have been doing some work on leiningen from time to time.
Also I first ran into Clojure in 2011, and while I liked the language, I simply loved how leiningen just worked and I was instantly a fan, not even thinking about how it's built, just nice for the users[0]. And I've always hated projects/ecosystems that made you jump through hoops or some arcane syntax and exploded half the times you tried to build. So "easy" is really important.
But for professionals who spend a lot of time with the same tool, "simple" is more important in the long run.
The whole thing was "it must be simple, even if the user needs to spend more time learning it" which is only true for people who learn the thing and keep on using it. For us who come back to a project after months (because we switch between tech stacks) it might be just twice the amount of work, if you need to relearn it every time.
Spec and tools are being developed to lay a foundation, so one can talk about and build upon the ideas of granular, function level dependencies, change (breakage vs. accretion) and so on. Ultimately this is about how code changes over time.
It may be the case that lein's internals are a blob of complexity, without boundaries defined in the "right" places. Maybe Clojure Tools' 3 or 4 components have it correct (though I'm skeptical of that, since there's overlap between them -- even moreso now with tools.build).
Conversely, it may be that build tooling is by its nature a messy affair (dealing with dependencies, resources on the filesystem/network, interaction with tools, tests, etc.), and that making those problems go away to the extent technically possible does your community of developers the greatest service. That would explain why talk of CLI and deps invariably has pushback and lein advocacy.
I wouldn't know if lein is internally complex because I've never had to go digging in it beyond the documentation project.clj file, even to do rather complex build definitions. By comparison, I've easily spent man-weeks on getting to the same place with deps.edn, and I would still find even defining the roles of its components difficult if pressed. I also worry that if we hit new Clojure devs with a wall of tooling issues like this, they won't stick around, even if we tell them the underlying theory of their tooling is more sound.
This sounds like a educational problem rather than "lets dumb down our tools to cater to new developers".
Clojure seems to balance "cater to professionals VS being easy to get started with" to the first mentioned segment, something I myself has no problem with.
There are lots of other things - they are all additive. Many Clojure libraries and tools don't need a build.clj or tools.build at all.
My experience is that deps.edn is much easier to get started with. With a template from something like clj-new you can get testing, a basic jar building script, etc to get started with and it's all there for you to muck with if needed.
First, start here: https://clojure.org/about/rationale
Then Deps and CLI is explained here: https://clojure.org/reference/deps_and_cli
> what I should be using now
As always, depends. Building a project in a rush? Use whatever you're most comfortable with and have the most experience. Want to further your understanding of Clojure the ecosystem? Dig into Deps & co.
> I’ll just stick to cutting and pasting onto my leiningen config I think
Yeah, if that's your general approach to development, use whatever you already know. Once you're ready to understand the tools you depend on, then you can start looking into tools that was built with some more thought behind them, like Deps.
tools.build is a helper lib to write programs that make artifacts such as Jars or Uberjars. It competes with things like depstar in that sense.
Tasks are still meant to be managed by deps.edn (tools.deps) and the Clojure CLI by having tasks be Clojure programs that are invokable as either -X, -T or -M, and the idea is you would orchestrate them with another tool like make, a shell command or script, babashka task runner, npm scripts, just etc.
This means that any Clojure program can be a "task". All you need to add a new task to a project is define an alias for it in the project deps.edn.
Now one caveat is depending on the type of program the task is, you might need to call the alias with either -X, -T or -M option:
clojure -T:alias
clojure -X:alias
clojure -M:alias
Ideally all Clojure programs that implement a task move to rely on exec-fns, and thus would be called with -T or -X, which will eventually allow you to chain tasks together where the return of the first task exec-fn will be passed to the next task's exec-fn, so you can compose tasks and run them consecutively from the clojure CLI.So tools.build is just a lib that you can use to help you write tasks (which are just normal Clojure programs), but it is itself a set of exec-fns tasks which you can use directly as an alias as well:
clojure -T:build-api copy-file :src '"./src/tempo.clj"' :target '"./output/tempo.clj"'
with alias as: :build-api {:deps {io.github.clojure/tools.build {:git/tag "v0.1.6" :git/sha "5636e61"}}
:ns-default clojure.tools.build.api}I think having a cohesive story for this is table-stakes in a modern programming language.
This is even worse than how it is for Python - which does have a similar problem - because at least in Python's case you can "just run" your scripts up until the point where you need dependency management and packaging. You can't even execute a .clj file (as far as I know) without going outside of the official resources.
$ clj --version
Clojure CLI version 1.10.3.849
$ echo "(println \"hello world\")" >> hello-world.clj
$ clj hello-world.clj
hello world $ java -cp clojure.jar clojure.main hello-world.clj
Still, I'd argue it's about the same as Python, at least for the most basic use case, like what you were complaining about in your previous comment.Most people just use Leiningen and don't mess with this stuff.
This post does not paint a good picture of Clojure development tools, but don't get turned off by it.
99% of Clojure projects I've worked on just have a declarative project.clj file and building it is as simple as:
$ lein uberjar
I will never understand the core team's acute NIH Syndrome and tendency to fragment the community, but I guess Open Source Is Not About Me.What I want from a build tool is: manage dependencies, starting a repl, running tests, and building a jar. Leiningen provides me this: lein repl, lein test, lein uberjar. Simple and exactly what I need, and importantly: i can expect that all leiningen projects use the same commands.
Deps can be made to do all these things, and more, but you need to configure it. Which test runner should I use for tests? Which library should I use to build an uberjar? Since it's all configurable, I risk that every deps project gets it's own special snowflake build setup, and I need to spend time avoiding this.
Until deps gets a standard way for building and testing, i'm sticking to leiningen.
Not sure what you're on about.
lein AFAIK produces the same classpath every time.
deps: Maybe put the paths first and then let's gamble what a seq on a map produces...
https://github.com/clojure/tools.deps.alpha/commit/c22ad46c6...
Lieningen gets out of my way and lets me do the things that really matter, and I'm not switching till I see some clear advantages of the new tooling.
I like deps, but I can’t quite articulate why.
Data > functions > macros is a core concept of Clojure. Does that come into play here?
These are prompts from a curious outsider’s perspective.
tools.deps makes better choices when dependency specs conflict, so I would suggest trying https://github.com/RickMoynihan/lein-tools-deps if you otherwise want to use lein, and get the best of both worlds.
By better, I mean:
> Leiningen and Maven, when there is a conflict always pick the version that is closest to the root of the dependency tree; where as tools.deps always picks the newest.
Leinengen’s (defproject) takes a series of keys and values. It is declarative. You can probably get into dependency hell if your config is complex enough, and I’m almost certain it’s doing more than I understand under the hood. (like why doesn’t it just take a map instead of being a macro?)
tools.deps OTOH appears to need both a bunch of top-level definitions in a namespace (instead of a map literal) AND a hand-rolled build.clj file with custom functions to do most of the things that leinengen considers to be boilerplate. This does imply that you have much more flexibility with tools.deps, but it also raises the bar for entry, and it doesn’t appear to be as data-centric.
If I want to do ANYTHING meaningful with Clojure, I need a project environment to start with. I am not confident about how to do this with clj, even after reading https://clojure.org/guides/deps_and_cli#_writing_a_program Maybe I just haven’t found the right guide, or maybe clj wants me to commit more to working REPL-first than my dev environment (VS Code + Calva) has good support for.
What I’m wondering now is, if the gains that tools.deps provide are worth the entry price, does it make sense to have something like “create-react-app” to generate a project directory with sane defaults that can be customized if/when needed, if your project doesn’t fit the 90% case? Does this already exist?
‘dustingetz linked the Simple Made Easy talk. I get the distinction between the two, and I don’t want to swallow the hairball. I’m already sold on the Clojure paradigm.
But I ALSO still want to “get this instantly and start running it in five seconds,” and I don’t understand why I’m not allowed that if I choose tools.deps. I don’t think the two goals are more than accidentally orthogonal.
also those custom hand rolled fns are … a library, which is way better than a build plugin
If tools.* is the language maintainers’ preferred method, I think there is value in having a single command to set up a “simple/default Clojure project” in a folder, even if that only cuts the commands down from four to one.
$ mkdir hello-world
$ cp deps.edn hello-world
$ cd hello-world
$ mkdir src
I don’t understand where that deps.edn comes from in a fresh project. It’d be great to have “clj create hello-world” build one for me in ./hello-world, use my running Clojure version as a dependency (if that’s how “clj” works), create the child src/ (and /test if that’s a best practice) folders, etc. The idea being to generate a local library on-demand with sane boilerplate defaults and almost no knowledge. I can learn later what Else I might need if I can get it up and running quickly first.Leinengen looks easier to me for my admittedly trivial use cases, because I don’t need a library or plugins at all. And again, I might be in the minority. But if folks keep saying “lein handles 90% and is easier,” and if we can make tools.* handle those same 90% cases trivially, it seems worthwhile.
that's it
(! 639)-> mkdir fresh && cd fresh
(! 640)-> mkdir -p src/myns
(! 641)-> echo '(ns myns.myfile) (defn foo [{:keys [name]}] (println (str "Hello, " name "!")))' > src/myns/myfile.clj
(! 642)-> clojure -X myns.myfile/foo :name "filoeleven"
Hello, filoeleven!
No deps.edn file needed -- until you want to add extra dependencies etc. # this is a one-off step to install the clj-new tool:
(! 646)-> clojure -Ttools install com.github.seancorfield/clj-new '{:git/tag "v1.1.324"}' :as new
Installed new
# now you can run it anywhere to create new app or lib projects:
(! 647)-> clojure -Tnew app :name myname/myapp
Generating a project called myapp based on the 'app' template.
(! 648)-> tree myapp
myapp
|____.gitignore
|____.hgignore
|____CHANGELOG.md
|____deps.edn
|____doc
| |____intro.md
|____LICENSE
|____pom.xml
|____README.md
|____resources
| |____.keep
|____src
| |____myname
| | |____myapp.clj
|____test
| |____myname
| | |____myapp_test.clj
This is already set up with testing and JAR building for you: (! 649)-> cd myapp
(! 650)-> clojure -X:test
Running tests in #{"test"}
Testing myname.myapp-test
FAIL in (a-test) (myapp_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
actual: (not (= 0 1))
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
(! 651)-> clojure -X:uberjar
[main] INFO hf.depstar.pom - Synchronizing pom.xml
Skipping paths: resources
[main] INFO hf.depstar.aot - Compiling myname.myapp ...
[main] INFO hf.depstar.uberjar - Building uber jar: ./myapp.jar
[main] INFO hf.depstar.uberjar - Processing pom.xml for {net.clojars.myname/myapp {:mvn/version "0.1.0-SNAPSHOT"}}
(! 652)-> java -jar myapp.jar
Hello, World!For the CLI -- which uses tools.deps under the hood (not the brand new tools.build) -- all you need is your deps.edn file: a declarative hash map describing your dependencies and, under aliases, any additional tools you want to use.
The build.clj file is something some folks have already been doing in one form or another for large, complex projects (and would be familiar to anyone using Boot instead of Leiningen).
tools.build provides some common tooling and structure that should help reduce the complexity out there by providing a standard way to do a number of tasks that folks were already doing a different way (yes, including some of the core Leiningen tasks) and to provide a composable, function-based set of tools to help you write your own build scripts.
The bottom line: if Leiningen does what you need, great! Keep using it. If you find Leiningen to be restrictive and not a good fit for your project -- as we did, years ago -- then tools.build might be better. We left Leiningen for Boot many years ago and if we hadn't started to run into bugs/issues with Boot at scale we'd probably still be using it. Instead we switched to the CLI/deps.edn back in 2018 and we've just adopted tools.build which has simplified some parts of the build script we already had.
I guess it boils down to the fact that I don't find it significantly harder to use than Leiningen, there are great libraries and tools that work well with it (shadow-cljs can source its deps from deps.edn for example), and I wrap the entire thing in a task system anyway (lately, Babashka tasks).
I guess it helps that its the "official" way to do things, but that's not a primary concern for me.
Then again, gradle builds seems to be more popular than maven these days.
Want a project with both Clojure and Java? Want tests that magically integrate with CIDER's test functions? Complex build profiles for different target environments? Lein makes this stuff as painless as possible, whereas these things are possible with Clojure Tools, but have fun wasting time messing with it and doing awkward stuff like inlining strings of eval-ed clojure code in your build file. With Leiningen these are solved problems.
I also find the core team's behavior perplexing. I'd argue the messy tooling they've been producing has probably been net negative value add to their community. Working downstream from this stuff has degraded the dev experience. Thankfully, it can be safely ignored.
For experienced Clojure developers this might seem trivial, but new people (in my experience) are very impatient and want to get started ASAP. Honestly, who can blame them? Why does it have to be so difficult in this day and age?
Another thing I hear Clojure developers say is that beginners can start with Lein, but once you get more experienced with Clojure you can switch to tools.build. Why does a build tool need to be so unusable that you can only use it when you become more familiar with the language? You don't see that with Ruby's Bundler, or Elixir's Mix. They just work, just like Lein does.
Simple Made Easy is what attracted me to Clojure, and tools.build, tools.deps, tools.cli are aligned with this mission. Cognitect is doing exactly what they said they would do and have been doing all along.
Re-read the talk here: https://github.com/matthiasn/talk-transcripts/blob/master/Hi...
(PS, a note to Cognitect comms – maybe start here with your next blog post and see if people react differently!)
The whole point of the talk is that choosing "easy" (as in easy to get started) solutions while ignoring the complexity will be harder to maintain in the long run over choosing the "simple" solution that takes a bit more time to set up. Personally, this tracks pretty closely with my experience.
(Relevant slide from the talk: https://res.infoq.com/presentations/Simple-Made-Easy/en/slid...)
The claim here is that the thing that makes your language easier to pick up initially nonetheless hobbles it for long-term use - refactoring, adding features. There is a business case for making the latter easy at the expense of the former.
Code can be easy or hard to write and/or change at various levels of complexity, and it's hard to solve for everything when designing a language.
There's lots of other things you can do with clj - these are all additive. At some point, you might (possibly) need a build script. When you do, it's written in Clojure using a pretty straightforward set of functions and you can copy/paste in a starting point. You can grow that build file forever.
The fact is, the solutions the core team have been putting out for the last several years have been neither simple or easy, and in fact tend towards a weird and arcane complexity in service of problems no one seemed to have.
I remain baffled by Spec, by the clj tool, by deps, and now this. At every step it seems as if the team lives in their own world, and not in ours, and each new tool is as cryptic and convoluted as the last.
They frequently don't even seem to encourage code that is consistently idiomatic to how the rest of us even write Clojure. Who else was even using namespaced keywords for anything until Spec decided to not only use them, but link the entire mechanism to a mutable namespace registry? How is that "functional" in any way?
No, they are neither simple nor easy. They make new project setup un-necessarily hard, they make builds un-necessarily hard and complicate stuff for both newbies and intermediate programmers. Only experts with tools.* may be comfortable with this and I have seen these experts actually copy sample template `deps.edn` for new projects and use long complicated aliases.
For both simplicity and ease-of-use this should be done by a tool and not a human.
When I recommended some friends to Clojure, they stopped after getting stuck in deps.edn. Clojure setup feels more complicated than C++/Java/Kotlin/Python project setup nowadays.
I wish Clojure would use Rust's Cargo for inspiration.
`cargo new --bin projName` and open the folder in your editor/IDE of your choice.
We have a fairly large codebase (113K lines) in a monorepo with over a dozen artifacts built from about fifty subprojects. Leiningen really wasn't a good fit for us so we're very glad of alternatives.
I feel the same way. It has definitely hurt the ecosystem.
Every Clojure IDE, for example, has to support a new deps/build system that is still a WIP and doesn't come with batteries.
I run REPLs for weeks, sometimes months, so I don't want my editor to start my REPL since my editor is going to get restarted a lot more often than my REPLs!
Also, that's the master version of project.clj, which is a superset of all possible features lein supports. Nobody's project.clj file looks like that, though I suspect you know this. Should we compare that complexity to a monster build.clj and this master deps.edn:
https://github.com/seancorfield/dot-clojure/blob/master/deps...
Lein is also a singular tool vs. a murky interaction between a suite of tools (tools.deps with deps.edn, Clojure CLI, and tools.build).
The README explains that and even links to a better-documented and more carefully curated set of aliases that folks might want to use instead (the Practicalli repo).
In reality, I use very, very of those aliases from that dot-clojure file, because the projects I work with already have the ones I need for working on those projects. That can be as simple as a :test alias or it can be up to a dozen aliases used in variations combinations for a variety of tasks.
You can certainly use the CLI and deps.edn for ClojureScript, by the way -- check out Figwheel Main which uses deps.edn and provides a nice workflow.
We had to scour the internet and engage in weeks of teeth grinding to get CLI/deps to do all the things our project needed.
I think perhaps the biggest issue for most people is discoverability: how do I learn about the tools I need to get "task X" done with the Clojure CLI, deps.edn, and associated "stuff".
Even though I created clj-new (and boot-new, on which it is based, which in turn was based on lein's new), I pretty much never use it myself, except for quick examples to test stuff and/or help beginners with. I always start a project from scratch with an empty deps.edn and an initial src/myns/myfile.clj or similar.
I use depstar heavily but I strongly suspect that it will ultimately go away as tools.build matures to the point where all the special cases that depstar handles today are folded into t.b.api/jar and t.b.api/uber -- I don't want to maintain something that duplicates what the core team have poured so much time, effort, and careful thought into!
I'm already contemplating a "reboot" of clj-new as a very simple wrapper around t.b.api/copy-dir with some simple templates -- since that's most of what clj-new does (even the more complex template-running stuff is really only a slight augmentation of t.b.api's create-basis + java-command + process).
I'm not sure what this is referring to here. The "Getting Started" guide for ClojureScript uses deps.edn and the clojure CLI to introduce ClojureScript. You can certainly use figwheel or shadow-cljs with clojure tools.deps and deps.edn. And since the new build.tools allows just running a program, you can of course build clojurescript from a `build.clj` build script. I cannot think of a step in this process that isn't amenable or simplified by the use of the clojure cli, tools.deps, and now build.tools.
;; This is an annotated reference of the options that may be set in a
;; project.clj file. It is fairly contrived in order to cover lots of
;; different options; it shouldn't be considered a representative
;; configuration.
The comment from ‘bm3719 makes tools.build sound like a less useful solution when compared to the practicality of leinengen, and your reply only has ClojureScript in its favor if I trust that the linked sample project.clj is indeed not representative (which matches my limited experience with my own “useful toy” projects and the other projects I have browsed). “awkward stuff like inlining strings of eval-ed clojure code in your build file” sounds really bad, but that could be equally contrived for all I know.I also don’t understand the value of running builds from the REPL. What kind of developer process makes that a useful thing to have? What am I missing?
These are genuine questions, not advocacy for one thing or the other. I want to work professionally with Clojure(Script) enough that it is a prerequisite for me to consider changing jobs. I’ve only used lein thus far, so I don’t have enough experience with either tool to share a meaningful opinion here. I am extremely curious about the state of things though.
Lein is easy to get started with, and for simple projects does everything you need. tools.build is endlessly flexible. For some projects, tools.build/tools.deps was much easier for me to use than leiningen (easier to just write clojure code directly than figuring out the whole lein plugin stuff). Use whatever works for you.
bad, obv, because all the RAM the JVM needs.
- REPLs start up very slightly quicker
Only time I can think of when I need to restart the REPL is when I add a new dependency, but that doesn't happen so often.
So why you need fast REPL startup really?
The whole 'reloaded workflow' is a workaround to the slow restart IMGO. It works well, but there is a learning curve, especially coming from languages with fast edit-compile-run cycles such as go.
I've also worked with scheme, and having a repl up and running in milliseconds is really nice.
True that temporary edits could in theory get in the way, although I never had that happen to me in ~6 years of experience with Clojure. Server bindings to port should not warrant a REPL restart, usually you can save the returned function/data from starting the server to also turn it off (and unbind the sockets).
> The whole 'reloaded workflow' is a workaround to the slow restart IMGO
I disagree with this, it's not a workaround. It's a different workflow, yes, but it is different on purpose, not by accident.
Being able to evaluate and run snippets of code will (for me) always be a faster workflow than even the fastest edit-compile-run languages, since I can evaluate code in the middle of functions and won't have to have any unit tests just to test something temporarily in isolation.