This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-11-13
Channels
- # announcements (2)
- # babashka (30)
- # beginners (44)
- # biff (20)
- # calva (15)
- # cider (7)
- # clerk (4)
- # clojure (15)
- # clojure-austin (1)
- # clojure-europe (35)
- # clojure-hungary (1)
- # clojure-nl (1)
- # clojure-norway (6)
- # clojure-uk (7)
- # clojurescript (33)
- # conjure (1)
- # cryogen (1)
- # cursive (3)
- # data-science (8)
- # docker (2)
- # emacs (78)
- # gratitude (2)
- # hyperfiddle (26)
- # improve-getting-started (1)
- # jobs-discuss (12)
- # lsp (7)
- # malli (11)
- # missionary (57)
- # off-topic (14)
- # pathom (13)
- # portal (6)
- # reagent (6)
- # releases (3)
- # ring (25)
- # shadow-cljs (18)
- # squint (11)
- # vim (3)
See gist for the issue: https://gist.github.com/telekid/07cb59b77b93b45ae5de0cefce92dec8#file-temp-adjust-clj-L7
i dont think you should ever have to catch missionary.Cancelled, is that right Leo?
eventually no, but currently yes https://github.com/leonoel/missionary/issues/94
you mean: today userland should never have to catch missionary.Cancelled, but in the future there may be a reason for userland to catch Cancelled?
under what circumstance exactly do we care
I see
today, m/?< ("switch") will cancel the old child branch, and let the terminal value (likely the exception) be seen by the parent, and then boot the new child branch
the desired behavior is for the old child branch to be flushed for effect (resource disposal) but the terminal value is not seen, because the "switch" is effective immediately
we want to ignore everything produced by the previous branch after its cancellation
and this is justified by the idea that since a variable input has changed (causing the switch), seeing the terminal value is an inconsistency, as in a glitch?
or is it simply justified by ergonomics, parent userland should not need a defensive try/catch everywhere
(otherwsie the Cancellation would propagate too far and unwind the entire program, which is never correct the user's intent)
it feels wrong to have to catch Cancelled on switch because there is no real-world scenario requiring this
there is also no good reason to delay the creation of the next branch due to a slow shutdown of the previous one
ah, under today's switch semantics the terminal value is required and awaited before we can switch
I wanted to ask, why is this a discrete phenomenon only, but i remembered that m/cp didn't exist yet and when we worked out the semantics of m/cp is how we first understood how switch and cancellation should interact
so m/cp was built with the right semantics in the first place, and updating m/ap was backlogged
(the discrete phenomenon here being the idea of a delayed termination?)
is it even possible for a continuous flow to have delayed termination?
ah, of course, the quickstart shows how to have a continous flow be cancelled but the response to the cancellation is not seen until (under the flow protocol) the flow notifies and then is consumed
and under switch, we cancel and then stop listening, (though the machinery will consume the terminal value to drive resource cleanup)
> the continuation of ?<
will throw Cancelled
before switching
I think that this means I should be doing something like this, but of course it is illegal because one Cannot recur across try
And for kicks I tried defining it as a continuous process (which is probably preferable anyway,) but modifying (ignore this, I tested it incorrectly. Obviously tempo
still terminates the flow with Watch cancelled.
cp
isn't compatible with m/?<
.)
this pattern is not correct because for each iteration of the loop another subscription to tempo
will be created without cancelling the previous one
Something like this:
;; Emit a keyword representing a midi event on each beat. If `tempo` increases, :kick-drum should emit more frequently.
(fn drum [tempo]
(m/ap
(m/amb (do (m/?< (beats tempo 0))
:kick-drum))))
Or perhaps the effect would happen in a reducer, not sureThis pattern works. This is basically a discrete version of the with-cycle
pattern, the cycle is needed to keep track of state through successive switches
(defn now [] (System/currentTimeMillis))
(defn variable-clock [<bpm]
(m/ap
(try (let [state (atom [0 (now)])
[i ts bpm] (m/?< (m/latest conj (m/watch state) <bpm))
target (+ ts (int (* 1000 (/ 60 bpm))))]
(m/? (m/sleep (- target (now))))
(reset! state [(inc i) target]) i)
(catch missionary.Cancelled _ (m/amb)))))
(comment
(def !tempo (atom 120))
(def ps
((->> (variable-clock (m/watch !tempo))
(m/reduce (fn [_ i] (prn i)) nil))
prn prn))
(reset! !tempo 60)
(ps)
)
https://gist.github.com/telekid/86bebd7f94073302e743abf01b148a8e. It prints midi notes at the given tempo and adjusts the length of notes proportionally. While this particular example isn't too hard to express imperatively (or in a traditional functional manner), more complex examples are tough to express and I can already see how missionary makes it a ton easier.
My next goal is to figure out how to model notes as resources that I can hand over to the supervision tree to avoid "hanging notes," where a midi on event occurs without a matching midi off event.
https://gist.github.com/telekid/86bebd7f94073302e743abf01b148a8e (just in case you didn't catch the link in my prev msg.) Will follow up when (if? ha) I figure out note modeling
related reference - https://www.cs.yale.edu/homes/hudak/Papers/HSoM.pdf has a midi intro as well. heads up the PDF is unfinished, book version is the final draft