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

Open
rbuckton opened this Issue Feb 7, 2018 · 8 comments

Comments

Projects
None yet
7 participants
@rbuckton

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

charmander commented Feb 7, 2018

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

This comment has been minimized.

zenparsing commented Feb 8, 2018

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

@mAAdhaTTah

This comment has been minimized.

Contributor

mAAdhaTTah commented Feb 8, 2018

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

This comment has been minimized.

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

This comment has been minimized.

Member

jridgewell commented Feb 8, 2018

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

This comment has been minimized.

Member

littledan commented Feb 8, 2018

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment