missionary

2024-10-24T16:26:45.027589Z

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 ➡️

2024-10-24T16:27:52.244489Z

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...

2024-10-24T17:13:48.096249Z

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$Process

leonoel 2024-10-24T19:56:40.746089Z

I 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.

2024-10-24T20:29:35.264249Z

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.

leonoel 2024-10-24T20:31:46.981509Z

correct

👍 2
2024-10-24T20:39:20.335289Z

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

awb99 2024-10-28T15:07:12.220469Z

@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.

👍 1
2024-10-28T15:08:26.494639Z

Nice one! I will try it soon for sure

awb99 2024-10-28T15:21:39.841089Z

@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.

awb99 2024-10-28T15:23:57.948869Z

(defn calculate [env formula-fn args] (with-bindings env (apply formula-fn args)))

awb99 2024-10-28T15:24:43.357239Z

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.

2024-10-28T15:25:52.342429Z

I will take a closer look. But idk when