Clojure Mount

I am a newbie to Clojure. I am finding a hard time to understand when and why mount lib is required. I have seen many places where mount/component lib is used.

Thank you.

Do you mean GitHub - tolitius/mount: managing Clojure and ClojureScript app state since (reset) ?
If so, you might want to consider component GitHub - stuartsierra/component: Managed lifecycle of stateful objects in Clojure instead (to begin with) as it is much easier to understand and work with. For a use case, take a look at Sean Corfield’s UserManager example at GitHub - seancorfield/usermanager-example: A little demo web app in Clojure, using Component, Ring, Compojure, Selmer (and a database)

As to when / why … component (and mount, and integrant) are small libraries that provide a way to manage global state in a clean way within a functional language with immutable data structures. Component provides lifecycle methods (e.g., start/stop) to manage the stateful parts of your program. So, once your program is big enough to need global state (e.g., web server, database connection) you should consider using Component. What Component facilitates is separating the stateful parts and allowing you to control those independently of the stateless (e.g. pure functions) part. Please take a look at the github repo for Component for more details on the usage of the pros/cons. From that page, there is also a link to a YouTube video that may be of some help.

4 Likes

Another point of view (a complementary one) is that if you do not see the need for a thing, then simply do not use that thing.

If you are accustomed to people counseling you to “always start with the Spring Framework” then this might be a new experience!

Don’t reinvent the wheel, of course. The point is, it’s not very hard to retrofit a program later, when you decide to devote more attention to aspects where this-or-that library is useful.

1 Like

I’m going to add a bit of a cautionary tale here…

When we got started with Clojure at work – about a decade ago – we didn’t know about things like Mount or Component (they may not even have existed back then?) so we took the “easy” approach with things like database connections and cache setup and various other things that needed some “state” set up as our programs were initialized: we just put them in global variables! Sometimes we used a def with a computed value right there inline, sometimes we used a delay so it was “initialized” on first use, sometimes we used an atom and computed the value explicitly at program startup and then reset! it to that value. It all worked. We just referenced those top-level Vars as needed in other namespaces and we continued building.

Over time, we had more programs that all needed some combination of these global resources and we started to write code to manage their initialization in more comprehensive ways, but every new app we built had to remember to call that initialization code in the right order: we had some code that needed both the database stuff and the caching stuff, then we moved all the database connection metadata to config files, now the database connection initialization depended on the environment configuration initialization. And we fairly quickly had a mess of dependencies that all needed to be initialized in a certain order and passed into each other in certain ways. Plus we had a lot of code that all depended on these globals.

We also had all of this setup in our test code, sometimes with different pieces needing different values – and with all those globals, it was often much harder to test code because you couldn’t just mock a resource.

We needed a better approach.

This was exactly what Stuart Sierra’s Component library was designed for: you declare a “lifecycle” for each resource – a pair of start / stop functions – and you declare the dependencies each resource needs prior to startup. Component figures out the right order to initialize everything and how to build your “system” from all these inter-dependent pieces. And then, instead of global vars, you pass the system (or a subcomponent of it) into each function that needs it, starting right at the top of the call chain in your -main function.

Component is simple, and if you use it from day one it is also easy. Your tests can very easily swap out real resources for mock resources as needed, and can also very easily build multiple systems so you can test interactions (this has been very important for us as it makes testing cluster synchronization code so much easier when we can run multiple “systems” in a single JVM for testing!).

We build all new code this way and we’re very happy with Component.

The cautionary tale? We had a pretty large codebase before we introduced Component and we had a lot of code that depended on those global Vars! We introduced Component several years ago and we still have a few pieces of our system that rely on globals instead of a (sub)component being passed through the call chain. Not adopting Component early on has been very, very painful. If I could go back in time and give one piece of advice to my younger Clojurian self, it would be “Use Component! Now!” and I could have avoided so much pain as our applications grew.

Dorab gave an excellent answer about what your options are and why you might use them but Phil is also right insofar as you won’t feel you need them when you get started and they could be just another obstacle to learning Clojure – but my advice, from a decade of production Clojure, is: use Component as soon as you build your first production app. Use Component for your database connection pools (next.jdbc supports this out of the box!), use Component to start (and stop) your web server, use it for anything that has an initialization that depends on anything else. It will make it easier to add new resources to your app, it will make it easier to test your app.

As for Mount, some people love it (I don’t – see this thread about DI in Clojure). Some people love Integrant for this (I don’t – see that same thread!). That’s a good thread to read as a whole for more background on dependency injection (which is really what this whole Component/Mount/Integrant thing is about). There’s also this short thread about Mount and Component – plus several other threads here where either, both, and/or Integrant are discussed so it’s worth using the search feature at the top of the page to see what other discussions have happened here about the pros and cons of each.

7 Likes

+1 to what Sean said. From approximately the same experience. Retrofitting will be harder.

That being said, if you’re just playing around and learning, it’s okay to use unmanaged global state. But once your program has more than a few (2? 3?) globals, you’ll be better served learning and using Component to manage your global state. Also, as Sean says, if you have a production app that uses global state, I’d also strongly recommend using Component from the beginning.

Another, somewhat related (see the DI discussion Sean mentions), recommendation is to pass in the appropriate Component as an explicit parameter to functions that operate on that state. That will make it easier to inject test versions of the Component during test. See the db functions defined in usermanager-example/user_manager.clj at develop · seancorfield/usermanager-example · GitHub in Sean’s UserManger repo for an example.

Did Component play a role in redesigning your functions to have their dependencies injected?

As I said in other threads about this, my Clojure code bases just use top level defs with delay. We have environment based configuration for configuring the initialization based on environment: test, dev, beta, gamma, prod, different regions, etc. which the delay code leverages to configure the initialization. And the delays depend on each other, so they naturally handle initializing things in the right order since they’re pull based. They also prevent initialization from triggering at compile time and on loading or reloading a namespace.

But, we’ve always injected them into our code from the start, passing them through the call chain.

Probably using Component instead of what I do wouldn’t be that much more complicated at this point and would allow tests to run in parallel, I honestly just never tried it, because this worked well for us and we never had reasons to look for something else.

Something else that hasn’t been mentioned and might be a reason to upgrade to Component is being able to use tools.namespace to live reload the whole app and restart all dependencies as well, shutting them down in the right order, cleaning their resource and re-initializing them. Cause you can do that with delays, but it won’t shutdown things, so for example if you have a web server bound to a port, and you refresh the delay it’ll create a new web-server bound to the same port which could throw saying port already bound, which is where Component being able to shutdown things when you reload can be useful.

1 Like

Yes, but only insofar as we were switching from implicit dependencies (on global vars) to explicit dependencies (on that data being passed in).

The main redesign that came with Component was considering “startup” and “shutdown” explicitly around the resources we work with – which makes them much more friendly for REPL-based work – and also being explicit about the actual dependencies, expressed as data structures, so we named each resource directly and listed what each resource depended on.

The really nice aspect of Component, for us, is that you can simply declare a new dependency (by adding to the data structure), and Component takes care of adding that dependency in and managing its initialization in the correct order and it will be passed through your existing call chain as part of the “system” – and also when you are working with the REPL, all the pieces are initialized in the right order and also shutdown in the right order (the reverse order to startup) so that you can spin systems up and down in the REPL.

2 Likes

@didibus Have you encountered redelay? I haven’t used it, but it looks like it might fit your use case. Redelay state acts like derefable delays, but also supports stopping (in reverse init order) and resetting.

1 Like

That’s awesome, delay with a stop, pretty simple. Ya that would cover the edge case I brought up, thanks for bringing it up.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.