I'm having trouble getting my head around the purpose of supply {…}
blocks/the on-demand supplies that they create.
Live supplies (that is, the types that come from a Supplier
and get new values whenever that Supplier
emits a value) make sense to me – they're a version of asynchronous streams that I can use to broadcast a message from one or more senders to one or more receivers. It's easy to see use cases for responding to a live stream of messages: I might want to take an action every time I get a UI event from a GUI interface, or every time a chat application broadcasts that it has received a new message.
But on-demand supplies don't make a similar amount of sense. The docs say that
An on-demand broadcast is like Netflix: everyone who starts streaming a movie (taps a supply), always starts it from the beginning (gets all the values), regardless of how many people are watching it right now.
Ok, fair enough. But why/when would I want those semantics?
The examples also leave me scratching my head a bit. The Concurancy page currently provides three examples of a supply
block, but two of them just emit the values from a for
loop. The third is a bit more detailed:
my $bread-supplier = Supplier.new;
my $vegetable-supplier = Supplier.new;
my $supply = supply {
whenever $bread-supplier.Supply {
emit("We've got bread: " ~ $_);
};
whenever $vegetable-supplier.Supply {
emit("We've got a vegetable: " ~ $_);
};
}
$supply.tap( -> $v { say "$v" });
$vegetable-supplier.emit("Radish"); # OUTPUT: «We've got a vegetable: Radish»
$bread-supplier.emit("Thick sliced"); # OUTPUT: «We've got bread: Thick sliced»
$vegetable-supplier.emit("Lettuce"); # OUTPUT: «We've got a vegetable: Lettuce»
There, the supply
block is doing something. Specifically, it's reacting to the input of two different (live) Supplier
s and then merging them into a single Supply
. That does seem fairly useful.
… except that if I want to transform the output of two Supplier
s and merge their output into a single combined stream, I can just use
my $supply = Supply.merge:
$bread-supplier.Supply.map( { "We've got bread: $_" }),
$vegetable-supplier.Supply.map({ "We've got a vegetable: $_" });
And, indeed, if I replace the supply
block in that example with the map
/merge
above, I get exactly the same output. Further, neither the supply
block version nor the map
/merge
version produce any output if the tap
is moved below the calls to .emit
, which shows that the "on-demand" aspect of supply
blocks doesn't really come into play here.
At a more general level, I don't believe the Raku (or Cro) docs provide any examples of a supply
block that isn't either in some way transforming the output of a live Supply
or emitting values based on a for
loop or Supply.interval
. None of those seem like especially compelling use cases, other than as a different way to transform Supply
s.
Given all of the above, I'm tempted to mostly write off the supply
block as a construct that isn't all that useful, other than as a possible alternate syntax for certain Supply
combinators. However, I have it on fairly good authority that
while Supplier is often reached for, many times one would be better off writing a supply block that emits the values.
Given that, I'm willing to hazard a pretty confident guess that I'm missing something about supply
blocks. I'd appreciate any insight into what that might be.