Closed jocrau closed 8 years ago
Thanks for bringing it up.
(mount/in-cljc-mode)
sets the internal mount atom that is not used until (mount/start)
is called. Can you share why you think just placing it before (mount/start)
does not work for you?
I suspect the problem is somewhere else.
I dug a little bit deeper. The exception I got was caused by the fact that I dereferenced a state within a def
, which is - of course - evaluated at compile time. Then a NotStartedState is bound to the Var. The next time I reload this namespace with the def
, I get a
CompilerException java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to java.util.concurrent.Future
Putting a (mount/in-cljc-mode)
before the state is dereferenced in def
avoids the exception.
Check out https://github.com/jocrau/mount/tree/issues/55 for an example how to invoke the exception.
thanks for the example. in the future it probably would be easier to play with these instead the ones that baked into mount dev, but I understand what I you see.
Keeping states in def
s "by yourself" is what mount tries to avoid :) If you need a state keep it in defstate
, since it would plug into the mount lifecycle.
What would be the reason you need (def foo @config)
?
Using def
was definitely the root cause for the issue. It would be great if mount could issue a proper warning if a state is used that way. The example
namespace as consumer of config
shouldn't be aware of the fact that mount is doing some voodoo in the background ;-). It should consume config
as if it is defined as (def config (atom {...}))
in the conf
namespace.
It is interesting that it actually works if I put (mount/in-cljc-mode)
before def
.
I think you can close this issue. Thanks!
When you do (def foo @xyz)
, xyz
should be defined, since it is being deref'ed. If it is not defined, it throws. I think it is an expected Clojure behavior with or without mount.
With mount, in case xyz
is a state var, I would suggest not to use it in def
, in the same way you would not do (def conn @db-connection)
before creating a db-connection
.
In this case mount does not know config
is used in def
, in fact mount does not watch any usages, it just creates things and adds a way to restart them.
Let me know if this makes sense, or you have other questions.
That makes sense to me, thanks. And (def conn @db-connection)
is a good example of how not to architect a system ;-).
And I also follow your argument that mount
should not behave differently from "pure" Clojure. But it would be totally fine (for a non mount
user) to define an atom config
in a configuration namespace with some "hard-coded" default settings. That atom could be dereferenced at any time. If I would replace this atom definition with defstate
I would get the Exception above in (mount/in-cljc-mode)
.
What I would wish for is, that defstate
puts something like (atom nil)
in place until the system is started. It might be nit-picking, but when you say "If it is not defined, it throws", I think of "not defined" being
(def config (atom nil))
rather than
(def config nil)
Does that makes sense to you? (and feel free to close this issue if you have better things to do ;-) )
in (mount/in-cljc-mode)
mount's defstate
is kind of like an atom: i.e. it is a DerefableState
that extends Clojure/Script's IDeref
.
In fact, if you use mount in cljc
mode, in case you'd like to start your system lazily, you don't have to call (mount/start)
, since every time a state is deferenced, it would start and return its value.
In case mount is used in a default clj
mode, a defstate
would create a NotStartedState
before (mount/start)
is called. This way, whenever you try to access this state without starting it previously, you'll get an exception that NotStartedState
can't be "used/cast/etc.." to whatever the type you expect.
So there is really no (def config nil)
use case, unless you create it yourself :)
I understand. Thanks again for the explanation! I will close this (non-)issue now. :-)
The CLJS documentation mentions that
(mount/in-cljc-mode)
has to be invoked "anywhere before a call to (mount/start), usually at the entry point of an app: in the -main, web handler, etc.".This was not sufficient for me, as the namespaces that are required in the
system
namespace containing the-main
method are evaluated before-main
is invoked. The solution was to add(mount/in-cljc-mode)
(right after after(ns ...)
) in each namespace that contains(defstate ...)
.Maybe there is even a cleaner solution to the mode-switching.
Thanks for "mount"!