problem-solving
problem-solving copied to clipboard
Make react return values
It would make react even more useful if that could return data from inside its block.
I suggest doing that by making done accept an optional value and make react return it.
So something like this
say react whenever Promise.in: 1 { done 42 }
would print 42
would that make sense?
I guess the best way to find out is to write a PR for it:
This would involve adding a payload to the control message throwing in sub done and replacing the Nil in sub REACT with the value from the payload?
I really like this idea – it would increase the parallelism between react and gather. How would done work outside of a react block? Still accept a value and return it somewhere? Throw an error? Discard the value?
Throw an error:
$ raku -e 'done'
done without supply or react
in block <unit> at -e line 1
@jnthn opinion?
done without supply or react
Sorry, I wasn't clear: I meant how to handle done in a supply context, not in an arbitrary context.
@jnthn opinion?
There's really two requests in one here, and I feel very differently about them.
The first is that a react block might produce a value. This breaks the principle that react is for side-effects and supply is for the production of values. The very reason I introduced react to the language was for situations where we want to implement a sink of events that will react to them by doing side-effects. Thus I find this proposal simply too corrosive to the design to accept.
The proposal for done with a value is OK by me. In fact, the S04 design document proposes that the last control exception thrower could take an optional value argument, which would become the result of the final iteration (in the case that a loop is being evaluated for its values). That's the interactive dual of what is being proposed here. We could implement this behavior of last at the same time we do it for done, then it's nice and consistent. Anyway, a tentative +1 on this part.
Returning to the problem as a whole. The semantics of await on a Supply are to tap it, keep track of the latest value it has emitted, and produce that value when it is done (or throw an exception if the Supply should quit). Combined with a done that takes a value, we can write:
say await supply whenever Promise.in: 1 { done 42 }
Which is only one word longer than what was originally requested.
Ah, and a further point: it's not uncommon to write an emit followed by a done in a supply block today also, so this would find far more general use.
Problem with last taking a value to return, is that last can already take a value. Well, if that happens to be a Label:D. I guess we probably do not want to last a Label ?
@lizmat Yeah, S04 speculated using multiple dispatch to resolve that. Agree that one would pretty much never be using a Label as data.
Ah, and a further point: it's not uncommon to write an
emitfollowed by adonein asupplyblock today also, so this would find far more general use.
Hmmm, would this be a way to address the "chatty WebSocket" problem with Cro, where it follows the last data frame in a message with an empty fin frame, because emit and done have so far been separate operations?
I feel like there's a little more work to do to take advantage of this, but my brain is not fully engaged at the moment, so I'm not seeing exactly what that work would be.
@japhb possibly.
Meanwhile I'm trying to grok what the semantics of dd ^10 .grep: { $_ < 5 || last 42 } would be. (0,1,2,3,4,5).Seq ? Or (0,1,2,3,4,42).Seq ?
(0,1,2,3,4,5).Seq
I think this must be the right one. It doesn't look right if grep would produce anything but what it is supplied with.
Hmmm, would this be a way to address the "chatty WebSocket" problem with Cro, where it follows the last data frame in a message with an empty fin frame, because emit and done have so far been separate operations?
No, this is purely a convenience on the sender side. done $x should be precisely equivalent to emit $x; done.
It doesn't look right if grep would produce anything but what it is supplied with.
That's my thought too.
Looking at &take, it seems that the truthiness of 42 matters as well, so dd ^10 .grep: { $_ < 5 || last 0 } would be (0, 1, 2, 3, 4).Seq. Does that seem right as well?
I'd forgotten you even could use next, last, etc. in grep, and wonder if this was an intended feature, or an accident of grep being implemented in terms of map. If grep really is defined as relying on map for its loopiness then the 42 ending up in the result list is the rather unfortunate logical consequence of that. We will need to pick a semantic for now, but I'd not be entirely sad to see redo/next/last on a grep ending up deprecated...does anyone have an example where they are useful rather than baffling?
does anyone have an example where [using
next,last, etc. ingrepis] useful rather than baffling?
I think the main use-case for last in grep is to short-circuit when eagerly grepping over a long list for performance reasons.
In other languages, I've seen cases where code would have been more readable with a .filter but was written as a for loop precisely to allow for short-circuiting. Raku's support for laziness helps, but there are still cases where code will need to be eager.
[Edit: I can't think of any possible reason for using redo or next inside a grep, though, unless the code is doing something a bit crazy in terms of side effects. Maybe if grep is being used with gather/take?
Also, the semantics of grep are pretty much identical to a map that sometimes returns Empty, which I guess would give people a way to short circuit when filtering a list even if they can't do so inside grep]
I think the main use-case for
lastin grep is to short-circuit when eagerly grepping over a long list for performance reasons.
Ah yes, I'd overlooked, and agree it's of value. I guess when doing that, you'd want to choose whether or not to include the current value, and boolifying the argument to last would be the logical way to do that. So yes, I think I concur with your expectations on that.
FWIW, I also dislike next, redo and last in .grep and .map.
next and redo are simply meh.
last could perhaps be replaced by returning IterationEnd as value?
FWIW, I also dislike next, redo and last in .grep and .map.
Well, in map they are there because:
foris defined in terms ofmap(with appropriate sink/eager and serial enforcement); andforin any non-trivial or non-sink case is even compiled into a call tomap.- From the perspective of the language user, it allows one to easily refactor between
forandmap
could perhaps be replaced by returning IterationEnd as value?
I only really would want to see IterationEnd show up in code that is implementing an Iterator.