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

Replacing the # symbol #5

Open
trustedtomato opened this issue Dec 27, 2017 · 10 comments
Open

Replacing the # symbol #5

trustedtomato opened this issue Dec 27, 2017 · 10 comments

Comments

@trustedtomato
Copy link
Owner

Private fields in the Class field declarations proposal does seem to conflict with the # symbol, and it's in Stage 3. So the # should be replaced with a symbol which is one of these: !@#$%^&*()_+-=[]{};:'",<.>/?

Pointed out by Isiah Meadows & Sebastian Cholewa.

@trustedtomato
Copy link
Owner Author

My idea is to replace it with \ (backslash). Backslash is illegal outside string literals, so it must be good. And this proposal is similar to De Bruijn indices, which is for representing λ calculus, and λ looks similar to \.
Another idea is to use @, since it's partial application.
But I think \ creates less noise, so I prefer that one.

Examples using \:

[1,2,3,4,5,6,7,8,9].map(\1+?); // partially apply an operator
//=> [2,3,4,5,6,7,8,9,10]

[1,2,3,4,5,6,7,8,9].reduce(\?+?); // use an operator as a function
//=> 45

['cat', 'coconut', 'carrot', 'dog', 'sun'].filter(\?.startsWith('c')); // partially apply a function/method
//=> ['cat', 'coconut', 'carrot'] 

Examples using @:

[1,2,3,4,5,6,7,8,9].map(@1+?); // partially apply an operator
//=> [2,3,4,5,6,7,8,9,10]

[1,2,3,4,5,6,7,8,9].reduce(@?+?); // use an operator as a function
//=> 45

['cat', 'coconut', 'carrot', 'dog', 'sun'].filter(@?.startsWith('c')); // partially apply a function/method
//=> ['cat', 'coconut', 'carrot'] 

@trustedtomato
Copy link
Owner Author

The backslash \ is not good.
Example:

const u0061 = 'donut';
const a = 'apple';
x = \u0061;

Is x equal to 'apple' or function(){ return 'donut'; }?
Note: it's ambiguous.
See Mathias Bynens' article about valid variable names.

@kosich
Copy link

kosich commented May 1, 2021

For a moment of scrolling I had a hope that \ 1 + ? would work 🙂

What do you think of simple {}, as in:

[1, 2, 3].map({ 1 + ? })

@trustedtomato
Copy link
Owner Author

trustedtomato commented May 7, 2021

Hey!:)

I think the {} syntax wouldn't work just like that because of the object property value shorthand. For example, ({ x }) would be ambiguous: would that mean () => x or ({ x: x })?

About \ 1 + ?: although I definitely think it would be cool, it's not a common in JavaScript to use space as part of the syntax. For example, a developer who is not very familiar with the syntax might write \ 1 + ? as \1 + ? and bam, invalid escape sequence.

Combining those could work, namely using something like \{} as in:

[1, 2, 3].map(\{ 1 + ? })

But well, that's ugly.

I think that out of all of these, \[space] seems to be the best option. The invalid escape sequence error would be pretty easy to debug, and the code would be readable, too. I don't think many people use escape sequences as variable names (why would they?) so these wouldn't be very confusing:

[1, 2, 3].map(\ 1 + ?) // => [1, 2, 3].map((x) => 1 + x)
[1, 2, 3].map(\ add(1, ?)) // => [1, 2, 3].map((x) => add(1, x))
[1, 2, 3].map(\ ?.toString()) // => [1, 2, 3].map((x) => x.toString())

Edit: I've removed the comment's precedence part since that already came up in #3, just didn't remember.

@trustedtomato
Copy link
Owner Author

Or a binary-only operator could be used instead as syntactic marker (like ^), which is a lot better since it wouldn't require space after it. The examples in my previous comment using this syntax are:

[1, 2, 3].map(^1+?) // => [1, 2, 3].map((x) => 1 + x)
[1, 2, 3].map(^add(1, ?)) // => [1, 2, 3].map((x) => add(1, x))
[1, 2, 3].map(^?.toString()) // => [1, 2, 3].map((x) => x.toString())

@kosich
Copy link

kosich commented May 11, 2021

Yeah, \[space] and ^ are cool options! (with significant space being bit odd 🙂)

.

Regarding {}, I thought if we enforce {} grouping to have a sigil in it — we could overcome the ambiguity. E.g.:

let a = { x }; // Object
let a = { x + ? }; // Partial expression, ≈ n => x + n
let a = {{ x: ? }}; // Partial expression, ≈ n => ({ x: n })

// therefore

[1,2,3].map({ x }); // mapping with an object, runtime error
[1,2,3].map({ x + ? }); // mapping with partial expression, ≈ [x + 1, x + 2, x + 3]
[1,2,3].map({{ x: ? }}); // mapping to an object, ≈ [ {x: 1}, {x: 2}, {x: 3} ]

And with bubbling to the top of an expression, we could make some {} optional, e.g.:

let a = { x: ? }; // Partial expression, ≈ n => ({ x: n })

.

The {} grouping is already used to denote function's body, so is somewhat familiar.

While the syntax might not be obvious at first glance and can be puzzling in complex cases (though, imho, partial
expressions are better suited for short and simple scenarios).

What do you think?

@kosich kosich mentioned this issue May 11, 2021
@trustedtomato
Copy link
Owner Author

I feel like the need for a lookahead is not nice - is there any other language feature which needs it? Other than that, your proposed syntax is neat, I think.
The constraint of having to have a ? in the {} grouping is just fine, since the very reason why this syntax would exist is to eliminate the need for having to name the arguments twice (like in (x, y) => x + y you have to name both x and y twice).
But, I definitely think that the {} shouldn't be optional, since expressions might contain statements which contain expressions.

const a = (() => {
  const b = {
    c: ?
  }
})()
// Is a or b a function?

One thing that I would add is that the ? should always belong to the nearest {}, so that const addTwo = { ? |> { ? + 2 }} works as expected.

The arguments I can think of against your proposed syntax is the need for a lookahead, which comes hand-in-hand with the garden-path problem. A good thing about it is that it only introduces one new symbol, so that the ^ unary operator can be used for something else in the future. I suppose readability is arguable in simple scenarios (and we are examining those).

@kosich
Copy link

kosich commented May 13, 2021

I agree, lookahead is a big downside. Afau, similar lookahead is done to distinguish { block } vs { object }, e.g.:

// Block w/ a labelled loop
{
  abc: while(true) break abc;
}

// inline object
{
  abc: 1
}

though it's not a reason to add to this syntax pile.

.

Regarding partial expressions w/ statements:
Imho, statements should not be directly allowed inside the partial expressions, and partial expressions should strictly be expressions. While statements could be still allowed via the do-expressions proposal. This is a good thing to delegate.

Regarding optional markings:
With above-said, maybe let a = ? + 42 can be allowed w/o enforcing markings.
Also, if pipes go minimal / F# way, partial expressions could be a popular tool there. So a loosened syntax might be desired.

.

And we'll probably get more insights if we try playing with the code.

@trustedtomato
Copy link
Owner Author

Nah, you cannot define an inline object where a statement can go. Your inline object example does not result in an object. Try writing {}.x and ({}).x in the console par exemple - the former results in an error, while the latter results in undefined.

I do find the expression-restriction a bit too quirky - again, there is no existing syntax which doesn't allow an expression to contain a statement in the mentioned sense. Since the syntax will be used for simple function definitions, practically speaking this shouldn't be a problem, but I feel like JavaScript has more than enough gotchas already. The other thing is that if one sees
const add = ? + ?, they might think const twoThreeFour = [1, 2, 3].map(? + 1) should work too, and the thing is that it does work, but unexpectedly, which is arguably worse than having an error.

@kosich
Copy link

kosich commented May 14, 2021

I think, it's trickier in eval mode:

Screenshot 2021-05-14 at 13 51 31

but indeed if a context is understood, either can be predetermined.
Though I'm no expert in JS syntax or parsing. And it's not an argument to pick {}.

.

I now agree that let a = _ + 1 should not have an implicit grouping, since let a = _ is a statement, while a = _ is an expression. A nuance well known, though I didn't think of it when considering grouping. Still have my doubts regarding pipes (but I imagine defining rules w/ pipes special case can be overcomplicated).

Again, we need hands-on experience.

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

No branches or pull requests

2 participants