12 ms·
Clojure builds as an amalgamation of orthogonal parts
- divs1210 5y agoNote to non-Clojurians: 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.
- synthc 5y agoI've tried deps several times, but I keep coming back to leiningen. Can anyone explain the advantage of using deps to 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.
- dustingetz 5y agodeps is extremely simple, which means very little time spent debugging, and it never breaks
- beders 5y agodeps can't produce stable builds as it insists dependencies are not ordered. Yet on the classpath they are. That order shouldn't matter, but it very well does. 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...
- dustingetz 5y agothanks, didn’t know that, will definitely be annoyed when i hit that someday
- john-shaffer 5y agoThat appears to have been true for only about a month: https://groups.google.com/g/clojure/c/WI3ddZRK4Bg/m/RtJVt3IZBwAJ https://groups.google.com/g/clojure/c/WI3ddZRK4Bg/m/RtJVt3IZ... https://github.com/clojure/tools.deps.alpha/commit/c22ad46c6cf913448ea33d40434e4e8dfb3ef2b3 https://github.com/clojure/tools.deps.alpha/commit/c22ad46c6...
- divs1210 5y agoI have never stressed over or debugged Leiningen in my 6 years of Clojure experience. Not sure what you're on about.
- divs1210 5y agoSame. I don't want to spend time learning build tools and configuring builds - I want to Get Shit Done. 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.
- tut-urut-utut 5y agoExactly. Lein is like maven, declarative and just works in a standard way for 95% of use cases. Deps is like ant, can do anything, super flexible, but you need to start from scratch actually programming your build for every project.
- fnordsensei 5y agoI first used deps when doing a project with Datomic Cloud. I like deps, but I can’t quite articulate why.
- filoeleven 5y agoAfter using it with Datomic Cloud, do you prefer it also for local projects? Is there something that happened with Datomic Cloud configuration that made it “click” for you elsewhere? Did you just have to use it there and that experience informed your preferences? Data > functions > macros is a core concept of Clojure. Does that come into play here? These are prompts from a curious outsider’s perspective.
- john-shaffer 5y agoI found an old thread that examines some of the early usage of deps. Data > macros does indeed seem to be a driving force: https://clojureverse.org/t/combining-tools-deps-with-leiningen/658 https://clojureverse.org/t/combining-tools-deps-with-leining... tools.deps makes better choices when dependency specs conflict, so I would suggest trying https://github.com/RickMoynihan/lein-tools-deps 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.
- filoeleven 5y agoLeinengen vs Maven dependency resolution is pretty distant to my concerns. I may be in the minority there, but I don’t think I am. I guess the thing I’m struggling to understand in the build tool differences is: 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 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.
- bokchoi 5y agoIt's like we learned nothing from years of suffering with snowflake Ant builds. Then again, gradle builds seems to be more popular than maven these days.
- pjmlp 5y agoThat is what happens when a new generation has grown without Ant and then a giant behemoth with endless cash for top hardware gets a deal to push Gradle as the official build tool for their mobile OS.
- vore 5y agoI would think that Gradle is a lot more pleasant to use than Ant...
- pjmlp 5y agoNot for those that don't suffer from XML allergy, so given that, the outcome is almost the same, with the caveat that Gradles uses a ton of more resources than Ant, as it needs its background daemon sucking up 2GB to pretend being fast.
- bm3719 5y agoHard agree here. As a full time Clojure dev, my experience is that lein "just works". I've also used the Clojure Tools professionally, and have seen the dev team lose countless days of what would be otherwise productive time putzing with it, building infrastructure that lein has had out-of-the-box for years, and trying to make sense out of its inscrutable docs. 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.
- AtticHacker 5y agoI agree. To me tools.build is not a replacement for Lein at all. Lein is more intuitive to most beginners. It's much easier to tell someone to run `lein new some-app` and `lein run` / `lein uberjar`, than to tell them to configure their deps.edn file so they can run some alias with the `-X:...` / `-M` arg to create and run a new project (and who knows which dependency to use / configure to build an uberjar). 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.
- dustingetz 5y ago> RICH HICKEY: I think that, collectively, we are infatuated with these two notions of easy. We are just so self-involved in these two aspects; it's hurting us tremendously. Right? All we care about is, can I get this instantly and start running it in five seconds? It could be this giant hairball that you got, but all you care is, can you get it. 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/Hickey_Rich/SimpleMadeEasy.md 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!)
- dustingetz 5y agoLeiningen is over 10 years old and has tons of pain points. For example, I bet you're not using ClojureScript, which is a mess of plugin debugging in leiningen. The goal of tools.build is to let you code your builds as simple functions that run at the REPL, which is awesome. No more finding misplaced keys in soup like https://github.com/technomancy/leiningen/blob/master/sample.project.clj https://github.com/technomancy/leiningen/blob/master/sample.... . If leiningen works for you, great, keep using it!
- bm3719 5y agoWhat pain points are those? Last I checked, doing cljs on Clojure Tools wasn't even possible. If that's still the case, then that's not a valid comparison (complex vs impossible). 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.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).
- filoeleven 5y agoTo be clear, you are pointing to this as an equally contrived example as what ‘dustingetz linked to, correct?
- bm3719 5y agoCorrect.
- seancorfield 5y agoThat isn't a project deps.edn file though -- it's a compendium of tools that are available for the CLI/deps.edn and I put it together as a working example so folks coming to the CLI/deps.edn stuff could see the broad range of tools available and could copy'n'paste any useful bits they want. 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.
- mapgrep 5y agoYa, this accords with my thoughts when I first saw this bubble up, I don't understand the benefit over just using lein, which is very easy and straightforward (for my use case). I thought maybe this new approach is needed by people with complicated builds? My own builds have been quite simple so far, but I'm new to Clojure.
- divs1210 5y agoI have worked on pretty large Clojure projects, and found Leiningen to be perfectly adequate.
- cutler 5y agoAlthough I prefer Lein at this point one of its disadvantages not mentioned here is that it requires its own JVM process which means you're running 2 JVMs during development.
- jgalt212 5y agobut probably good and bad. pretty tough for one namespace to bleed into another due to a bug or incorrect config. bad, obv, because all the RAM the JVM needs.
- fmakunbound 5y agoI don’t understand why deps was needed and I’m surprised its authors didn’t foresee the confusion it would create. I’m also confused as hell what I should be using now, and the idea of researching deps seems exhausting. I’ll just stick to cutting and pasting onto my leiningen config I think.
- capableweb 5y ago> I don’t understand why deps was needed First, start here: https://clojure.org/about/rationale https://clojure.org/about/rationale Then Deps and CLI is explained here: https://clojure.org/reference/deps_and_cli 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.
- fmakunbound 5y agoIt’s still alpha, and from what I can tell from the links referenced, a severe case of NIH. Hopefully it will be “made simple” and “de-complected” soon though so that I can tell what the point of it is in the presence of Leiningen.
- raspasov 5y agoVery well written post. Simplicity is a good thing!
- thom 5y agoGiven the general tone of comments here, I thought it worth pointing out the advantages of using deps.edn instead of a Leiningen project: - REPLs start up very slightly quicker
- capableweb 5y agoI don't understand why people keep complaining about slow REPL startup time. How many times do you really restart your REPL? In my day job, I usually start up the REPL once and then close it when I'm done during the day, or just fold up the laptop which suspends the REPL and then it's ready to go when I unsuspend. 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?
- synthc 5y agoI also go without restaring the repl for weeks, but there are plenty of situations where you need to restart the repl: adding libraries, servers binding to ports, making sure you have no temporary edits, etc, etc. 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.
- capableweb 5y ago> I also go without restaring the repl for weeks, but there are plenty of situations where you need to restart the repl: adding libraries, servers binding to ports, making sure you have no temporary edits, etc, etc. 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.
- bananaoomarang 5y agoI actually have found tools.deps to be pleasurable to use on new projects, though I haven't tried porting anything that previously used leiningen directly. It's simple, it's clear, I guess I find it pretty easy to reason about, but maybe that's because my experience with Lein was more 'copy and paste this huge config' and not really starting from scratch.
- fungiblecog 5y agoWhat all the critics miss is that Lein is "Easy" but tools.deps, tools.build are "Simple". Rich wants all of clojure to be built from simple orthogonal parts that compose together. Ideally a multi-purpose tool like leiningen should be built on top of those simple parts. Other tools can re-use those parts in different ways. With Leiningen - its a great tool but it's a complex thing that you take on an all-or-nothing basis.
- deleted 5y ago[deleted]
- roenxi 5y agoTheory is all very well, but adding a library to a project is a critical requirement for all projects. A new starter comes over to Clojure from Python and the Deps guide [0] confidently explains too them that they add {: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_using_libraries https://clojure.org/guides/deps_and_cli#_running_a_repl_and_... [1] https://clojure.org/reference/deps_and_cli https://clojure.org/reference/deps_and_cli
- cutler 5y agoClojure aficionados love to obesess over this stuff or maybe it's a leftover from their Java days. You come away from Rich Hickey's sermons on the Mount all fired-up with the value of simplicity and before you know it you're knee deep in Clojure's build tools maze - lein, boot, deps.edn, clij, shadow-cljs, tools.deps, tools.build. Clojure blazed the trail for simplicity but as far as build tools are concerned only Go managed to pull it off.
- geokon 5y agoIt's nice to have complex build/test setups with deep directory structures, I'm really happy this stuff exists and is being enhanced, but the flexibility kinda adds a barrier to getting started. It's also important to be able to just drop into a REPL, load some libraries and go. I think the coolest thing in the pipeline is going to be `add-libs`: https://insideclojure.org/2018/05/04/add-lib/ 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 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
- capableweb 5y ago> I'm really happy this stuff exists and is being enhanced, but the flexibility kinda adds a barrier to getting started 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.
- geokon 5y agoThe tools pay off for sure but even once you know how to use deps.edn and company, I'd argue you still really need quick one-file programs. 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.
- billfruit 5y agoThis is good for clojure, I think. There was a time when lein seemed the preferred tool for clojure projects, but lein seemed to be doing too many different things, and more significantly was not packaged woth clojure. So during that time clojure felt batteries-not-included. With in built tooling, getting into and using clojure should be more easier.
- brundolf 5y agoGlad they're finally making this stuff first-party. When I messed around and did a Clojure project, the build/dependencies story was the biggest sour note of the experience by far. It was bewildering to have to go set up some third-party tool with a mustache logo before I could follow along with the official tutorial. I think having a cohesive story for this is table-stakes in a modern programming language.
- capableweb 5y agoFor as long as I can remember, the instructions from the official docs leads you into a REPL where you can start learning. Not sure what this "official tutorial" you are talking about, but as long as you get a REPL running, you should be able to get started.
- brundolf 5y agoI couldn't start writing and compiling my own source files until I went through all these unofficial channels to get stuff set up. I might be misremembering about the actual tutorial itself, but step 2 (do your own project) was literally impossible without the detour. 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.
- capableweb 5y agoI'm not sure how they can make it easier than what it currently is, no need for any unofficial channels. If you follow the guide at https://clojure.org/guides/getting_started https://clojure.org/guides/getting_started to install Clojure, you'll end up with `clj` in your `$PATH`, and then it is as easy as it is with Python, unless I misunderstand what you mean. $ clj --version Clojure CLI version 1.10.3.849 $ echo "(println \"hello world\")" >> hello-world.clj $ clj hello-world.clj hello world
- mark_l_watson 5y agoI like Clojure but it is not my primary development language so I am happy to stick with lein. As others here have said, lein is easy to use and gets the job done of creating new projects from a variety of templates, building, and deploying.
- didibus 5y agoI asked some questions on slack related to this, and in case anyone else was confused like me thinking tools.build is a new framework for managing build tasks, that does not seem the be intent. 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}
- uDontKnowMe 5y agoThank you fogus and the team for your work on these tools!