Skip to content
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

F#-style addition, lightweight "inline" arrows #93

Closed
rbuckton opened this issue Feb 7, 2018 · 9 comments
Closed

F#-style addition, lightweight "inline" arrows #93

rbuckton opened this issue Feb 7, 2018 · 9 comments

Comments

@rbuckton
Copy link
Collaborator

rbuckton commented Feb 7, 2018

This idea is based on the example in the comment here: #89 (comment). Note: In the comment, ■ is used as a token placeholder to avoid bikehshedding on the actual token.

x
  |> ■ + 3
  |> ■ * 2 + await f(■)
  |> await ■
  |> g(■)
  |> await ■
  |> ■[Symbol.iterator]
  |> ■ instanceof Function

While some parts of this example could be converted into arrow-function syntax for the F# approach, we cannot concisely move await (or yield) expressions into a different function, since the context for the await is the current function.

One option might be to introduce a lightweight "inline" arrow syntax using -> that does not actually create a function object, must be immediately invoked as if it were a function. Since it is not actually another function body, we could use await (or yield) in the body of the inline arrow. For example, using this approach we could rewrite the above example using F# style pipes like this:

x
  |> ($ -> $ + 3)
  |> ($ -> $ * 2 + await f($))
  |> ($ -> await $)
  |> g
  |> ($ -> await $)
  |> ($ -> $[Symbol.iterator])
  |> ($ -> $ instanceof Function)

While this could be limited to the RHS of |>, it could theoretically be expanded to be used in any expression: (a -> a * 2 + await f(a))(x).

The grammar could be something like the following (though more time needs to be spent investigating necessary cover grammars):

InlineArrow[Yield, Await] :
  ArrowParameters[?Yield, ?Await] [no LineTerminator here] `->` [lookahead ≠ `{`] AssignmentExpression[+In, ?Yield, ?Await]

ParenthesizedExpression[Yield, Await] :
  ...
  `(` InlineArrow[?Yield, ?Await] `)` Arguments

PipelineExpression[In, Yield, Await]:
  ...
  PipelineExpression[?In, ?Yield, ?Await] `|>` `(` InlineArrow[?Yield, ?Await] `)`
@ljharb
Copy link
Member

ljharb commented Feb 7, 2018

I'm not sure how you'd enforce "must be immediately invoked" - but using a single -> expression as the top-level RHS of a pipeline might be an interesting way to avoid bikeshedding on the token entirely.

@rbuckton
Copy link
Collaborator Author

rbuckton commented Feb 7, 2018

@ljharb yeah, I have to think about that a bit more as well. One possibility is parsing restrictions, allowing it only as `|>` `(` ThinArrow `)` and `(` ThinArrow `)` Arguments

@charmander
Copy link

An alternative: |> identifier => expression would make identifier => expression look like a function, which it nearly is. |> _ => await _ would be a bit strange, but still unambiguous. It also solves the problem of allowing arrow functions to appear unparenthesized.

@zenparsing
Copy link
Member

I would be uncomfortable using -> for something whose difference with => is so subtle.

@mAAdhaTTah
Copy link
Collaborator

I'm also not in love with introducing pipeline-only syntax; part of what I love about the current F#-style proposal is how little new syntax it introduces, although that does make it tough to solve the "await problem".

@rbuckton
Copy link
Collaborator Author

rbuckton commented Feb 8, 2018

|> _ => await _ would be a bit strange, but still unambiguous.

This wouldn't be valid outside of |> since the arrow is not async, and would lead to confusion. The reason I proposed -> is to avoid that confusion.

You can think of -> as a way to create a lexical binding that is scoped to the expression to the right of the ->, e.g.:

let x = ...;
let y = (a -> a * 2 + f(a))(x)

In general its the same behavior as (a => a * 2 + f(a))(x), except where await and yield are concerned and that it doesn't add to the call stack and doesn't allocate a function object.

@jridgewell
Copy link
Member

I'm like 70-30 against. -> is very close to being =>, and I think it's going to lead to confusion about which has which semantics. I imagine beginners are going to get very annoyed because they want a closure (=>), but think it'll immediately be able to use await. It'll be a syntax error at least.

Additionally, I don't see much benefit for this compared to just using fat-arrows. We still have to type the (x -> x) characters at least. If we special case await, then I see no benefit.

@littledan
Copy link
Member

I'd prefer if we can minimize the number of tokens we introduce to make the feature set easier to learn. From that perspective, the optimal solution would be one token for pipeline, and one token for partial application and/or pipeline placeholder.

@tabatkins
Copy link
Collaborator

Closing this issue, as the proposal has advanced to stage 2 with Hack-style syntax.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants