-
Notifications
You must be signed in to change notification settings - Fork 30
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
What's keeping this from Stage 1? #24
Comments
I'm the champion. : ) There are a couple of reasons that I haven't attempted to advance this proposal with TC39: First, when I presented this proposal back in March, I received a fair amount of pushback on the following grounds:
On balance, TC39 wasn't particularly supportive. We went ahead with getting it into Babel, though, in order to gather some feedback. That feedback has been really useful. I believe that the next step is to decide whether we would be better off splitting the proposal into separate "method extraction" and "functional pipelining" proposals. I'm cautiously in favor of that. The second big reason that I haven't advanced this proposal is that I don't think this is the right time for adding syntax to JS, with the big exception of async functions. I think we need to focus on just async functions for ES7/2016/whatever. Too much syntax too quickly isn't a good thing, especially given the huge amount of syntax added with ES6. |
Okay. Understandable. |
I'm curious about functional pipelining. How would that work out? |
Edit (Aug. 12, 2016): This is merely a strawman proposing a change to the proposal, not the actual proposal itself. Just to clarify. @zenparsing I do like the idea of separating these two ideas. First of all, method binding a la Java would look really nice. It could even open up the possibility of a property shorthand. getBooks().then(JSON.parse).then(books => books.map(book => new Book(book)))
// Simplify constructor call
getBooks().then(JSON.parse).then(books => books.map(new::Book))
// Shorthand binding of book => book.length
books
.then(books => books.filter(compose(::length, atLeast(5))))
.then(books => books.map(new::Book))
Shorthand for book => book[prop]
books.then(books => books.map(::[prop])) It would be incredibly convenient, and it is also very commonly used. It's also extremely optimizable. As for a pipelining syntax, I was kinda thinking of something like this: function *map(f) {
for (const i of this) yield f(i)
}
function* concat() {
for (const i of this) {
if (i[Symbol.iterator]) yield* i
else yield i
}
}
function *flatten() {
yield* this
->map(x => x->flatten())
->concat()
} |
And for the dynamic class A {
foo() { return "A!" }
bar() { console.log(this.foo()) }
}
class B {
foo() { return "B!" }
}
console.log(new B().bar()) // "B!" |
@IMPinball I think the method being bound should be on the right of the operator, for consistency both internally and with other languages. Type::new or Type::static and instance::method provide a predictable way to reference a call of some sort, even though new is an operator (putting it before the type seems like you're inverting the application). All forms can desugar into simple arrow functions or even just function calls. |
@ssube Then how would you do something like |
@IMPinball I think the property access should take care of that: instance::[new]. I'm not sure I've seen that in use, but explicitly marking it as the new property should work. |
@ssube I like that idea. On Thu, Sep 17, 2015, 23:57 Sean Sube notifications@github.com wrote:
|
@shovon A pipelining operator (found in some languages like F# as function fn(a, b) {
console.log([a, b].join(", "));
}
"hello"->fn("world");
// > "hello, world" Where the left hand side is used as the first arg in call to the function on the right hand side. |
Closing in favor of #26, as the initial questions have been answered. |
It seems like this issue isn't really resolved since a bunch of the community want to see it move forward, and it still isn't. Is it possible to reopen this so people have a place to discuss the fact that it's being blocked? (re: #30 (comment)) Right now #30 has the most recent discussion but isn't actually about the progress of the proposal |
@deontologician Happy to reopen. I would be willing to present this proposal to the committee again, but would like a co-champion. |
Does the co-champion have to be on the committee already? |
@deontologician Yes. |
Is there a list of people on TC39? I found http://tc39wiki.calculist.org/about/people/ but I'm not sure if it's up to date |
I really hope this proposal will be presented again to TC39. It just too useful for things like the I just wrote a couple of helper functions to make selenium tests really easy to write:
Every helper function is |
@donaldpipowitch You could just call |
|
@flying-sheep my point is more that So in that case it's just a regular, infix function call, with syntax that looks like something entirely different (namespaces in Ruby, bound methods in Java, etc). Purr had a similar problem where |
of course you’re right that there’s no functional difference here. both are just ways to call freestanding functions. so your problem with |
I have been overloading the Please see https://github.com/gajus/babel-plugin-transform-function-composition. For a visual example: Input: apple
::foo('foo parameter 0', 'foo parameter 1')
::bar('bar parameter 0')
::baz('baz parameter 0'); Output: baz(
'baz parameter 0',
bar(
'bar parameter 0',
foo(
'foo parameter 0',
'foo parameter 1',
apple
)
)
); Unlike the bind proposal, this implementation does not concern with the invocation context. I know that this is unlikely to be serious contender to the current proposal because of the JavaScript-land conventions, but throwing it into the conversation for others awareness.
|
@gajus Interesting! |
OK, so there’s now three slightly different proposals: given the operator
|
@flying-sheep I believe the gajus model is @gajus can you explain more about why that is? |
oh right. i didn’t read properly and just assumed it would be that way. |
I cannot recall the exact reason, though I am pretty sure it was done to be consistent with Ramda. From Ramda documentation:
Ramda core contributors (ping @buzzdecafe @davidchambers @CrossEye @megawac @ndhoule) are in better position to argument this design decision. |
FWIW, I just assumed @gajus meant `foo::bar(baz)` -> `bar(foo, baz)`. That
at least works with 90% of the functional, uncurried APIs out there.
Here's how I see it:
1. `foo::bar(baz)` -> `bar.call(foo, baz)`
Pros:
- Very similar to existing OO methods.
- Can borrow builtins.
- `Reflect.apply`, `Function.prototype.call`, `Function.prototype.apply`,
and `Function.prototype.bind` can be implemented in terms of this, and the
common `%Call` native no longer needs to exist in V8 and friends. So all of
those can then be implemented at the language level. Implementors would
appreciate this.
Cons:
- Extension methods aren't inherently OO, and your FP fans may not like it
a ton.
- Doesn't work well with libraries like Lodash.
- It naturally begets the `foo::bar` proposal here, which has been noted to
cause confusion for not being memoized. The only way to rectify that
without major perf issues is via an internal, per-realm 2D weak map with
objects, the extension methods, and the resulting bound function. Doable,
but complex.
2. `foo::bar(baz)` -> `bar(foo, baz)`
Pros:
- Works well with functional APIs like in Three, Lodash, etc.
- Can define extensions via arrow functions, due to no `this`
Cons:
- You have to wrap builtins to borrow them
- Looks method-like when it's not. (The lack of `this` might surprise those
unfamiliar.)
3. `foo |> bar(baz)` -> `bar(baz)(foo)`
Pros:
- Works well with curried APIs like in Ramda and lodash-fp.
- Transpiling and desugaring is far simpler.
Cons:
- Engines don't do much closure elimination for the obvious. (They only
usually optimize IIFEs.)
- Engines have to create some exceptionally complex ICs and flags to avoid
allocating a closure for function calls into immediately returned closures
from different scopes, and they currently do none of this. You'll get
pushback from them on this part in particular.
- It requires a familiarity with FP to use, and JS isn't idiomatically
functional. (It's closer to Erlang than Scala/F# or Java, because it's so
event-driven.)
…-----
As for `foo::bar(baz)` -> `bar(baz, foo)`, that order is rarely used
outside of curried APIs.
My preference is for the status quo (albeit slightly modified), because
that will make self-hosting builtins easier, it leverages the `this`
optimization already employed in argument order, and has the fewest gotchas
for the less experienced majority of users.
|
I think perhaps talking about alternative proposals on this proposal's repo isn't particularly productive? It seems that the only thing this issue needs is to identify a champion who is already on TC39. Discussion of anything else, prior to that, is premature. I'd be happy to do it - I'm a very big fan of the original proposal - but I won't have the time to take it on for quite awhile. |
@ljharb I agree, and in hind sight, I probably should've not replied with that... I did take one of the cons and converted it into a new suggestion (see #46), as it looks like it should already be that way, though.
Note that this pro I listed is false, because changing |
using |
@ljharb The spread operator in |
right but |
@ljharb I'm aware. I was specifically referring to those specific methods, which require variadic application. |
How about people contacting/pinging champions to get feedback as to why they don't want to be champion? Might be worthwhile finding out what needs to change (i.e. drop the @zenparsing its been 2 years since your remarks - all the async changes have filtered through now, so perhaps now things are relatively quiet to think about this again? Would you want split off the "functional pipelining" proposal before picking it up again? |
@MeirionHughes In all honesty, this strawman in its current form is for all intents and purposes dead, and most of the conversation here is already looking for better ways to handle this (as the current strawman has quite a few shortcomings). |
i don’t share this view. i think the tradeoffs here are preferable to the ones in other approaches. i’ll refer to the three options as “bind-style” ( e.g. you say the bind-style “Doesn't work well with libraries like Lodash”, but neither does curry-style. people had to write lodash-fp, and such a port is easy if you already have a final API you just need to adapt in a rather mechanic way. also you say bind-style was “OO”, but it isn’t: “OO” means “data coupled with behavior”, which this proposal decidedly isn’t: finally you mix syntax and semantics. partial-style doesn’t need to use |
@flying-sheep To clarify, here's some of its shortcomings (and why we need a better proposal):
Before I proceed further, we should really be talking about this (beyond the binding side) over in this repo.
I personally proposed
I'm aware, but it is something to consider. In particular, it's why
I mean OO in the sense that:
So yes, it does have some degree of coupling between data and behavior, and in effect, you're just extending an object with new behavior. Semantically, Oh, and I'd rather 3. be dropped from this proposal, and this proposal would rectify 2. |
Given that this thread has sufficiently derailed* and the original question for all intents and purposes answered (there's too little consensus on what problems this proposal's bind half is actually solving), I'm closing this. * I'm not pointing fingers or judging anyone for it, since I myself helped it along. 😄 |
Note that " Doesn't work well with libraries like Lodash." is false/irrelevant. lodash-bound already exists, so it's entirely possible to use lodash with the current Secondly the only reason |
@ALL Can we just let this thread die? @zenparsing Since the pipeline operator proposal is down a similar vein for the common use case of this, I feel this proposal could probably stand to be recast now, to not try to duplicate that effort. |
Have no idea why "explicit this" will block this proposal.
Just drop ditto.
I agree. So what's the next step? |
Edit: Keeping this list up to date.
TL;DR: See title.
This already has transpiler support, and has definitely fulfilled at least most of the requirements for Stage 1. These are the ones I can tell are very clearly, unambiguously met (pulled from the process document):
I'm just wondering what's holding this back from Stage 1.
The text was updated successfully, but these errors were encountered: