This repository was archived by the owner on Jan 26, 2022. It is now read-only.
Draft a version that uses braces to distinguish pipe styles #31
Labels
explainer/readme
About readme.md
@littledan has asked whether there is any way to reconcile the F#-style-only proposal edited by @mAAdhaTTah with this F#-plus-Hack-style “smart-mix” proposal and make the latter a superset of the former. It seems likely that presenting the pipe operator to TC39 incrementally might be more likely to earn consensus for approval. That is, it might be desirable to present the simpler F#-style-only pipe proposal first, then, assuming TC39 accepts it, later also proposing a Hack-style smart-mix pipe proposal as an extension.
The obvious way to combine F# style with Hack style into a “smart mix” is to rely on the presence of the topic operator to determine which style a pipe uses. But I am still quite afraid of this from a human usability perspective; it’s a mode error waiting to happen. I suspect that it would be easy for human developers to miss the presence or absence of the topic operator, requiring careful scanning of the entire pipe expression to ensure its meaning, and undermining syntactic locality and semantic clarity.
That is why the current smart-pipe proposal restricts its tacit F# mode to identifiers. However, this choice breaks compatibility with @mAAdhaTTah’s F#-only proposal.
Another common concern people have expressed about the current smart-pipe proposal is that it disallows tacit use of metafunctions. An example of such concern may be found at tc39/proposal-pipeline-operator#134 (comment). Under the current smart-pipe proposal, using autocurried metafunctions would require explicit use of the topic reference. For instance, given
divideBy
,powerOf
, andmultiplyBy
(unary functions that create unary functions), and assuming the current smart-pipe proposal using%
for the topic, a developer would have to write code like this:It might be true that there are general issues with autocurrying in variadic-functional languages. And the idiomaticity of autocurrying in the APIs of JavaScript, the DOM, Node, etc. might be questionable. But it’s still very much worth figuring out whether the autocurrying use case can be made easier.
Most importantly, it is worth carefully thinking about whether backward compatibility can be achieved with an F#-only proposal, while avoiding human-usability hazards.
The answer is that yes: There is at least one alternative way to reconcile the two piping styles with one common operator: by marking placeholder expressions with brace-delimited blocks. Object literals can’t ever be functions, so this would be visually unambiguous…at least less so than relying on the presence of
%
.In this case,
|>
would have tighter precedence than binary (and perhaps prefix/postfix) operators—basically functioning as a slightly looser member access—such that these are equivalent:Phase One
In the initial proposal, tacit function application would look like this (assuming F# style):
With this initial proposal, the example in tc39/proposal-pipeline-operator#134 (comment) could use the tacit syntax, because it doesn’t use braces:
Phase Two
A follow-up proposal for placeholders would look like this:
Like with the current smart-pipe proposal, braces with placeholders would allow Elixir-style first-argument function calls, but they would be explicit, not tacit. The Elixir-style example from this comment by @zenparsing would look like:
Phase Three
If we eventually get to this point, I hope that tacit pipe functions, like those in Additional Feature PF, could eventually be considered by TC39 too. They would be compatible with using braces for placeholders, with just one additional operator:
Phase Four
And, assuming that Phase Three is adopted, then N-ary pipes (Additional Feature NP) would make them even more useful.
Questions
Some problems with braces would be that:
Braces would make it more difficult to create object literals, which would require nested grouping operators like with
x|>{{a: %, b}}
. But this would not be unique to pipes. Arrow functions already do something similar withx => ({a: x, b})
.Braces may make developers think that they could add statements into the braces, since they look like regular blocks. Developers might expect
x|>{ console.log(%); % + 1 }
to just work, but it would be a syntax error. This might occasionally be annoying but at least the error is an early error. And ifdo
expressions ever get accepted then placeholder pipes could be extended to support statement lists withdo
-like semantics.Other questions include:
How tight should the precedence be? Consider the following examples, which assume that the operator’s tightness is between the binary and unary operators:
If the operator is tightened to between unary operators and method access, then the examples become:
If the operator is further tightened to have the same precedence as member access, then they become:
Which tradeoffs would generally be best?
Would
->
be a better choice for the operator than|>
? Other programming languages use->
as a very tight operator for member access or related concepts, such as with C++’spointer->MemberFunction()
. If a tight precedence is chosen for the pipe operator, especially if it's equally or nearly as tight as method access, then->
might be better than|>
at conveying the analogy between member access and function/expression application.Should the tacit syntax favor F# style (as im the examples above) or Elixir style? It is probably a fundamental trade off that both tacit Elixir style and tacit F# style cannot be equally accommodated by the same operator. The example in Where does the F# and Smart proposal conflict? Let's split them up into two proposals, one predicate on the other. proposal-pipeline-operator#134 (comment) would be the following if the tacit syntax favored F# style:
…but would change to the following if the syntax favored Elixir style:
And Consider Elixir-style pipeline as one of the blessed alternatives proposal-pipeline-operator#143 (comment) would be the following if the tacit syntax favored F# style:
…and if it favored Elixir style:
I don’t know what I’d call this brace-using idea, but I think I might already like it more than the current smart mix proposal, even if it’s slightly chunkier.
And it would automatically be a superset of @mAAdhaTTah’s current F#-only proposal (as long as the proposal tweaks its precedence and adds two early errors for cases that would never naturally occur in F# style alone anyway).
The text was updated successfully, but these errors were encountered: