just wondering: defstate
is something like a global 'constant'(?) atom? If so, well, I always have a rather weird feeling with globals. So the use case for mount, is it about managing 'system state' rather than the state of a namespace?
I'm trying to find out the main difference between mount and component. I did check https://github.com/tolitius/mount/blob/master/doc/differences-from-component.md#differences-from-component already. The only thing that actually bothers me about mount is that state is a global 'constant'(?).
yes, it's a global constant
the same way a System of components is a global constant if you store it in a Var
(in whatever sense we use the word "constant" here)
@dhruv1: is it still a problem, or you're just wondering why it happens?
itβs no longer a problem.
and iβve figured out why it happens. mount has an atom internally which keeps track of the state and thatβs where i was running into the problem
i was wondering if there was a way to avoid this situation. it took me a bit of time to figure out why I kept getting the error.
@kurt-yagram: I would not call it a constant
necessarily since the state does change: does not remain to be "constant" between start
and stop
. the key here is that it is managed, i.e. it is not just a global singleton, as some people claim: but a managed state.
> The only thing that actually bothers me about mount is that state is a global
what exactly does bother you about it? the idea is that you'd make a state for an external resource, for example a database. In case you need a state (connection) to another database / different schema, you have plenty of options: https://github.com/tolitius/mount/blob/master/README.md#swapping-alternate-implementations or you can just create a different state.
it reflects the reality if you think about it: database is your resource, and it is "single", you need connections to two different databases? it would be two different states (could be the exact same function to create these states, given the different configs, but two different instances with connections to two different databases).
@tolitius : yeah, it bothers me because of bad experiences with it, probably. It always bothered me and I don't really know why. Maybe I should just get over it. now, I might have to use mount a little different, but what I do know is: I read my config from, well, that doesn't matter, but I have like 1 configuration map, containing the config for different parts of may application. So I do:
(mount/start-with-args (::datomic config)
#'my.ns.db/conn)
(mount/start-with-args (::security config)
#'my.ns.security/config)
...
In my.ns.db
:
(defstate conn
:start (new-connection (mount/args))
:stop ...)
Now, I would prefer to use just (mount/start)
, or something like in the docs:
(-> (only #{#'foo/a
#'foo/b
#'foo/c
#'bar/d
#'baz/e})
(with-args {:a 42})
(except [#'foo/c
#'bar/d])
(swap-states {#'foo/a #'test/a})
(swap {#'baz/e {:datomic {:uri "datomic:<mem://composable-mount>"}}})
mount/start)
However, swapping my.ns.db/conn
is a connection and can't be started/swapped with the map. So I would actually have to call new-connection
where I call mount
.
I just can't figure out what's the best thing to do here: calling a bunch of functions to configure the state (like new-connection
) in the main
of the application, or passing the general config to all parts as mount/args
? (In the latter, I pass actually way to much configuration).
It's just: if I do the former, I get a cyclic dependency (unless I put methods as new-connection
not inside the my.ns.db
namespace?)@dhruv1: usually you would do something like (reset)
https://github.com/tolitius/mount/blob/master/README.md#the-importance-of-being-reloadable but I agree, it might make sense to potentially have a some kind of (mount/reset)
that could take params: i.e. (mount/reset :ignore-errors true)
. which, if well documented, could help in situations like this one.
could you create an issue, so we don't forget?
@tolitius sounds good. thanks π
created. thanks @tolitius
@dhruv1: thank you!
@kurt-yagram: trying to piece the problem together... one sec
@kurt-yagram: not understanding this piece:
swapping "my.ns.db/conn" is a connection and can't be started/swapped with the map
do you need to create a different connection depending on the arguments? i.e.
(defstate conn
:start (new-connection (config :store))
:stop ...)
where (:config store)
may return different (say database schemas)?
i.e. -Dconf=prod-config.edn
vs. -Dconf=dev-config.edn
?
yeah, although not with -D
parameters as such. The main struggle I have is: the way I start the application now is a bunch of mount/start-with-args
.
Now, I'm starting to realize that there's probably no way around this. I can use mount/start-with
, and put create-...
and new-...
methods somewhere in the 'main' namespace. I don't really have a default state, so I always need to add the state during start.
Or, taking the sms example ( https://github.com/tolitius/mount/blob/master/README.md#swapping-states-with-values ): create-sms-sender
is inside the app.sms
ns, so I can't call it from the ns where it is mounted. I can't do mount/start-with {#'app.sms/send-sms (app.sms/create-sms-sender (:sms config))}
.
> the way I start the application now is a bunch of mount/start-with-args
why not via a single config?
> I can't do mount/start-with {#'app.sms/send-sms (app.sms/create-sms-sender (:sms config))}
what I don't understand is why you need to do it π i.e. why not just https://github.com/tolitius/stater/blob/master/smsio/src/app/sms.clj#L12-L13 where (:sms config)
would be different depending on the config file / profile / etc.. ?
Yeah, well, I actually just didn't want pass all configuration to each part, but only the component-specific configuration. Anyway, maybe I shouldn't make things so complicated π.
using (:whatever config)
should do fine...
@kurt-yagram: yea, I usually find as the app grows in size and gets more components, a config is a very convenient way to provide runtime dependent values. also, since config could be edn
it is not that difficult to destructure, or you can just use cursors: https://github.com/tolitius/cprop#cursors π
cprops
... I like