Closed grammati closed 7 years ago
good observation, but nothing really Heisenberg'y :)
defstate
creates a managed var which also is aDerefableState
that extends clojure.lang.IDeref
protocol (or IDeref
in cljs).
A Clojure var is also "derefable":
boot.user=> (def a 42)
#'boot.user/a
boot.user=> @#'boot.user/a
42
In other words when you "evaluate" blah
, it gets deref'ed, hence calls a deref
function on it. When it happens, it gets started, since mount states are all DerefableState
s.
=> (require '[mount.core :refer [defstate]])
=> (defn- start-fn []
(println "Launching missiles!")
(+ 40 2))
=> (defstate blah :start (start-fn))
=> @blah
Launching missiles!
#object[mount.core.NotStartedState 0x14116aaf "'#'boot.user/blah' is not started (to start all the states call mount/start)"]
=> blah
42
notice how blah
is manually deref'ed, and you get the same behavior.
This has to do with two things:
an ability to not rely on var mutation every time state changes [but rather store the state behind @
, like an atom does]
as well as supporting ClojureScript, since most useful namespace API in ClojureScript do not work due to the hosting language as well as an :advanced
compilation mode where everything gets renamed and "repackaged"
As to not calling (mount/start)
and still see states starting the first time they are deref'ed, sometimes proves to be quite useful, in cljc
mode, since it enables a lazy application start.
Managing state in ClojureScript should give you more details about the cljc
mode, lazy starts and the overall rationale.
Thanks for the detailed answer.
However, I'm afraid I don't really understand.
I get that a Var implements IDeref, and that just evaluating it causes its deref method to be called. But if defstate
is essentially just def
-ing a var that contains an instance of DerefableState, I would expect that evaluating that var would just return the DerefableState instance. But somehow, it seems that the deref method of the DerefableState being called. That is, there are two derefs happening implicitly - of the Var and then one of the DerefableState inside it. Could you explain how this is possible?
Thanks!
Sorry, nevermind, I figured it out. It's exactly what I thought at first - as side-effect of printing, the DerefableState instance gets started.
ah.. yes, I see what you mean now. I thought you were talking about (println "Launching missiles!")
within a :start
function, but you mean REP
L eval on P
.
I'll add a couple of comments to the pull request you sent.
Things defined with
defstate
start themselves, I think due to some sort of side-effect of printing.Example:
Given this file:
I started a repl (nrepl, in emacs, via leiningen), evaluated the namespace, then simply evaluated
blah
twice.Note that I never called
mount.core/start
, butblah
is now started.So a defstate seems to create some sort of Heisenvar, such that simply looking at it changes its value.