Fork me on GitHub
#missionary
<
2022-08-02
>
martinklepsch11:08:07

I recently took the throttle function from the wiki and it does what I need but I’m having a hard time really understanding the role of each missionary function in it. I wonder if some code comments in that function could be added to explain? https://github.com/leonoel/missionary/wiki/Debounce-and-Throttle#throttle

martinklepsch11:08:17

In particular the nested amb> and how relieve works is not quite clear to me

Ben Sless11:08:10

Relieve turns a discrete flow into a continuous one You sample it, then the outer amb returns the forms in a flow in order, so first x, then sleep. The inner amb fails, which means that the entire computation is discarded, going back to the next available sample from the input

👍 1
martinklepsch18:08:01

Thanks. The inner amb failing and the consequences of that are a bit unclear still but the relieve and sample thing was very insightful!

Ben Sless18:08:37

The inner amb failing is your way to terminate the process

Ben Sless18:08:03

maybe the comments make it more readable:

(defn throttle [dur >in]
  (m/ap
   (let [x (m/?> (m/relieve {} >in))]
     (m/amb> ;; sequentially in an amb
      x ;; emit x
      (do (m/? (m/sleep dur)) ;; await sleep
          (m/amb>)))))) ;; die

Dustin Getz22:08:32

Simply, (m/relieve {}) means "don't block producers - discard stale values" m/relieve is a dangerous primitive, yes it converts a discrete flow into a continuous flow with a reducing fn BUT it does so by consuming the upstream flow as fast as possible and dumping it straight into the accumulator. In other words it "relieves" backpressure. This is appropriate for event sources like user interactions (typing into a dom input) because you only care about the freshest value and you never want your backpressure to slow down the user from typing. You lose the ability to run discrete effects on each discrete event, which for user typing into an input is fine, you only care about the freshest value and you don't care to see each intermediate keystroke. HOWEVER, by disabling backpressure you've ... lost all the benefits of backpressure, the whole point of backpressure is to tell the producer to slow down and prevent the system from getting too stressed the dangerous part is that m/relieve can be used to create "heaters" like this monster, which will turn your fan on while counting as fast as it can:

(->> (m/ap (loop [] (m/amb (m/? (m/sleep 0 1)) (recur))))
                (m/reductions + 0)
                (m/relieve {}))
Here is a video of the heater: https://www.loom.com/share/4406782e7a29487d8186eda86f8ef17b

Dustin Getz22:08:57

The safe way to turn a discrete flow into continuous flow is m/reductions with m/latest:

(tests
  (def <counter (->> (m/ap (loop [] (m/amb (m/? (m/sleep 0 1)) (recur))))
                     (m/reductions + 0)
                     (m/latest identity)))
  (def !it (<counter #(! ::notify) #(! ::terminate)))
  % := ::notify
  @!it := 1
  % := ::notify
  @!it := 2
  % := ::notify
  @!it := 3
  (!it))
✅✅✅✅✅✅
This counter is backpressured, it will count as fast as the consumer samples

Dustin Getz23:08:14

The experience was not good enough. I think maybe this is the old product though, possibly Clerk is better but not hosted afaik

Dustin Getz00:08:14

i am now imaging a discord eval-bot paired with RCF; and a nice discord logger which can link to a message or range of messages

❤️ 1
Daniel Jomphe15:08:53

Are you aware of #clerk channel?