Hi all, I've got 2 questions about the debounce sample:
1. why did we have to wrap the sleep inside a try-catch?
2. what's the point of returning (amb) rather than just nil
By convention, tasks and flows crash with missionary.Cancelled when they're cancelled before termination. In ap initial design, I chose to reflect this behavior on cancellations generated by `?>` ?< which is why the cancellation exception has to be caught. amb just means we don't have any meaningful value to return in this case, but nil also works as long as the consumer expects it. It turns out this design choice was a mistake, the correct behavior for ?< is to spawn the new branch immediately and silently flush the previous one in the background. It is implemented in cp and will eventually be ported to ap so you won't have to guard cancellation anymore.
https://github.com/leonoel/missionary/issues/68
https://github.com/leonoel/missionary/issues/94
was the first also meant to be ?<?
returning nil means the consumer will see a nil. Returning (amb) means the consumer won't see anything.
@leonoel if you remove the cancelation behavior from ap, how we we cleanup? Do the tasks under the ap still get the cancelation exception?
the current branch is still cancelled with all its children, just post-cancellation events are ignored
I see. So we can still catch the exception, but failure do so wouldn't affect the fork that interrupted the current branch?
do you have an example ?
no, the assumption is you never want to interact with the old branch, therefore missionary cleans it up silently. ?< says "I care about the latest value", i.e. it's OK to silently clean up the old value's supervision tree
I get that. The case I'm talking about it.. I care about the latest value. However processing of the current value mightve reached a point that I need to do cleanup before cancelation
How's can this be done if current branch is silently killed:
(m/ap (try
(let [x (m/?< a-flow)
y (allocate-resource! x)]
(do-something! x y))
(catch missionary.Cancelled e
(deallocate-resource! y))))you'd wrap your resource with cleanup, not the ap
see this discussion https://clojurians.slack.com/archives/CL85MBPEF/p1739693891085089 . TLDR you'd use the (m/sp (m/? m/never) (finally (cleanup))) pattern or the (m/?> (m/observe (fn [!] (! resource) #(cleanup)))) pattern
From playing in the repl, I think i got it:
1. cancellation of incomplete forks of ?< isn't silent.
2. unlike core.async, nil is a valid return value in an ap block