-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add select keyword #3130
Add select keyword #3130
Conversation
/cc @waj |
8ee9db6
to
8ff864b
Compare
EDIT: added missing specs where that show how |
describe "Normalize: case" do | ||
it "normalizes select with call" do | ||
assert_expand "select; when foo; body; when bar; baz; end", <<-CODE | ||
__temp_1, __temp_2 = ::Channel.select({foo_select_action, bar_select_action}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_for_select or _action, which is it? :)
Not too sure a about either name yet, have to sleep over it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good catch. _for_select
was our original choice, but _select_action
matches the SelectAction
"interface". We aren't sure about the name either, but since it's not going to be written manually...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just conceptually, not referring to the existing Future
, can't SelectAction
be thought of as a promise/future? Channel.select
receives a list of promises and returns the first that could compute a value. Following that line of thought, SelectActon
becomes Promise
and the suffix becomes _promise
, and channel.receive_promise
suddenly reads pretty well to me. Going further the whole concept could be decoupled from Channel
and Channel.select
could become Promise.select
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must admit it sounds good, but on second thought this is not really a promise. It's one possible action that can be executed in a select branch, but it's not guaranteed that it will be executed.
Plus, adding more functionality to it is pretty hard, you have to know the runtime internals. I'd say it's extensible so we can add more cases in the standard library, but I wouldn't expect 3rd party libraries to extend this, at least not now. So I prefer it to keep it under Channel and with a name that explicitly states it's for a select
expression, and only through a select
expression (so the underlying names don't matter much)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I asked a bit around whether there are existing terms or concepts to this problem.
One interesting note was that it could be a modled as a priority queue with dynamic weights. The weight would denote the readiness state, the queue would hold the operations and pop once. We would need then need to implement a constrained pop that blocks until an element with a high enough weight is available. But all of this doesn't help much with the name for the containers in the queue.
The other note I received was that OCaml calls this "alternatives", or its formal parent CSP calls it "choice". https://en.wikipedia.org/wiki/Communicating_sequential_processes#Algebraic_operators
I think that could actually work here as a generalized construct, Choice.select
, Channel#receive_choice
, Channel#send_choice
, Choice#ready?
, Choice#execute
, Choice#wait
, Choice#unwait
.
I'm not sure I follow you on the runtime internals, Choice
would define the the interface with how the operations have to behave, things like "wait
should ensure the current fiber is rescheduled when value of ready?
changes" and "unwait
should remove any reschedule setup by wait
". We then just have to document Scheduler.enqueue
and that's it, the rest is up to the implementer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, but I prefer to wait until we have true parallelism. I don't think any action could be put inside a select
, there will be mutexes and locks involved and it will be highly coupled with how the runtime works. I'm not sure there will be many extensions to this, we just did it this way so we could more easily implement all the operations.
In any case, this will probably evolve and change in the future until we stabilize everything in 1.0. But I'm convinced that using these things outside a select
expression makes little sense, so the real names aren't very important, and if they are called SelectAction
then it's pretty clear it's for them to be used in a select
.
Fixes #688
This adds the
select
keyword, which is syntax sugar to performa
Channel.select
more easily.For example, our current channel_select sample is:
With the new syntax it can be written as:
A
select
when
expression can either be a call or an assignment whose right-hand side is a call. The call name is added "_select_action" (taking bang and question marks into account) and that is passed to theChannel.select
method.The nice thing about this is that this is extensible: we could define a
timeout_select_action(n)
method, so one could do:In fact we were able to implement this but this will be pushed in a later commit (the interface and docs about how to implement a "select action" are current missing).
This is a breaking change, because now
select
can't be used as a method call, just likecase
can't be used for that. One has to use an explicit receiver, likefoo.select
orself.select
, or::select
.