Skip to content

Tricky case for uncurried by default when . appears in non head positions #5792

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

Closed
bobzhang opened this issue Oct 26, 2022 · 14 comments
Closed
Milestone

Comments

@bobzhang
Copy link
Member

let f = (x, .y, z) => x +y + z

. should be only allowed in the first position (. x , y) => x + y

@mununki
Copy link
Member

mununki commented Oct 26, 2022

How about "res.async"?
Worth considering how to handle "bs" and "res.async" respectively.

Maybe we can parse it (x) => (. y, z) => ...?

@mununki
Copy link
Member

mununki commented Oct 26, 2022

Had some related exploring rescript-lang/syntax#683

@bobzhang
Copy link
Member Author

The idea is that by reading (, (. you know that the function is curried / uncurried, so that we can toggle the default in the future. cc @cristianoc

@cristianoc
Copy link
Collaborator

cristianoc commented Oct 26, 2022

I think this comment is about what happens in a single parenthesised thing ( ... ) which currently mixes curried and uncurried intentions. Instead, it's more clear if each parenthesised group defines either an uncurried or a curried function, so one can just flip the interpretation later on.

Not sure if disallowing . in non-first position any different than formatting it into first-position as #5792 suggests. If (x, .y, z) => x +y + z parses to the same AST as (x) => (. y, z) => x+y+z does, then effectively it's as if the first syntax did not exist in practice (and will in fact disappear after formatting). I guess experimenting with that parsing, and printing, does not hurt. If it does not work, one still has the option to disallow (x, .y, z) => x +y + z.

@cristianoc cristianoc added this to the v11.0 milestone Oct 26, 2022
@cristianoc
Copy link
Collaborator

Added this under the overall uncurried by default issue #5597

@IwanKaramazow
Copy link

IwanKaramazow commented Oct 28, 2022

I recently took care of the printing side in rescript-lang/syntax#668.

Do we have the budget to implement this breaking change right now? (Budget as in release-wise, implementation effort is a 5min change)

@cristianoc
Copy link
Collaborator

This seems part of rescript-lang/syntax#683, which is for v11.
Hopefully we don't need to break anything if it can be transformed at parsing time.

@cristianoc
Copy link
Collaborator

Actually this let f = (x, .y, z) => x + y + z is already formatted as let f = x => (. y, z) => x + y + z right now.

I think the issue is something else: that (.x) => (y) => 12 becomes (. x, y) => 12, so that's not a way to express the intention of a unary uncurried function returning a curried function.
However, notice instead that (.x) => { y => 12 } stays (.x) => {y => 12}

I thought that the original proposal was only to ban (x, .y, z) => x + y + z, but equally one would have to ban x => (.y) => z => x + y + z.

Assuming this stronger ban is the intention, one could instead:

  • parse (x, .y, z) => x + y + z to x => { (.y, z) => x + y + z }, and
  • parse x => (.y) => z => x + y + z in the same way.

With that choice, assuming current curried by default mode is C, and the new uncurried by default mode is U, the conversion from C to U would turn (x, .y, z) => x + y + z into (. x) => { (y, z) => x + y + z }.
CC @mattdamon108

@cristianoc
Copy link
Collaborator

Similarly, (.x) => (.y) => 3 currently formats to itself, which is problematic when one wants to flip the switch.
Instead, it could be parsed as (.x) => { (.y) => 3 }.

@cristianoc
Copy link
Collaborator

There are also some issues one cannot easily get around without telling the parser whether it is in curried or uncurried mode.
For example, when in uncurried mode (x) => (y) => 3 is 2 nested uncurried functions, and one cannot format it to (x, y) => 3.

Without a distinction between uncurried and curried mode, one would have to be safe and format (x) => (y) = 3 to (x) => { (y) => 3 }.

@bobzhang
Copy link
Member Author

bobzhang commented Nov 2, 2022

I suggest we have a simple and clean semantics of curring/uncurring in the stage as early as possible. Depending on the ppx transformation is not reliable, the formatter is also hard to get it right.

To make the formatter easier to write, here is the encoding I proposed that we should do it in the parsing stage instead of relying on the later transformations.
https://docs.google.com/document/d/1St30rQ2YqzW1M2vAkdrK-qJK-2u-xLutJ1Z4ZfS8g5s/edit

The current workflow is that we parse it as curried call with [@bs] attribute and running ppx transformation to turn it to the encoding mentioned above, my proposal is that parse it directly to the encoding documented as above. The benefit is that the formatter is more robust and honest to our encoding, since it does not rely on the attribute but on a dedicated syntax (record here)

@cristianoc
Copy link
Collaborator

Added an issue for this: #5793

@cristianoc
Copy link
Collaborator

@cristianoc cristianoc transferred this issue from rescript-lang/syntax Nov 10, 2022
@cristianoc
Copy link
Collaborator

cristianoc commented Nov 12, 2022

Summary of the issue.

First, the meaning of switching between normal mode and uncurried mode.
If (x,y)=>3 is in normal mode, then flipping all the elements would give uncurried (.x, .y) => 3 which is redundant because in uncurried mode, there's no need to repeat the dot for each element. Instead, uncurried (.x, y) => 3 is the expected form.

So the idea is to only flip the first element in a parens group.
But flipping only the first element does not make sense for the problematic example
(x, .y, z) => x + y + z.
In fact, this behaviour, of changing mode inside parens, only belongs to normal mode. There is no such concept in uncurried mode.
However, notice that normal mode (x, .y, z) => x + y + z desugars to x => (.y, z) => x + y + z, and this other form can be flipped to obtain uncurried (.x) => (y, z) => x + y + z.

So one could forbid the problematic example, as the flipping rule does not give the desired result. However, flipping after formatting does. So it's not necessary to forbid it.

In summary, if a term does not have dots in the middle then it can be converted to uncurried form by flipping.
If it does have dots in the middle, it can be converted to uncurried mode by formatting then flipping. The formatter can do that for you.
See examples in #5796

@cristianoc cristianoc changed the title Report error when . appears in non head positions Tricky case for uncured by default when . appears in non head positions Nov 12, 2022
@cristianoc cristianoc changed the title Tricky case for uncured by default when . appears in non head positions Tricky case for uncurried by default when . appears in non head positions Nov 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants