tolitius / mount

managing Clojure and ClojureScript app state since (reset)
Eclipse Public License 1.0
1.23k stars 89 forks source link

changing :start-with to take a "config" map #47

Closed tolitius closed 7 years ago

tolitius commented 8 years ago

While it's good to have separate APIs to swap / filter states needed. It is limiting if they don't compose.

We started discussing it here with @aroemers, and this was one of the primary reasons for a mount-lite spinoff.

I really want to preserve the varargness of (mount/start) / (mount/stop) since it is really simple and useful in REPL, hence I don't want to overload them. But I feel (mount/start-with) can be a great candidate for composing the mount configuration. e.g.

(mount/start-with {:only #{}                   
                   :swap {}
                   :swap-states {}
                   :except #{}
                   :args {}
                   :with-deps {}})

This is the current approach that we discussed with @wkf on #mount slack channel, and it does break the existing (mount/start-with), but I feel the timing and the nature of its use still permits it.

tolitius commented 8 years ago

not to solve two things at once and not to overload start-with, would make sense to first start with just functions that could simply be composed:

(-> (only 1 2 3 4)
    (with-args {})
    (except 2 1)
    (swap-states {4 #'foo.bar/5})
    (swap {2 42 1 34})
    (mount/start))

this way the current start-with won't change, neither would (mount/start) since it optionally takes states since day one.

aroemers commented 8 years ago

Hi @tolitius, me again. I have given it some more thought as well, and I could not resist to write to you. Keep in mind that I love mount.

Here's the thing. I really urge you to reconsider going for a data-driven approach for the composability you want to add (as you show in the opening post of this issue). I feel the gains are superseding the possible negatives. A data-driven approach decomplects, something the users might like. It decomplects forming the start/stop options and performing the start/stop options. Also functions like except now uses the set of available states (find-all-state) when except is called, and that set may have changed already when start or stop is called. You say that users will thread start or stop immediatly after, and that may be true. I do that myself as well. But with a data-driven approach, it does give the possibility of separating creating the options and performing start/stop, and is also less suprising to the user.

But don't do it for your users per se, the gain is also for yourself, as the library developer. It makes it easier to change the internals of the composable functions (because hey, you are only transforming data you fully control) and it makes it easier to add more options later on. Take for instance the parallel option that mount-lite offers. I know you are not interested in doing that, but let's just take it as an example. In your current design, the parallel function would take a set of states (or call find-all-state), and return the same set of states. This is because parallel does not have to do anything with the set of states; it just specifies the number of threads you want to use. How are you going to make sure such an option reaches start or stop? With with-meta on the set of states? That would be kind of dangerous, as all the other transformations must make sure that meta data is kept. Or add another function called start-parallel? I think you'd rather not as I know you want to keep the API minimal. With a data-driven approach, adding such an option is no problem. This also benefits the API stability which, again, I think the users will appreciate.

Alright, so much for the background of the main point I think I want to make. What I want to say is, I believe that going for the data-driven approach now, instead of going for two APIs (one with functions that compose and call start, and one with data that calls start-with), is beneficial because it will provide a single API with less suprises, gives you more flexibility and may prevent an API that "needs" changes in the future when users ask for some feature. Many gains, with the loss that the semantics of start-with has to be changed.

I hope you don't mind me rambling on over this. If you do, please say so as well.

tolitius commented 8 years ago

@aroemers

I hope you don't mind me rambling on over this.

I love feedback. I never mind it.

I believe that going for the data-driven approach now, instead of going for two APIs (one with functions that compose and call start, and one with data that calls start-with), is beneficial.

I agree with you in theory :) And I have plans to add data driven start config which maybe useful in places like lein profiles, but I also like having these building functions. It comes down to declaring vs. programming. I don't want to take the programming out of building states. Having this functions help tearing application states apart and composing them back in different order, shape or form. Data allows do the same thing, but these functions are a lot more primitive, hence to understand and reason about them is a lot simpler than about a config DSL.

Your main objection is that they do stuff, rather than declare what to do. And in REPL I mostly want to do stuff. mount has two different goals: manage application state and help development with stateful resources. I like certain side effects in development that allow me to "touch" the code. I like breaking things, and making new ones.

"Starting mount" is not something you, as an application developer, base your APIs on. mount, once started, is not really used that much in production, unless you need to hotswap / partially start stop things via nREPL, etc. which is quite rare. That's why I'd like to have as much flexibility as I want in development with mainly designing states so the focus of production applications in just on (mount/start).

aroemers commented 8 years ago

Hi @tolitius, sorry for not getting back to you sooner. I understand your point of view, thank you for explaining. I think mount users are happy with the composability you have given them. As far ar I am concerned, this "issue" can be closed.