Fork me on GitHub
#boot
<
2016-09-19
>
mikethompson02:09:12

(dispatch-sync [:init]) is indeed effectful. It sets the initial state of the application. Ie. it causes an initial value to be put into the one big atom app-db

mikethompson02:09:42

I'm not sure of the context here (dunno much about boot yet) but thought I'd chime in on that part

micha02:09:58

hi @mikethompson

micha02:09:17

the issue was an interesting one

micha02:09:46

one one hand you need to initialize the application in the main napespace when the application is first loaded

mikethompson02:09:53

I saw re-frame mentioned and thought "i can help here" but then ran into my lack of boot knowledge.

micha02:09:10

but you also want to re-initialize it when namespaces are reloaded on the fly

micha02:09:34

the undesired behavior occurs when you reload the main namespace

mikethompson02:09:39

So this is a figwheel-like situation ?

micha02:09:41

then the hook runs twice

micha02:09:21

the solution i proposed was something like this, assuming a function init that does the initialization

micha02:09:08

(let [loaded? (atom false)]
  (defn on-js-load []
    (if @loaded?
      (init)
      (reset! loaded? true))))

micha02:09:29

and call that function when doing the hot reload

micha02:09:47

you'd define the on-js-load fn in the main namespace of course

micha02:09:07

so that atom is reset to false whenever the main namespace is recompiled and reloaded

mikethompson02:09:28

Or the other way might be ... the handler for the event [:init] could check to if app-db already has a value and not give a new value if it does

micha02:09:48

ah that sounds like a better solution

micha02:09:09

actually i am out of my depth there

micha02:09:32

maybe it's desirable to re-initialize when code is reloaded?

mikethompson02:09:56

Most of the time you want the app state retained

mikethompson02:09:34

You want new code loaded but you want the state of the app to be retained

micha02:09:57

so a delay would work then, or defonce, i suppose?

mikethompson02:09:05

@michael.heuberger can work it as he sees fit. I'll give a code fragment ...

mikethompson02:09:25

It seems to me you want to make the handler do nothing when it is called the 2nd, 3rd, etc time. It should do a correct initialisation the first time. But every time thereafter, it should be a noop

mikethompson02:09:39

If that's correct, so something like this ...

mikethompson02:09:13

(def-event-db 
   :init
   (fn  [db _]
      (if  (= db {})        ;;    initially db will be {}
          (......    return the initial value to be put in app-db)
          db)))         ;;   <---   don't change anything ... just keep existing state as is 

coyotespike03:09:54

(just going to say that I love both Boot and re-frame, so seeing this discussion about integrating them, before I run into the same challenges, is fantastic)

juhoteperi05:09:12

@michael.heuberger Do you call the init fn from cljs.edn and the namespace top-level?

juhoteperi05:09:56

Don't use :init-fns and the init function will only be called once

martinklepsch09:09:14

@juhoteperi how will it only be called once when he puts it into the ns toplevel?

juhoteperi10:09:24

once per load

martinklepsch10:09:12

how does this work? like when I change the namespace the function is called again no?

martinklepsch10:09:05

then I'm confused but I'll just accept it for now 😄

juhoteperi10:09:11

but the problem was that init! was called twice per reload

juhoteperi10:09:21

or twice per initial load

martinklepsch10:09:07

ah ok, then I misunderstood 🙂

richiardiandrea19:09:50

so is it possible to have macros in pod with-eval-in code? like when, I have a strange problem with symbols and I guess I am not unquoting correctly

alandipert19:09:09

@richiardiandrea i ran into strange thing there recently myself

richiardiandrea19:09:49

uhm I am trying a couple of things with bt/template to see what is wrong as we speak, maybe it is just me 😉

alandipert19:09:21

in my case i was able to reproduce using backtick directly, i worked around by using .invoke

richiardiandrea19:09:07

@alandipert I am exactly working with a boolean named autostart? can it be the name? 😄

martinklepsch19:09:10

I'm good at hitting buttons lol

richiardiandrea19:09:47

lol thanks @martinklepsch I still don't dare merging myself 😄

martinklepsch19:09:12

I was thinking I'd just leave a comment if it looks good to me but didn't make much sense with such change

richiardiandrea19:09:51

@alandipert moment of lazyness, is there a way to macroexpand with backtick?

alandipert19:09:48

@richiardiandrea there's macroexpand-all in clojure.walk?

richiardiandrea19:09:18

ah ok, I thought I needed some particular thing for it to work with backtick...

richiardiandrea19:09:35

so basically this works:

boot> (bt/template (do [email protected](when '~autostart?
             ;; Auto-start the system
             (require 'dev))))
this does not:
(bt/template (do [email protected](when '~autostart?
             ;; Auto-start the system
             (require '~'dev)
             (dev/go)))) ;; No such namespace: dev

richiardiandrea19:09:39

the latter expands to:

(clojure.core/apply
 clojure.core/list
 (clojure.core/concat
  [(quote do)]
  (if
   (quote (clojure.core/unquote autostart?))
   (do (require (quote dev)) (dev/go)))))

richiardiandrea19:09:54

now I see I need to quote it right?

hiredman19:09:59

you likely need to dynamicaly resolve and call dev/go

alandipert19:09:05

@richiardiandrea you wanna do ((resolve 'dev/go))

richiardiandrea19:09:39

the weird thing is that without the when everything works, but there must be an explanation to that as well

micha19:09:54

yes, it's a weird thing about clojure's eval

micha20:09:11

the compiler treats top level requires specially

hiredman20:09:34

the compiler treats top level do's specially

micha20:09:58

yea it amounts to the same thing though

richiardiandrea20:09:19

ah ok, yes because the require did work, the dev/go didn't (which is in a do in with-eval-in definitely)

hiredman20:09:11

vars are resolved at compile time, and because the require the the dev/go where in the same compilation unit, the require hadn't run when clojure went to compile dev/go, so the compiled couldn't find the var

hiredman20:09:41

a top level form ~= a compilation unit

hiredman20:09:11

the compiler treats each thing in a top level do as its own top level form, so each thing in a top level do is its own compilation unit

alandipert20:09:57

btw @hiredman welcome to the party, good to have you

richiardiandrea20:09:14

mmm, probably the macro force is not strong in myself at the moment, but aren't both the when and the non-`when` version in a do?

hiredman20:09:55

the when version is a when, not a do, so the entire when is a compilation unit

micha20:09:29

basically a special case hack in the clj compiler

micha20:09:40

if it's evaling a do it does a special thing

micha20:09:57

if it's not a do it compiles the form normally

micha20:09:22

the special thing is what makes (eval '(do (require 'foo) (foo/bar))) work

richiardiandrea20:09:14

but that is exactly the above isn't it...I am still a bit confused 😄

micha20:09:37

the special thing is to compile the clauses in the do separately

richiardiandrea20:09:01

I mean

(if
   (quote (clojure.core/unquote autostart?))
   (do (require (quote dev)) (dev/go))
assuming autostart? is true

richiardiandrea20:09:09

ah ok now it starts to make sense

micha20:09:14

the first form is if there

micha20:09:32

so the special case doesn't apply

richiardiandrea20:09:45

awesome, crystal clear now

micha20:09:55

and the compiler will expect that all names have been resolved before referencing them

richiardiandrea20:09:29

uff, I think thanks to you guys I leveled up 😄