Hi Missionaries!
Is it expected behaviour that m/via tasks are not only don't have external dynamic bindings inside, but breaking them in outside tasks also?
Example in Thread ➡️
Here is an example. This code:
(def sp-task (m/sp :sp-task))
(def blk-task (m/via m/blk :blk-task))
(def ^:dynamic *var* nil)
(defn outer-task
[t]
(m/sp (println *var*)
(println "task:\t" (m/? t))
(println *var*)))
(binding [*var* :var]
(m/? (outer-task sp-task))
(m/? (outer-task blk-task))
(m/? (outer-task sp-task)))
Will print out:
:var
task: :sp-task
:var
:var
task: :blk-task
nil
:var
task: :sp-task
:var
As wee can see, *var* binding is broken in outer-task after blk-task computed...And what is moreover sad, that call of ((bound-fn [] (m/? task))) can return missionary.impl.Sequential$Process in some cases...
For example:
(def ^:dynamic *var* nil)
(m/?
(m/sp
(binding [*var* :var]
(type ((bound-fn [] (m/? (m/sleep 100))))))))
prints missionary.impl.Sequential$ProcessI see two separate issues here
1. Dynamic vars. I do not think a correct solution exists, dynamic scope and async just don't mix together. Implicit binding conveyance has weird edge cases, it has a performance impact even if you don't use it, and it simply cannot work in clojurescript because vars are not reified. For these reasons, missionary operators just ignore the existence of dynamic vars, the resolution depends on the current thread context which may change during the lifecycle of each process.
2. Indirect calls to m/? m/?> m/?<. They're not supported and won't be until the JVM and browsers provide a continuation capture mechanism. Currently the call returns an internal object, this is obviously wrong and unhelpful, it should throw an exception instead.
Thank you. Correct me if i wrong:
m/? m/?> and m/?< sholud be called only at first level of m/sp m/ap etc, or they have unpredictable behaviour.
correct
Then exceptions would be nice, because indirect calls are work as expected often and appeared an illusion that it's okay until a certain point
@sasha_bogdanov_dev I did write a linter that at least alerts when this is being done: https://github.com/clojure-quant/missionary-test/blob/main/.clj-kondo/hooks/mawait.clj Please not that this is my current work in progress. I am pretty sure that it can be improved a lot.
Nice one! I will try it soon for sure
@sasha_bogdanov_dev If you find a solution to wrap dynamic vars into tasks, then please let me know. I have the same problem as you. There needs to be some kind of helper function that sets bindings inside m/sp or m/ap. So perhaps a (wrap-bindings [env-map sp-process]) where env-map is {'ns1/s1 ns1/s1 'ns2/s2 ns2/s2}. That is what I am trying to get done.
(defn calculate [env formula-fn args] (with-bindings env (apply formula-fn args)))
This is what I use currently, but it only works on functions inside a m/sp or m/ap and I am pretty sure it can be generalized.
I will take a closer look. But idk when