tc39 / proposal-smart-pipelines

Old archived draft proposal for smart pipelines. Go to the new Hack-pipes proposal at js-choi/proposal-hack-pipes.
https://www.jschoi.org/18/es-smart-pipelines/spec/
MIT License
182 stars 15 forks source link

Spec: Specify Feature NP (N-ary Pipelines) #16

Closed js-choi closed 3 years ago

js-choi commented 6 years ago

The augmentations that Feature NP would apply to Feature BP have not yet been added. This issue is therefore not resolved and is blocked by #17.

dead-claudia commented 6 years ago

Thought I'd point out a couple areas of potential syntactic ambiguity in this:

By any chance, could this be better specified just in terms of an unpacked array input? Engines and transpilers could trivially eliminate the array allocation (or destructure it if they couldn't eliminate it), and it'd also make the pipeline a little less complicated to visually and programmatically parse.

[a, b, c]
|> ...f(#, x, ...)
|> ...g

// Desugaring
g(...f(a, x, b, c))

(My reason of interest in NP in general is because this would make n-ary lifting in my proposal much simpler to implement, and I've already found uses for that in many areas.)

js-choi commented 6 years ago

@isiahmeadows: Thanks for commenting on these—they are excellent points.

As far as I can tell, N-ary pipeline heads are not actually grammatically ambiguous with the current syntactic grammar for Feature NP. func(...a |> f |> g) would indeed be func((...a |> f |> g)). However, the garden-path problem is definitely real and concerning, given that avoiding garden-path sentences is a goal of this proposal. Not grammatically ambiguous but visually ambiguous, especially with potentially long pipeline-head expressions.

I’m working on #18 and #17 right now, but this is indeed a problem and I’ll have to think more about this. I don’t want to lose consistency between the syntax for pipeline heads and intermediate pipeline steps, losing which would be its own sort of complexity. But maybe there’s no alternative, and maybe it’s not as important to pipeline’s use cases like partial application…

The most likely solution is what you already have mentioned: requiring parentheses for both n-ary pipeline heads and n-ary pipeline steps and getting rid of the PipelineHead : `...` ConditionalExpression, PipelineStep : `...` ConditionalExpression, and PipelineStep : `...` productions. f((...veryLongExpression)) is a Syntax Error, after all. In that sense, f((...veryLongExpression) |> pipeline) isn’t a garden path, since the presence/absence of a following pipeline distinguishes between Syntax Error and pipe expression. In contrast, f(...veryLongExpression |> pipeline) requires lookahead for distinguishing between Syntax Error and two valid interpretations (spreading of veryLongExpression into the function call of f versus spreading of veryLongExpression into the pipeline head. (Though I’m not yet sure what you mean by specifying Feature NP “ just in terms of an unpacked array input”, as contrasted with the current approach of spreading with iterators just like with array literals and function calls.)

I’ll follow up when I finish #17 and #18#25, and I would be interested in any further ideas you come up with in the meantime.

As a parenthetical follow-up to https://github.com/tc39/proposal-pipeline-operator/issues/106#issuecomment-373455338, I am very interested in your own proposal. I think there’s a lot of potential there. I’m still chewing over it as I work out the formal specifications of the remaining additional features, because it’s a big problem. I’ll reply in your new issue #24 when I can. I’m happy you’re interested in mine too.

dead-claudia commented 6 years ago

(Though I’m not yet sure what you mean by specifying Feature NP “ just in terms of an unpacked array input”, as contrasted with the current approach of spreading with iterators just like with array literals and function calls.)

I probably should've said "unpacked arguments list". The trick is that instead of specifying the pipeline itself to be n-ary, you could just choose to consider individual calls n-ary.

// 1.
array
|> ...foo(...)
|> bar(#)

// Desugars to:
bar(foo(...array))

// 2.
array
|> foo(#)
|> ...bar(...)

// Desugars to:
bar(...foo(array))

This makes the syntax a little more flexible on this front without complicating it too much.

There's one added caveat that probably needs addressed: you could potentially define a pipeline like this:

// Is this legal?
array |> ... ...

I suspect it shouldn't be, since you can't destructure into nothingness. It's morally equivalent to ...array as an "expression", which isn't possible within the current semantics of spreading or destructuring. However, this is legal and makes perfect sense with the semantics (although it's completely and totally useless):

array |> ...[...]

// Desugars to
[...array]