Closed introom closed 4 years ago
check out hubble, it has an example of config as a defstate
.
the idea is: Clojure compiler will compile it first and then other states may just take it in:
(:require [hubble.env :as env])
;; ...
(defstate http-server
:start (start-www (env/config :hubble))
:stop ((:stop-server http-server)))
i.e. since Clojure compiler sees that config is used in http server, it will compile the hubble.env
namespace first.
This approach is also how luminus uses to pass around config.
However, one severe defect is that, we cannot modularize the namespace since it explicitly imports that specific config.
This adds complexity to reasoning, reusability, and testing.
We need some way of passing the config as parameters to that namespace to achieve inversion of control.
can you provide a concrete example of what it is you are trying to do?
inversion of control
is definitely not something mount tries to achieve since it is not a framework. "frameworks" are rarely needed in Clojure due to the fact that the focus is on using functions to move data through time rather than hierarchy of nouns working on the same piece of state.
adds complexity
needs examples, and perhaps things could be done/designed in a different way to remove the anticipated complexity.
can you provide a concrete example of what it is you are trying to do?
I have two namespaces:
(ns app.config)
(defstate config ,,,)
(ns app.server)
(defstate server ,,,
which internally uses app.config)
My problem is,
How to let app.server/server
use the config from ns app.config
, without explicitly (require '(app.config)).
The problem of explicitly requring ns app.config
is that doing so will closely couple (ns app.server) with (ns app.config).
One plausible way is to pass app.config/config as argument to app.server/server. But how to achieve that?
why not have a server
depend on a config
that it always needs in order to start?
you can always swap this config with a test config or any other data structure via (mount/start-with ...).
here is a more complete example on how to do it.
Yes. I am aware of such pattern.
So, IIUC, the ideology when using mount is: A module requires defstate of another module by explicit requiring the namespace. During testing, we swap the definition of that defstate with (mout/start-with).
What I am wondering is: Is it possible that, A module uses the state of another module by argument passing instead of the above explicit importing of entities from another clojure namespace.
A module uses the state of another module by argument passing instead of the above explicit importing of entities from another clojure namespace.
in order for a state to take arguments, something needs to call this state passing the arguments. mount does not really call states, a Clojure compiler does. there is a way to pass runtime arguments, but in case of an application configuration, I believe it is best to "pass" it in as another state. in tests you could start the application with a test configuration, hence mount/start-with
.
I would still ask a question:
why not have a server
depend on a config
that it always needs in order to start?
I can close the issue for now as I've understood mount's philosophy of globally swappable state .
sure, feel free to reopen in case you find the answer to the last question above
The answer is apparent as global variable (either immutable or not) vs local variable by parameter passing.
https://clojurians-log.clojureverse.org/clojure/2020-02-12/1581487563.428900
ah, k. I thought you had a concrete reason why this won't work for you, and I can help you. I didn't realize this is rather based on just a "this is bad" opinion.
now as I understand where you are coming from the issue may really be closed.
How do you use configuration as a =defstate=.
The obstacle is how to pass the config state as arguments to other states in the system?
If we use =mount/start-with-args=, where the config being the args, the problem is, at the time of =mount/start-with-args= being called, the config state has not been started.