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

Babel plugin #9

Open
kosich opened this issue May 1, 2021 · 5 comments
Open

Babel plugin #9

kosich opened this issue May 1, 2021 · 5 comments

Comments

@kosich
Copy link

kosich commented May 1, 2021

Hey, great work on the proposal!

I created a babel plugin with simpler rules ("simpler", not "better"!), exploring no prefix partial expressions:

let bang = _ + '!';
// equals to
let bang = x => x + '!';

You can play with it in the babel playground.

And here's the plugin repository where I described some rules and how it integrates with the pipeline operator.

Maybe it can help pushing the proposal further.

@trustedtomato
Copy link
Owner

Neeeat! I'm not quite sure how it handles brackets though. I think [1, 2, 3].map(_ + 1) should become [1, 2, 3].map((_temp) => _temp + 1) and not (_temp) => ([1, 2, 3].map(_temp + 1)).

I think one thing you could do is to bubble up the _ to the nearest (not-function-calling) grouping, so that [1, 2, 3].map((_ + 1)) could become [1, 2, 3].map((_temp) => _temp + 1). It definitely feels a bit quirky at first, but at second glance, it's pretty robust. It would even work neatly with pipelines: const addOne = (_ |> (1 + _)) would become const addOne = (_temp) => 1 + _temp (note that the mentioned code currently results in an error).

What do you think?

@kosich
Copy link
Author

kosich commented May 11, 2021

Heya, glad you liked it!

Yeah, I think constructs like [1, 2, 3].map(_ + 1) should definitely be supported!

Though, with a nearest non-calling group bubbling there might be a problem, as [1, 2, 3].map( 2 * (20 + _) ) wont bubble high enough. One possible solution is to use explicit marking, where we want _ to bubble to (as you originally proposed).

Without changing the babel's syntax parsing (which I think is more troublesome), we could try the {} marking (as discussed in #5 and if we won't find more issues with it).

Also, currently the plugin handles the pipes |> as a special case. With explicit markings, we might or might not want continue doing that. E.g.:

// special case, implicit grouping
let a = 42 |> _ + 1; // ≈ 42 + 1;
typeof a; // 'number';

// no special case, explicitly placing bubble point:
let a = 42 |> { _ + 1 }; // ≈ 42 + 1;
typeof a; // 'number';

// no special case, bubbling to the top of the expression
let a = 42 |> _ + 1; // ≈ x => (x + 1)(42);
typeof a; // 'function';
a('param'); // runtime error

// no special case, no implicit bubbling
let a = 42 |> _ + 1; // parsing error

.

note that the mentioned code currently results in an error

Great catch! I'll have to check that out, thx!
I was a bit lazy to add specs, but if we get serious, we should add proper tests.

.

Please, share your thoughts.

@trustedtomato
Copy link
Owner

I think not having special cases is a better starting position. I think if something can be added later and it makes sense to add it later, it should be added later. So I believe that having no bubbling and implicit grouping would be the best at first.

@kosich
Copy link
Author

kosich commented May 13, 2021

I was wrong, let a = { _ + 1 } is surely an invalid JS construct that fails at parse time (idk why I thought it would work).
So if we want a marker, seems like we'll need to alter babel's parser.

@kosich
Copy link
Author

kosich commented May 14, 2021

Since {} turned me down, I needed some other valid syntax to mark partial expression's root. So, I had to apply a bit of magic. Bit magic, to be exact. I decided to try overriding bitwise not ~ unary operator:

let a = ~ _ + 1;

similar to your proposed ^ syntax, isn't it?

And after a while, I think it works:

a = ~ _;
assert(a(42) === 42);

a = ~ _ + 1;
assert(a(41) === 42);

a = ~ _(40) + 1;
assert(a(inc) === 42);

a = [1,2,3].map(~ _ + 1).join();
assert(a === '2,3,4');

a = [1,2,3].filter(~ _ % 2).map(~ _ + 1).join()
assert(a === '2,4');

~ mark is now required everywhere, except for pipe RHS:

// in pipe LHS, the ~ marker is required
a = ~ _ |> inc
assert(a(42) === 43)

// without a marker babel will throw
a = _ |> inc // INVALID: _ is unbound

// RHS can have explicit or implicit grouping
a = 40 |> ~ _ + 1 |> _ + 1
assert(a === 42)

Here is the babel playground with new syntax.

Please, note that I haven't covered or tested multiple placeholders, nested ~ groups, async/await, and ~ w/o a placeholder. Which might be ok for current stage and is probably better to implement with parser override.

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

2 participants