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 #46

Closed
kosich opened this issue Apr 24, 2021 · 10 comments
Closed

Partial expressions #46

kosich opened this issue Apr 24, 2021 · 10 comments

Comments

@kosich
Copy link

kosich commented Apr 24, 2021

Hi, a great proposal, thanks for working on it!

I wanted to discuss the partial expressions option that is partially related to threads in #13, #20, and #23.

Suggestion:
If we could extend ? syntax to be allowed inside any expression — we might improve readability in mapping, applying, and piping. It would support currently proposed syntax (with a nuance in nested calls) as well as new applications. The idea is that ? would bubble to the top of an expression, making it a function with one or many parameters and with that expression as returned value (no eager evaluation). And, I think this doesn't need any additional sigil prefix.

let a = ? + 42;
// to equal
let a = x => x + 42;

Please, see more examples and doubts review below


What do you think?
Thanks 🙂

@kosich
Copy link
Author

kosich commented Apr 24, 2021

Examples:

Nothing changes in current syntax / behavior (with one level of call nesting, more on that later):

// partial application
let a = Math.max(?, 42);
let a = x => Math.max(x, 42);

// string templates
let b = `The answer is ${ ? }`;
let b = x => `The answer is ${ x }`;

As call context (related to @rbuckton suggestion in #23):

// NOTE: even ?. should not conflict in syntax w/ optional chaining, though might be confusing w/o parenthesis
let b = (?).foo(42);
let b = x => x.foo(42);

// in piping
42 |> ?.toString();
42 |> x => x.toString();

As a partial expression:

// simple case
let a = ? + 42;
let a = x => x + 42;

// in mapping
[1,2,3].map(? + 42);
[1,2,3].map(x => x + 42);

// in piping
[1,2,3]|> ? + 42;
(x => x + 42) ([1,2,3]);

and a similar behavior to @trustedtomato suggestion in #20 :

let id = ?;
let id = x => x;

let add = ? + ?;
let add = (x1, x2) => x1 + x2;

@kosich
Copy link
Author

kosich commented Apr 24, 2021

Doubts:

I. Syntax

I think, this syntax won't conflict with neither elvis operator 'a? b : c, nor with optional chaining a?.b. But I might be missing something. Maybe in case of conflicts we could use other sigil, like #`.

Also, the borders of the expression might not be obvious enough. More on this in next doubt:

II. Nested expressions and the border line:

Should this expression

let point = z => y => z + y + ?;

equal to point = x => z => y => z + y + x or to point = z => y => x => z + y + x? I'd prefer latter: function body is a borderline for a partial expression. But not sure if this behavior will be easy to define and (importantly) understand.

And it get's less obvious with nested fn calls:

let value = foo(bar(?, 42));

should it equal to value = foo(x => bar(x, 42)); or value = x => foo(bar(x, 42));? When talking about partial expression, latter makes more sense for me, as it raises to the top of the expression. Though it contradicts current proposal in nested partial application.

III. Curried partial expressions:

There are discussions around curried syntax for functions, so another question is should there be a syntax for curried partial expression and how to define it, so that ? + ? would generate x1 => x2 => x1 + x2, instead of (x1, x2) => x1 + x2.

IV. Extending this proposal might slow it down:

This is a huge change, and I don't want to slow the current proposal down. A separate proposal with different syntax for such partial expressions can be initiated (since there's probably a conflict in nested fn calls). And @trustedtomato has created a sigil-based one, though it seems to be stalled atm: https://github.com/trustedtomato/proposal-partial-expression.

If these "partial expressions" are worth exploring, should we limit the current proposal to partially applied functions only and restrict template strings from allowing ? for now? As it might conflict with partial expressions.

@dead-claudia
Copy link

I like the idea, but id = ? conflicts with your toString = (?).toString() - you should pick one and stick with it, or reject it altogether.

You should also be sure to constrain ExpressionStatement to include a [lookahead ∉ `?`] (similar to what it already does to avoid a conflict between block statements and object literals) to ensure it doesn't conflict with optional chaining.

let foo = i
?.foo()

I do have a suggestion: look into the possibility of using a separate grammar for partial terms, one that mostly aligns with expressions but deliberately doesn't satisfy the RHS grammar (as it doesn't evaluate to a value), and switching relevant constructs where useful to use that instead. (You could also make much better tradeoffs like potentially requiring parentheses for certain things like binary operations, and you could even look into things like operator functions like value.reduce(+, 0) rather than relying on purely ? to make everything work.)

@pepkin88
Copy link

pepkin88 commented Oct 10, 2021

(You could also make much better tradeoffs like potentially requiring parentheses for certain things like binary operations, and you could even look into things like operator functions like value.reduce(+, 0) rather than relying on purely ? to make everything work.)

This looks like what LiveScript has with its operators: https://livescript.net/#operators-partial
So in LiveScript you can turn an operator to a (partially applied) function, e.g.: + -> (+) or (+ 1). Basically any unary or binary operator have this feature.
It works also for property access: (.a.b = c) would be the same as JS _ => _.a.b = c; (obj.) is like _ => obj[_].
There is also a typical partial application, restricted only to function calls: obj.method(?, 1, ?) (they have _ as the placeholder though).

I think it would look good in JS. It doesn't introduce another placeholder symbol, in my opinion would be easily understood: if an operator lacks its operand, then it's a function awaiting that operand. Personally, I'd love to see something like this in JS, although I'm not that hopeful.

@ducaale
Copy link

ducaale commented Oct 10, 2021

This looks like what LiveScript has with its operators: https://livescript.net/#operators-partial
So in LiveScript you can turn an operator to a (partially applied) function, e.g.: + -> (+) or (+ 1).

@pepkin88 although not as versatile as livescript, I think the Functional operator propsal would fill that role.

@rbuckton
Copy link
Collaborator

Partial expressions require lazy evaluation (similar to an arrow function), while partial application uses eager evaluation (like f.bind()). I believe arrow functions are succinct enough for lazy evaluation, and have no plans to support arbitrary expressions for partial application.

I have been investigating functional operators similar to LiveScript's partial operators, but that will likely come as a later proposal.

@shaedrich
Copy link

As call context (related to @rbuckton suggestion in #23):

// NOTE: even ?. should not conflict in syntax w/ optional chaining, though might be confusing w/o parenthesis
let b = (?).foo(42);
let b = x => x.foo(42);

One non-partial expression ? case considered, two more to go: ?? and ?:

let b = (?)?.foo(42) ?: 'foo'

Quite a few ?—assuming, JS itself is not confused, the reader, especially beginners, might be

@lazarljubenovic
Copy link

Beginners are confused by every single character.

@shaedrich
Copy link

Beginners are confused by every single character.

@lazarljubenovic By some less, by some more—we should be striving for less

@lazarljubenovic
Copy link

We can't use this argument for every new feature by coming up with the most contrived example and then slapping a "but beginners...!" on it.

Beginners are confused by the distinction between commas and semi-colons. Beginners are confused by JSON being fine with double quotes but not single quotes. Beginners are confused by { sometimes being start of object, sometimes start of block. Beginners are confused by the difference between git and GitHub.

Beginners are confused by everything, all the time, everywhere. I'm confused by Rust. I'm confused by Polish. I'm confused by musical notation. I'm confused by the way heart works.

There's nothing that won't be confusing to beginners. But beginners will learn and move on. The focus should be if it's confusing for experts, so we don't end up in a situation where there's nobody to teach beginners.

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

7 participants