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

Partial expressions #186

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

Partial expressions #186

kosich opened this issue May 1, 2021 · 5 comments

Comments

@kosich
Copy link

kosich commented May 1, 2021

To prove minimal style extensibility (#167) and explore our options in Hack vs minimal argument, I created a babel plugin to support an experimental syntax of partial expressions. It could cover most of Hack style use cases, while allowing F# style and being useful on its own.

This syntax turns an expression with one missing argument into a function with a single argument. For example:

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

NOTE: The plugin uses _ as a placeholder for a value, because it's hard to override syntax in babel. In reality, it could be #, ? or something else.

tl;dr: you can try the syntax in the babel playground.

With minimal style pipes

I tried to cover two special cases for pipes:

When the partial expression is in the subject of piping:

let fn = _ |> increment |> console.log;
// equals to
let fn = x => (x |> increment |> console.log);

And when it's in a pipe:

let value = 42 |> _ + 1 |> console.log;
// equals to
let value = 42 |> (x => x + 1) |> console.log;

.

Similarly, we could do partial application:

42 |> console.log(_, '!');
// equals to
42 |> (x => console.log(x, '!'));

And as a context:

42 |> _.toString(16) |> _.toUpperCase();
// equals to
42 |> (x => x.toString(16)) |> (x => x.toUpperCase());

Rules

  1. Expression is considered partial if an unbound _ symbol is used in it.

  2. Expression boudaries are limited by following:

    2.1 root of an expression

    2.2 body of a function

    2.3 pipe in pipeline operator

What are the downsides?

  • it might be hard to see the root of a partial expression
  • while visually plain in simple cases, nested partial expressions might not that easy to understand
  • it might be expensive to parse
  • it doesn't support Hack style await _ syntax in pipes

We can add implicit indication of partial expression borders to solve first three issues. Though I'm not sure if the syntax will still be beneficial, compared to simple arrow functions.

Why not partial application?

Partial application seem to be solving a very narrow issue, while if we extend that idea to partial expressions — we could cover much more use cases, and it's especially sweet in the context of pipes.

Why not just Hack style?

Compared to Hack style, partial expressions:

  • are useful on their own and can be used anywhere in JS
  • allow using both minimal and Hack-ish style

Previous work

@trustedtomato made a proposal for partial expressions in 2018, probably with similar intentions, though it seems to be stalled.

UPD: @citycide has been exploring a similar syntax for a few years in the param.macro (also can be used as a babel plugin)

Because the idea is quite simple, I suspect there were more discussions / experiments with it.

Links

Play with it in the babel playground

And here's the plugin repository


Thanks 🙂

@Pokute
Copy link
Contributor

Pokute commented May 1, 2021

Very nice! While I this might not be my preferred choice, having more alternatives explored is indeed a good thing.

The babel plugin is also nice to have! I does have some bugs tho (42 |> console.log(_, _)) and could be polished further, but a lot of exploration can be done as it is.

@haltcase
Copy link

haltcase commented May 1, 2021

This is something I have been exploring for a few years in param.macro, which can also be used as a babel plugin (though it's designed for babel-plugin-macros).

@ducaale
Copy link

ducaale commented May 1, 2021

it doesn't support Hack style await _ syntax in pipes

IMHO, this is the biggest downside of the ones that were listed. F# flavour of the pipeline operator can await promises but it is a bit verbose.

I wonder how things would have looked like if there was a pipline operator specific for promises and null/undefined values.

promise >>= map(_ * 2) >>= filter(_ > 10);
[1,2,3] |> _[10] >>= _ + 10;

@kosich
Copy link
Author

kosich commented May 1, 2021

@ducaale , F# has suggestions for await postfix, like promise |> service.call await |> ... which is kind of similar to Hack Though I haven't thought deeply about it and was lazy to integrate it with the _ syntax 😊

Yeah, some kind of custom syntax would be amazing! I think, @Jopie64 suggested something similar in #167 (comment) .

doubtful and off-topic imho on await and pipes

With that said, imho the importance of await in pipes is a bit overrated. In my view pipelines solve two issues:

  • proper ordering in code value modifiers: a |> b |> c instead of c(b(a)))
  • and easing use of 3rd party utilities, like: [1,2,3] |> lodash.sort() |> ix.first(3)

In this context, promise.then is an always available built-in pipeline for promises (surely being more verbose).
So, in my view await is important, but not crucial.

Still, it'd be cool if we could achieve that too!

@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 12, 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

5 participants