-
Notifications
You must be signed in to change notification settings - Fork 107
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
Discussion: Proposal Mixes #89
Comments
Great explanation there! The only other thing I'd like to see is a summary about how these are or aren't "forward-compatible" with @rbuckton's partial application operator, and what implications they'd have for the operator if compatible. It's OK if we decide that that's not a goal, but I'd like to be explicit about the tradeoff. |
@gilbert thanks for the summary. I'm surprised by the presence of |> (a => readDB(a, config))
|> await becomes |> await (a => readDB(a, config)) etc... Do you mind if I add the #23 (comment) precedence proposal as a fifth option ("F# with lower precedence")? |
@littledan Sure, I added some bullets indicating compatibility. @pygy For F-sharp style, For the grammar point I think we should wait for an approval on its possibility from a tc member first. If approved, it would impact F-sharp usage sections instead of being a 5th option. |
Does this issue cover both the dual-operator Proposal 3: Split Mix and the single-operator Proposal 4: Smart Mix? Because as far as I can tell, #75 already covers Proposal 4. The original post proposes a single pipeline operator that supports both F#-style tacit calling and Hack-style placeholder expressions. |
@js-choi The conversation in #75 has bounced around quite a bit. With some concrete proposals to work off of, I think we should pick up both "mix" options here and close #75. I really like "Smart Mix" with some bikeshedding over the token. Seems like a solid best of both worlds. Are dropping the closing-over behavior discussed elsewhere? I think it's simpler without it, but just want to make sure that wasn't overlooked. |
I like the Smart Mix option as well. I want to suggest a specific version of it, inspired by comments from @rbuckton:
This version would be cleanly forward-compatible with the partial application proposal as it currently is. IMO unless we want to argue that partial application isn't going to happen, we should try to make placeholders explainable as them. Of course it's fair game to propose changes to partial application, but I don't really see any changes as being necessary here with the above options. @rbuckton explained to me that, even with the partial application proposal landed, it still may make sense to support a direct semantics for pipeline placeholders, to make it so that the anonymous partially applied function unobservable (e.g., through engine-specific exception handling APIs which sometimes expose sloppy mode function identities). What do you think? |
@littledan What you're suggesting is not a mix, but F-sharp behavior with partial application built-in. The hack-style behavior is gone, thus the need for |
@littledan Does this imply that arbitrary expressions, e.g. |
@gilbert Yes, I see that it's different. Should we put it as a fifth option in the wiki? @mAAdhaTTah No, this would be disallowed. |
Then I think we ought to have that conversation, because the forward-compatibility constraint is leading towards a bizarre set of special-case rules that I don't think anyone wants or needs. |
@littledan Yes, I think it would go well as "Proposal 1b", since it's F-sharp plus some features. To be honest I think it might be the one that works for everyone. At least, for what I think you're talking about: // Basic Usage
x |> f //--> f(x)
x |> f(?) //--> f(x)
x |> f(? + 1) //--> SyntaxError
x |> f(y) //--> f(y)(x)
// 2+ Arity Usage
x |> f(?,10) //--> f(x,10)
x |> f(?,10, ?) //--> SyntaxError?
// Async Solution
x |> f |> await //--> await f(x)
x |> f |> await |> g //--> g(await f(x))
//
// Arbitrary Expressions Option 1 (nothing special)
//
f(x) |> (a => a.data) //--> f(x).data
f(x) |> (a => a['my-data']) //--> f(x)['my-data']
f(x) |> (a => ({ result: a })) //--> { result: f(x) }
//
// Arbitrary Expressions Option 2 (special casing)
//
f(x) |> .data //--> f(x).data
f(x) |> ['my-data'] //--> f(x)['my-data']
f(x) |> (a => ({ result: a })) //--> { result: f(x) }
//
// Arbitrary Expressions Option 3 (generic placeholder)
//
f(x) |> (?).data //--> f(x).data
f(x) |> (?)['my-data'] //--> f(x).data
f(x) |> { result: (?) } //--> { result: f(x) } Restricting the placeholder to function argument positions is actually useful for more than just "forward-compatibility with partial app" – it allows But it still would be compatible with a partial app proposal that might look something like this: var f = ::g(?, 10) // apply 10
var log = ::console.log // method extraction Not that it needs to be compatible technically, but it should be compatible semantically. |
This was my hope. The only downside, as @zenparsing points out, is unmotivated complexity if we don't have partial application.
I really like this option! It bridges the gap to get full expressiveness, without special cases. We can tell a simple story, that |
As @gilbert and @mAAdhaTTah point out, this appears to be not a mix proposal but rather a variation of Proposal 1: F-sharp Style Only, with no opt-in Hack Style, because it forbids arbitrary expressions… …except for the third option in #89 (comment), which appears to be the usual Proposal 4: Smart Mix but using a If that analysis is correct, I will add As for the first two options in #89 (comment), should they get their own issue, as a “Proposal 1b”? I can also add them to the wiki—though I am a priori not a fan of them, since they involve much syntactic special casing while losing much expressiveness. It should be noted that option 3 can coexist with a
This question—to what extent the placeholder in Proposals 2, 3, and 4 should resemble syntactic partial application’s |
Why would |
@ljharb Because @js-choi You're right, if // Basic Usage
x |> f //--> f(x)
x |> f((?)) //--> f(x)
x |> f(?) //--> Syntactic sugar?
x |> f((?) + 1) //--> f(x + 1)
x |> f(y) //--> f(y)(x)
// 2+ Arity Usage
x |> f((?),10) //--> f(x,10)
x |> f(?,10) //--> Syntactic sugar?
x |> f((?),10,(?)) //--> Syntax error?
// Async Solution
x |> f |> await (?) //--> await f(x)
x |> await f((?)) //--> await f(x)
x |> f |> await (?) |> g //--> g(await f(x))
// Arbitrary Expressions
f(x) |> (?).data //--> f(x).data
f(x) |> (?)['my-data'] //--> f(x).data
f(x) |> { result: (?) } //--> { result: f(x) } This seems pretty acceptable, aside from the requirement to write extra parens in |
@gilbert restricting the usage to "once per pipeline entry" seems like it'd cover that? |
@gilbert: I must be misunderstanding something about this |
I think it's close enough to being a type of "mix" that we can discuss it here.
If we want to keep open partial application's options for its choice of syntax as open as possible, that makes a decent case for F#-style, no placeholders, and defer placeholders to that proposal. I think this becomes more appealing if this syntactic change makes it feasible to write arrow functions without parens. Otherwise, I think we should specify a goal in relation to the placeholders proposal before we bikeshed the token & mixes. If we use |
I don't think we want to pin special semantics to having anything wrapped in parenthesis. |
Forward compatibility with the |
@js-choi The concern is we could end up with two different placeholder syntaxes, one within pipelines and one outside of them. @littledan suggested "we should try to make placeholders explainable as them." I'm hesitant to include a placeholder syntax at all because of this, but if we're going to, I do think we need to explicitly decide the relationship between the placeholder we're trying to ship with the pipeline and the placeholder proposal, even if that is to agree that we're willing to potentially ship 2 placeholder syntaxes (inside & outside of pipelines). |
@mAAdhaTTah: Making pipe placeholders explainable as partial-application placeholders is an understandable goal, but I don’t see how it’s possible at a fundamental level, as long as we’re talking about Hack-style pipes that support arbitrary expressions. Partial application cannot explain anything other than function calls (and method calls if the partial-application syntax will support them). It alone cannot explain arbitrary expressions such as arithmetic operators, After all, this still is a valid use case: to use partial application inside of a Hack pipe, such as (This is true even with tc39/proposal-partial-application#13; even if syntactic partial application adopted a |
Yep, partial application is a red herring with respect to pipelines. |
I agree, but I see this as an argument for F#-style rather than dropping the forward-compatibility requirement. Perhaps something like Which is not to suggest I'm opposed to the "Smart Mix", although I might reframe is as "Hack-style with inlinable unary function", which is to say |
Could you explain what you mean here? Do you think partial application as @rbuckton proposed is a bad idea, or that it would be fine to have two separate tokens here, as they are doing different things? |
If that ambiguity (when making the partial-apply placeholder and pipe placeholder both Either way, the forward-compatibility requirement is moot; it’s covered by all four proposals. For Proposals 2, 3, and 4 it merely means that pipe placeholders can’t be the same as partial-apply placeholders. And it seems to me that these two types of placeholders are fundamentally different concepts. Just look at
This particular ASCII soup is a result of mixing And, hey, even if creating functions through partial application in a pipeline were super frequent, minimizing its visual noise would just be more bikeshedding for #91. We could always go Unicode, hahaha…oh, boy. Also, hey, thanks for engaging so cordially on this. Just wanted to say that. 👍🏻 |
@gilbert I'm referring to "Hack Style" only in that comment. I've stated elsewhere in this and other discussions that I support the Smart Mix and Split Mix proposals, although my current preference is for an F# solution (with room to grow in the future) in the operator's first iteration. |
I suspect there is significant overlap between expressions you can write with Hack-style pipes that would work the same if wrapped in an arrow function, and I don't think wrapping these with a familiar syntax is a heavy burden, although doing so is definitely noisier. Quite honestly, in the two examples @js-choi has above, the F#-style seems pretty reasonable, if less desirable. That said, we should be able to bare this out with some real-world usage after we complete the babel plugins. I'm not a heavy user of async / await yet
This feels needlessly personal. Would you mind rewording? @kurtmilam Advocating for F#-style and punting off the possibility of Hack-style pipeling to a future enhancement feels like shirking our responsibility to design this feature properly the first time. If you feel Hack-style pipelining is worth including, I think you should advocate for that now, rather than deferring that off to a future change / proposal. |
Let me say I never thought you or anyone has or had ill intention. Keeping things fair does not mean "looking out for bad guys", but just making sure things are played out properly. Just like a board game, someone might "cheat" by accidentally stepping over a rule if other players don't keep them in check.
These two claims have been qualified since their original introduction, and in their current form I agree with them. |
So I just discovered a new operator in JavaScript that works today! It's called async function go (str) {
let $;
return (
$= str,
$= $ + $,
$= $.toUpperCase(),
$= Promise.resolve($),
$= await $,
$= log($),
$= `oh ${$}`
)
}
const log = (x) => { console.log(x); return x }
go("hi").then(log)
// "HIHI"
// "oh HIHI" This is working code! We might not need the Hack-style operator after all 😛 |
Except that your |
@littledan: I’ve signed the contributor agreement and started a spec repository for a Proposal 4. Other than IRC Freenode #tc39, and maybe the es-discuss mailing list, is there a more informal place I could ask for questions on Ecmarkup and for general guidance on the spec drafting? For instance, I’m currently having trouble getting Ecmarkdown to be recognized in an |
@js-choi Let's talk either on #tc39 or in issues here. Others might run into the same problems as you, so it's helpful to have an informal place. I haven't tried using ecmarkdown in an As for not cluttering up this issue: Would it make sense for @js-choi and @mAAdhaTTah to each open up issues to track progress on their separate proposals? We can have more focused discussions there. |
@littledan: Talking on IRC #tc39; thanks for replying there. To answer your question above, I’m not trying to put Grammardown or other grammar notation in an As for separate issues, I’m neutral, but it might be useful. Maybe it might be good to wait a bit until the specs and plugins are developed further. Once we continue developing the specs and plugins further, perhaps we’ll see how much discussion occurs around them. But I don’t know. |
I'm starting with the babel plugin first, and I'll open an issue about it once it's ready. |
Just a quick update, since things have quieted down here a bit: I stated working on |
To add onto @mAAdhaTTah, us two have had professional work take up most of our time this past week, but we’ve also been able to put some work into the Babel plugins and formal specifications of our respective proposals (1: F-sharp Style Only and 4: Smart Mix) in our free time, while helping each other through email. We have deliberately been working from opposite ends: @mAAdhaTTah has been working on Proposal 1’s Babel plugin first. I have been working on writing Proposal 4’s formal specification first. Many problems with Babel that @mAAdhaTTah will run into are problems that I would also run into—likewise, the problems I’d run into with the spec and with Ecmarkup would also apply to both of us. So we figured it’d be most efficient to start work from the two opposite ends (plugin and specification), then adapt each other’s work for the half we haven’t worked on. My own specification’s repository is public, as I mentioned in #89 (comment). Anyone is free to watch its progress, but it is still quite unfinished and rapidly churning. I’ve mostly figured out how Ecmarkup works, but I have been churning out an explainer and a specification together in the Markdown readme first; the Ecmarkup document will come later. I hope to have my first presentable and well-rounded explainer+specification by the end of this week, but the planning fallacy will probably strike me, so it’ll hopefully only be a little bit later… We’ll keep you posted later this week. |
An update after a week: The Babel plugin for Proposal 1 (F-sharp Only with A first-draft formal specification for Proposal 4 (Smart Mix) has been finished and uploaded to my personal website. The draft is barebones but I think it completely specifies all the basics of Proposal 4 (Smart Mix). In contrast, Proposal 4’s explainer is not yet ready; the explainer will take a few more days before it is presentable for first review. I want to completely remove the current middle section, which is both now out-of-date and redundant with the formal specification. I expect that many questions regarding the specification’s design will be answered by the finished explainer. I also want to add more real-life examples from real-life libraries to the explainer. Right now it uses code from/with Underscore, Pify, and the WHATWG Streams standards, but I also want to add examples from/with Lodash, jQuery, Flux, Cycle, RxJS, and Ramda. If you have any other suggestions for important JavaScript libraries with which to show demonstrations (both from within their implementations or from code using their public APIs), please let me know. I also still want to add some more brief informative comments to the specification and maybe even add tentative grammars for add-on proposals, like pipe functions (see issues #96 and #97), in an appendix of the specification. After rewriting the explainer and adding more real-life examples, I plan to then either move onto Proposal 4’s Babel plugin, start a formal specification for Proposal 1, or start a formal specification for Proposal 3 (Split Mix). I’ll leave another update when the situation changes further or after another week – whichever comes earliest. Thanks. |
Another update: @mAAdhaTTah and I are still collaborating, mostly via email. He's been working hard on extending Babylon (the raw parser of Babel) to allow We’ve both started to work on implementing the Babel plugin in the same repository. I myself have finally finished a detailed explainer and specification for Proposal 4: Smart Pipelines, plus several possible extensions to it. @littledan will be able to look at it in a few days, and if he has time he will prepare a presentation presenting it and Proposal 1: F-sharp Style Only to the next TC39 meeting, later this month.
Thanks for all your patience. |
@js-choi How would I go about helping out with these plugins and testing? I'm not really sure where/how to plug in. |
@jlaustill Best bet to get started is to get familiar with the specs as they currently exist. Those are linked from the README in this repository. Otherwise, I don't know as there is much you can do at this point. The next steps are to get the proposals in babel. We've landed some initial scaffolding so we can introduce new changes without introducing breaking changes. We've got a PR here for babel: babel/babel#8289 That was mostly implemented by @js-choi, and I'm currently working on shepherding that into babel proper. We're mostly waiting on reviews from the babel team there, and I'm sure they're focused on getting babel@7 out the door, so that may take some time. We're also waiting for that to land before I start the F# Pipeline parsing, then after that, the babel plugins themselves need to be implemented. Given the complexity of the Smart pipelines, I expect the rest of those pieces to move a bit faster. Given that a lot of stuff is waiting for that initial PR to land, I'm not sure there's much you can do except tell people about the proposal as it stands. I've seen a lot of great input & conversation from the community, so any additional conversations / feedback would be appreciated. Otherwise, hold tight! Things are in progress, even if it sometimes feels like nothing is happening. |
@rbuckton Would it not be relatively straightforward for the compiler to optimize away the polymorphy of
|
Not exactly, since your "optimized" version doesn't execute in the same order as @gilbert's example. That said, an optimizing compiler could optimize this case, but that assumes it would happen often enough in user code that it would be worth optimizing. |
Did I make an error in the transformation? I haven't found it after double checking. Regardless, the important point is that this kind of operation can be optimized. Thus, JavaScript has an existing operator that gets us very close to the functionality and syntax we'd expect from a Hack style pipeline operator. If this doesn't happen often enough in user code already, given the existing operator, I wonder how much sense it makes to encumber a new pipeline operator with similar functionality. |
Its the order of evaluation that differs. In @gilbert's example, the following steps are taken in order:
In your "optimized" version, the following steps are taken in a different order:
Differences in evaluation order can be observable due to side effects. |
Thanks for taking the time to spell out the differences in evaluation order between the two examples. I found that very informative. It's also a concrete reminder (for me) of one advantage of minimizing / corralling side effects and mutation. |
Closing this issue, as the proposal has advanced to stage 2 with Hack-style syntax. |
Hi all, I've just updated the wiki with the current status of all proposals so far. This thread specifically is for exploring the possibility of mixing together the two main proposals (F-sharp style and Hack style). Please visit and read that page for details.
The text was updated successfully, but these errors were encountered: