-
Notifications
You must be signed in to change notification settings - Fork 90
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
Syntactic assist? #141
Comments
Interesting idea. I'm a bit hesitant to add syntax to the language without a compelling reason - it's easy for it to turn into cruft, and we have a limited number of special characters. If we use Also, it's unclear how you'd complete a stream from within the body of an |
Edit: Update a few things
I generally agree with that sentiment myself, and have countered numerous proposals on es-discuss because of it. And in particular, syntactic additions are usually pretty hard to justify. In this case, I feel it's justified because of the following:
Note that I just updated the proposal to simplify it some, and one of the changes involved using the keyword
Completing and erroring out of a stream would be via |
Here's a gist comparing my syntactic idea to the existing status quo with RxJS, and here's another that is a simple utility library for observables à la Lodash/etc.. Incidentally, it actually seems make the code more verbose, but it's much clearer and explicit with its data flow, emphasizing it much more than the algorithmic steps. IMHO it's actually much more declarative in how it runs, since it admits some non-determinism naturally, and it provides declarative facilities to manage it. I'll admit it's not exactly based on a traditional von-Neumann-based model, and is more of a blend between FRP, data-flow programming, and stream processing, and would really show its true colors on the server, where you could avoid most of frameworks in general, and instead focus on just control flow. Here's my current server, adapted to full ES6, with one variant rewritten to use stream functions. |
This needs to happen after es-observable lands. In the mean time, since Observable will implement We need to make sure we're not spreading the proposal too thin. |
I'm okay with that (and was going with that assumption). It wasn't meant to block the current proposal, but more so focus on future directions. In particular, I was also leaving the door open for interop with Promises, which are a whole separate deal, potentially more complicating (that I intentionally omitted from this starter proposal). This is more like a stage -2 strawman, to get the idea out there, and there are still certain things I still have to resort to the Observable constructor over, such as flattening an
That'd be nice, except Most likely, we'd continue using the same operator methods we use now, since it already helps the situation a lot (way easier to reason about than event emitters all over the place). |
Update: I've expanded on my concept here in this gist. It's blocked on Observables getting through, of course, but it's still somewhat related. It does in fact expand to integrate with both Promises and Observables, and I took caution to try to avoid certain performance cliffs in the process of explaining some of the details. |
I really like the
I am inclined to ask a somewhat offtopic, but I think fair, question: So my question is why is why is there so little (perceived) interest in adding some form of comprehension syntax to ECMAScript? It would be highly valuable for observables, iterables, and plain old arrays for the language to support comprehensions such as in C# from event in events
where event != null
where event.Key == Key.Enter
from c in select event.InputSource.Value
select char.ToUpper(c) and in Scala for {
(key, inputSource) <- events
if inputSource != null
if key == Key.Enter
c <- inputSource.value
} yield c.ToUpper And many other languages. Perhaps ironically, while these languages have always had first class iterator consumption syntax, something ECMAScript did not have for arrays until ES2015, when someone needs to express something more complex than their fancier comprehension syntax allows for, they most often tend to fall back to APIs closely resembling those provided by ECMAScript arrays, such as Basically without a reified syntax for comprehensions, including both filtering and projecting within an expression context, these iterator-like syntactic addition offer very limited value. I had heard that something like this was on the table at some point for ES2015 and was scrapped. This would provide of a lot of value in general and needless to say introducing syntactic sugar for observables, without even having basic filter support would be, from my point of view, a waste. Sorry for taking this off topic. With all the new iterator-like syntax being discussed in this issue, I really wanted to comment. Thank you. |
Edit: Changed disambiguator to
stream
, removed sync variant, clarified.Edit: Added arrow function variant,
parallel
keywordEdit: Most of this is out of date. Please see this gist.
I was looking at @jhusain's 2-year-old proposal of "push generators" (a glorified Observable), and I was thinking observables might be easier to consume with a syntax assist. Basically, a way of iterating eager sources, much like how generators use
for ... of
andfor await ... of
.Maybe something like this?
As for my specific choice:
emit
statement instead ofyield
: different semantics (push, not pull), andemit
may be firing multiple listeners.emit*
is an expression, because of the potential completion value.for ... from
: It's iterating events received from the stream/observable. I'm breaking from thefor ... on
idea, because you're not iterating values on anything. Note that it's only available instream
contexts.for await ... from
: Sometimes, it's convenient to await for completion. Also, such a promise would be purely an implementation detail, and an implementation might avoid creating the promise at all.parallel { ... }
: Merging is a very common operation, one that needs first class support. Iteration is inherently sequential, streaming is not. And merging is far more common than concatenation in the world of reactive programming. The word choice is to also open the door for expanding to promises.And some other notes:
return
andthrow
work as expected in stream functions, and map toerror
andcomplete
. (IMHO, the latter are probably not the best names.)for ... from
andfor await ... from
is executed in parallel and unbuffered, for performance reasons. Execution continues on the current tick and next tick, respectively, after the observable completes and all outstanding iterations are completed. If youawait
within that loop or read from another observable, there is the potential for race conditions.for ... from
variant is broken early (viareturn
orbreak
),unsubscribe
is called implicitly to avoid memory leaks.parallel { ... }
andawait parallel { ... }
are both expressions, returning an array of their completion values. An implementation might avoid storing them altogether if the result is unused.for ... from
andfor await ... from
would require a separate helper.parallel { ... }
andawait parallel { ... }
would require a separate helper.stream
instead ofasync
), and are also in an async context.The text was updated successfully, but these errors were encountered: