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

Parentheses #3

Open
ghost opened this issue Apr 1, 2015 · 12 comments
Open

Parentheses #3

ghost opened this issue Apr 1, 2015 · 12 comments

Comments

@ghost
Copy link

ghost commented Apr 1, 2015

Parentheses are often syntax noise. For example, if we have a silly function like this [1]:

foo(a, b, c) = print '{a} - {b} - {c}'

we can call the function like this [2]:

foo(1, 2, 3)

we can't omit parentheses (like in ruby or coffeescript) [3]

foo 1, 2, 3

I understand the reason. In Earl Grey we could code something like tihs: [4]

p = {1, 2, 3}
foo p

However [3] is the most readable piece of code. So I suggest some syntax sugar like this:

foo! 1, 2, 3

It's just an idea. What do you think about it?
Thank you for Earl Grey.

@breuleux
Copy link
Owner

breuleux commented Apr 3, 2015

Personally I find [2] more readable than [3], because the brackets isolate the function name better. It may be a matter of habit, but I do find for instance that [2] makes skimming easier: if I want to know what operations a piece of code is doing, I can spot parentheses and check what's to their left, whereas blanks are more ambiguous. Furthermore, in Ruby/CoffeeScript, expressions like g(f 1, 2, 3) look confusing. At a glance it looks like the commas separate g's arguments, even though they actually separate f's.

Either way, what you propose is currently impossible because line breaks are exactly like commas, and have the same priority as commas. So if you wrote, for instance:

foo! 1, 2, 3
foo2{}

That would be equivalent to foo! 1, 2, 3, foo2{}.

This being said, I am considering adding back semi-colons to serve as that low-priority separator instead (their usage would not be required, of course). Then I would raise the priority of the comma above that of the with operator (among others like ->, : and where), allowing you to write this:

foo with 1, 2, 3

You might prefer something terser, but ! already has a role that requires higher precedence than commas, and I don't really see what else could be used that doesn't look weird. I like with well enough, but feel free to suggest something else.

@ghost
Copy link
Author

ghost commented Apr 3, 2015

To omit parentheses is not always a good idea, especially if you have a function inside a function. However sometimes I think it makes code more readable. To omit parentheses is always optional.

I think adding back semi-colons is a good idea.

The following code is fine:

  foo with 1, 2, 3

I think it could be better if we could use a symbol as "with" alias. I don't know which symbols are avaible yet (few I guess). Some suggestions:

  foo · 1, 2, 3
  foo | 1, 2, 3
  foo ~ 1, 2, 3 

(Whitespaces could be significative)

If we don't pass any params to a function, instead of:

  bar()

we could code something like this:

  bar·
  bar|
  bar~

Maybe these sytanxes are a little weired. But I'd like to have a "with" keyword shortcut.
Thank you for your reply, breuleux.

@breuleux
Copy link
Owner

breuleux commented Apr 4, 2015

I committed the changes with semicolons and commas (so f with 1, 2, 3 works, as of the latest commit). Perhaps surprisingly given the amount of code, it really didn't break much.

foo · 1, 2, 3 is Unicode and I'd rather not go there right now. | or ~ could work, but I'd rather keep them in reserve, e.g. | would be nice for piping, maybe ~ for partials, I'm not sure.

Really, the best candidate would be foo: 1, 2, 3. foo: already works like foo(), and foo: bar like foo(bar). Unfortunately, foo: 1, 2, 3 currently translates to foo((1, 2, 3)), and that's a tricky corner to get out of because a large number of macros work on this assumption.

Thanks for the feedback :)

@ghost
Copy link
Author

ghost commented Apr 5, 2015

foo: 1, 2, 3

It would be very very nice! :)

@tacomanator
Copy link

Just adding 2c here. I've used LiveScript (which like CoffeeScript allows omission of parens) for quite a while now and have come to the conclusion that, readability aside, [2] leads to less errors than [3]. Consider:

f 1, g 2, 3

Is that f(1,g(2,3)) or f(1,g(2),3)? Even knowing the rules well, it's a common source of errors.

Also I understand that foo: 1 already works but this syntax seems awfully similar to what people are used to as an object key. I could see easily mistaking this for an object key if not readying very carefully. I'm sure you must have had a reason, however, could you shed some light on why you decided to use = instead of : for object definition?

@breuleux
Copy link
Owner

breuleux commented May 8, 2015

In my criteria when designing Earl Grey, I rank consistency over familiarity, meaning that most of the time I will implement unfamiliar syntax if I feel it leads to a more consistent design. In my estimation, few popular languages put any thought into syntactic consistency, which is why EG will sometimes be "weird" (because I try to "correct" what I think they do wrong).

Now, to me, variable definition and object field definition are extremely similar operations: we bind a name to a value, in one case in the lexical scope, in the other case inside an object. Moreover, there is no common situation where it would be ambiguous whether we mean one or the other. So I can't see any good reason why there should be two different operators to do these incredibly similar, non-interfering things. So I pick one operator for definitions (=) and one operator for control structures (:). Surely this is a more consistent choice than Python, which uses the colon for object definitions, for control structures, and for variable annotations. It makes no sense.

I'm not alone in this choice, of course. Lua's table literals use the same syntax as EG.

@tacomanator
Copy link

I think that makes sense and I tend to agree that one need not be held back by convention where there is a good reason. Thank you for taking the time to write your thoughts.

Regarding : for control structures, my first thought us that function invocations take arguments but not control structures, and thus is : a consistent choice for function invocation? Consider using that syntax inside an actual control structure predicate:

;; won't work
if foo: 1:
   ;; do someting

;; will work
if foo(1):
   ;; do something

Are you pretty well set on : or open to other ideas? What about <|?

@breuleux
Copy link
Owner

breuleux commented May 9, 2015

It's an operator priority issue. : is right-associative.

;; will work
if (foo: 1):
   ;; do something

Control structures do take arguments, the idea is that x y: z is sugar for x(y, z) (more or less). A whole lot of control structures fall into the pattern of taking a condition or specification and a body to execute, which is why the sugar works well. Still, it is true that by convention the things to the right of the colon are usually statements and not arguments, so it would be a bit confusing to merge the notations (I personally would not use it).

Anyway, let's see...

foo <| 1, 2, 3

...I don't know, I think <| is kind of ugly. I might just stick with foo with 1, 2, 3. It already works and it's relatively easy to type.

@tacomanator
Copy link

Thanks. Personally I agree, as too many syntaxes that mean the same thing make a language quite complicated.

By the way I took <| operator from LiveScript. They also have a |> operator, which is pretty awesome. At first I thought the operators are ugly as well, but the directionality of them really makes sense when considered as a pair.

@shadowhand
Copy link

I think [2] is the right solution. I very much dislike the omission of parens in CoffeeScript, it makes code unreadable in my opinion.

@breuleux
Copy link
Owner

breuleux commented May 9, 2015

@tacomanator Yeah, there is some elegance to LiveScript's <| and |>. I'm pondering using | for |>, or perhaps for stream composition.

@keithjgrant
Copy link

I agree with @shadowhand -- I never like the omitted parens in CoffeeScript. Far too often I find I'm adding/removing chaining while editing code, which means adding/removing parens, which becomes tedious. Just leave them on. If you're going to use a symbol (other than just a space char), parens are by far the cleanest option listed here.

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