Fork me on GitHub
#core-async
<
2015-11-21
>
bsima18:11:26

do core.async channels maintain dynamic scope???

bsima18:11:14

for example if I bind a dynamic var with binding and then send a value across a channel, will the binding go with it?

bsima18:11:26

my intuition is telling me it does not...

bsima18:11:33

my experiments at the repl are also telling me it does not...

darwin19:11:01

@bsima: my experience with ClojureScript (don’t know about Clojure): I avoid using binding directly within go block code. It behaved unpredictably. But wrapping it in a function called from go block fixed my issues and behaved as expected. Binding is active while function sits on a callstack (javascript has only a single thread).

bsima19:11:49

@darwin, hm, thanks for the report. My plan will just be to wrap the thing I'm passing through the channel in a map that includes the value to which I want the binding to be set. At the other end of the channel, I'll have a function that actually makes the binding if necessary. I'm working in clojure and don't wanna mess with the complexity of multithreaded dynamic bindings simple_smile

bsima19:11:49

fwiw I'm implementing a restarts system like in this talk https://www.youtube.com/watch?v=zp0OEDcAro0 pretty awesome talk

ghadi23:11:45

@bsima please file a bug report, I'll address it

bsima23:11:14

@ghadi really? I mean I would think that dynamic scope wouldn't be maintained across threads, but I'm not familiar with the theoretical foundations in this area

ghadi23:11:13

Should be preserved across logical threads (go blocks). At least that was our intent

ghadi23:11:46

I remember implementing it, maybe it broke. That would be unfortunate

bsima23:11:23

Hm okay then. Could also be that my implementation was a unique case. I also had a thread pool and a component involved, but I'll try and reproduce the binding bug tomorrow or Monday with a minimal example

ghadi23:11:41

Are you binding outside of the go block?

ghadi23:11:04

If you are doing it on the inside it won't work

darwin23:11:29

@bsima: thanks for the talk recommendation, going to watch it now

bsima23:11:41

@darwin: that was actually passed onto me by @michaeldrogalis while we were talking about his dire library. He says he prefers this restart technique over his dire technique nowadays, and I have to agree

bsima23:11:12

@ghadi: it's outside go blocks

bsima23:11:54

(defn persist!
  "Persist a thing via a core.async channel. The optional argument err will be
  bound to the *persist-error* restart."
  ([component thing]
   (let [ch (:channel component)]
     (>!! ch thing)))
  ([component thing err]
   (binding [*persist-error* err]
     (persist! component thing))))

bsima23:11:46

At the other end of that channel I have this:

(defn- persist-impl
  "Implementation of the persister. Simply spits to `tmp-<timestamp>'. If no
  file descriptor fd is given, then a unique filename is generates.
  Fail:    calls *persist-error*
  Success: calls *success*
  Restart: *persist-retry*, lexically bound to persist-impl."
  ([content]
   (let [t (time/now)
         fd (format "tmp-%s-%s.txt" t (uuid))]
     (persist-impl fd content)))
  ([fd content]
   (spit fd content)
   (if (persisted? fd)
     (*success*)
     (binding [*persist-retry*
               (fn [_ _] (persist-impl fd content))]
       (*persist-error* "Persist failure!" {:file fd})))))

ghadi23:11:29

Binding don't cross channels

ghadi23:11:41

Just lexical scope within the go block

bsima23:11:04

Okay so no bug report needed.

bsima23:11:31

I'll just change persist! to take an map with the thing to be persisted and the desired binding, and then actually do the binding in persist-impl