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

Alternative Syntax for Control Expressions #7024

Merged
merged 2 commits into from
Aug 27, 2019

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Aug 11, 2019

This PR implements a new "quiet" syntax for control expressions that does not rely in
enclosing the condition in parentheses, and also allows to drop parentheses or braces
around the generators of a for-expression. Examples:

if x < 0 then -x else x

while x >= 0 do x = f(x)

for x <- xs if x > 0
yield x * x

for
  x <- xs
  y <- ys
do
  println(x + y)

The rules in detail are:

  • The condition of an if-expression can be written without enclosing parentheses if it is followed by a then.
  • The condition of a while-loop can be written without enclosing parentheses if it is followed by a do.
  • The enumerators of a for-expression can be written without enclosing parentheses or braces if they are followed by a yield or do.
  • A do in a for-expression expresses a for-loop.
  • Newline characters are not statement separators in a condition of an if or a while.
    So the meaning of newlines is the same no matter whether parentheses are present
    or absent.
  • Newline characters are statement separators in the enumerators of a for-expression. This turns out to be not a problem because Allow infix operators at start of line #7031 basically removed the need to put expressions in parentheses to get nice operator formatting.

Rewrites

The Dotty compiler can rewrite source code from old syntax and new syntax and back.
When invoked with options -rewrite -new-syntax it will rewrite from old to new syntax, dropping parentheses and braces in conditions and enumerators. When invoked with with options -rewrite -old-syntax it will rewrite in the reverse direction, inserting parentheses and braces as needed.

Copy link
Member

@dottybot dottybot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, and thank you for opening this PR! 🎉

All contributors have signed the CLA, thank you! ❤️

Commit Messages

We want to keep history, but for that to actually be useful we have
some rules on how to format our commit messages (relevant xkcd).

Please stick to these guidelines for commit messages:

  1. Separate subject from body with a blank line
  2. When fixing an issue, start your commit message with Fix #<ISSUE-NBR>:
  3. Limit the subject line to 72 characters
  4. Capitalize the subject line
  5. Do not end the subject line with a period
  6. Use the imperative mood in the subject line ("Add" instead of "Added")
  7. Wrap the body at 80 characters
  8. Use the body to explain what and why vs. how

adapted from https://chris.beams.io/posts/git-commit

Have an awesome day! ☀️

@jducoeur
Copy link
Contributor

Pure opinion: I really dislike this sort of syntax -- I find it significantly harder to scan code and understand the control flow in keyword-centric languages like this. It's the sort of thing that would actively nudge me away from Scala.

I get that it's just being presented as an option, and that's fine in principle, but I gotta say: it seems contrary to the general bent of the Scala 3 project, of being a bit more opinionated about style. I would probably forbid it in any codebase I'm leading...

@odersky odersky force-pushed the rewrite-clean branch 2 times, most recently from df88fac to 6d6180d Compare August 12, 2019 07:35
@sjrd
Copy link
Member

sjrd commented Aug 12, 2019

I am very concerned about this. I grew up with Pascal/Delphi, so I'm not against the new syntax per se. I am concerned about the change.

The issue is that it will inevitably lead to a fracture of the ecosystem. A lot of code will use the old syntax, including all the existing material in books and on the Web. And supposedly a lot of new code will use the new syntax. We'll have zillions of people wondering how to read other people's code, or if the two things are the same, or which one they should use, etc.

We've seen this problem before for other things, for example implicit. However in this case we're talking about an entirely different scale: every single Scala file will be affected! From a beginner's code to an expert's. And for what? A purely syntactic change. The ratio benefit/migration pain will be extremely low for this change. Much lower than other changes we've been seeing in Scala 3.

The low ratio is not helped by the fact that I see little in the way of motivation for this change. With a historical record of everything that's been discussed the past few years, I can see this being motivated in the context of indentation-based syntax, which is a radically different syntax aspect. With such a change, there are objective reasons to switch to if then and while do, notably the fact that, otherwise, there is no way to write multi-statement conditions without introducing braces in a world that is otherwise brace-free.

In the context of brace-based syntax, I could see a much more restrictive version of this proposal: allow braces instead of parens for the condition of while and if. That would bring them in line with for (for which we have the choice of {} or ()) and would avoid the argued-as-ugly ({ ... }), allowing to write:

while {
  val c = str.charAt(i)
  c >= '0' && c <= '9'
} {
  i += 1
}

Aside:

Speaking of indentation-based syntax, using it would basically require treating newlines in conditions as statement separators, so that I could for example write the following:

while
  val c = str.charAt(i)
  c >= '0' && c <= '9'
do
  ...

If we don't treat them as newlines now, then treating them as newlines with indentation syntax would be an even more problematic change. For that reason, at least newlines should be treated as statement separators now (and hence systematically fail to parse, since we are not in a block under non-indentation-based syntax), if there is still any ounce of desire to have indentation-based syntax in the future.

@odersky
Copy link
Contributor Author

odersky commented Aug 12, 2019

@jducoeur @sjrd This is not intended to stay an alternative for long. My current thinking is that we should standardize on the new syntax, and rewrite all old syntax automatically (this PR proves that this is feasible). Using indentation or not is a separate aspect. I am ready to argue that the new syntax is vastly superior even if we stick with braces.

@hrhino
Copy link
Contributor

hrhino commented Aug 12, 2019

What's changed since last time?

allow braces instead of parens for the condition of while and if

This is a nice tweak, and I'd hope it could make it in. Notably, it doesn't require rewriting existing code, nor would such a rewrite rule be widely applicable.

I do like the while { ... } do { ... } syntax, probably because adjacent close/open braces look grating to me.

@propensive
Copy link
Contributor

I'm cautiously, hesitantly willing to accept this new style once I remind myself that it's only syntax. I've no doubt we'll all get used to it.

However, one minor benefit of the current brace-based syntax is that text editors and IDEs can very easily match an opening to a closing brace, and indicate this to the user.

@rubenfiszel
Copy link

I for one really like the added symmetry and welcome the change

@molikto
Copy link
Contributor

molikto commented Aug 14, 2019

I don't think rewrite tools solves all problems, there are people generating Scala code automately, or projects uses modified Scala syntax (https://github.com/lihaoyi/Scalatex), also all the code snippets written in various blog posts, forums on the web. It will just be a lot of pain.

I wonder if instead we can do a structural editor with configurable syntax presentation (I am interested in experiment with this).

@odersky
Copy link
Contributor Author

odersky commented Aug 22, 2019

Rebased on top of #7031

Allow drop drop parens or braces in control expressions

Use for-do, for-yield, while-do, if-then-else instead.
@odersky
Copy link
Contributor Author

odersky commented Aug 22, 2019

Squashed to a single commit on top of #7031

@jdegoes
Copy link

jdegoes commented Aug 23, 2019

Very beautiful improvement to the syntax of Scala. 👍

This streamlined syntax emphasizes the expression-oriented nature of Scala (reminiscent of the change in method syntax, def foo: Unit { } vs def foo: Unit = ???). Expression-orientation, and more generally, functional programming, are among the strengths of Scala; so I personally welcome the change.

I do think rewriting and deprecation of the old syntax are both important for any change of this magnitude: rewriting to ease migration, deprecation to enforce a single style in large code bases.

Now I know some will object to this change, on grounds it makes Scala a worse Better Java. There is a kernel of truth here; those who love curly braces will find more streamlined syntax yet another reason to stick with Java or Kotlin. But I would suggest that this change makes Scala a better Scala, and that the Better Java market is not a growth market for Scala.

Python has also shown, with its tremendous success, that the lack of parentheses, curly braces or the presence of indentation-sensitive syntax are no impediments to adoption or tooling (indeed, most languages would be lucky to have a fraction of Python's success in these areas).

In summary, this change is a great example of Scala "doubling-down" on Scala—a change courageously opinionated enough to (yes) give some another reason to stay with Java, but also bold enough to galvanize those who love the conciseness and expressive power of Scala, and who would love to see a change this impactful to end-user ergonomics and aesthetics.

@diesalbla
Copy link

diesalbla commented Aug 24, 2019

The Dotty compiler can rewrite source code from old syntax and new syntax and back.

If one believes in "Make each program do one thing well", one would avoid adding any code rewrite features to a compiler and let a separate tool, like scalafmt, to handle that in its stead.

Copy link
Member

@bishabosha bishabosha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@odersky odersky merged commit 6d16b93 into scala:master Aug 27, 2019
@odersky odersky deleted the rewrite-clean branch August 27, 2019 14:16
@anatoliykmetyuk anatoliykmetyuk added this to the 0.18 Tech Preview milestone Aug 28, 2019
@ryanberckmans
Copy link

@odersky might we support golang-style control flow syntax, which is omission of conditional parentheses but still braces around the block?

(I have limited interest in significant indentation syntax and will most likely keep using curly braces. But I find your exploration of "radical" changes to be inspiring and will give it a try, especially given the excellent tool support to convert sources :-)

Golang style might look like

def hello(x: Int): Unit = if x > 5 { 
  println(x)
  println(x*2)
}

@som-snytt
Copy link
Contributor

I love this change. It's like those mornings I remember to clean my eyeglasses. The rest of the day is better and clearer.

However, for a very long time, I've been accustomed to vi getting confused by stray braces in comments and then failing to match braces correctly, so I'll have to get used to not dealing with that issue.

@jxtps
Copy link

jxtps commented Sep 6, 2019

IFF we go full indentation-crazy, how about allowing the user to skip the new keywords and instead literally double down on indentation: use an extra indentation to denote what's inside the missing parenthesis:

if x < 0 && 
    y > 1  // <-- extra indent means it's "inside the ()"
  -x 
else 
  x

while x >= 0 && 
    y > 1 // <-- extra indent means it's "inside the ()"
  x = f(x)

for x <- xs 
    if x > 0 // <-- extra indent means it's "inside the ()"
yield 
  x * x

for x <- xs
    y <- ys // <-- extra indent means it's "inside the ()"
  println(x + y)

Maybe it's a little better if the "inside the ()" part lines up with the keyword+space:

if x < 0 && 
   y > 1  // <-- extra indent means it's "inside the ()"
  -x 
else 
  x

while x >= 0 && 
      y > 1 // <-- extra indent means it's "inside the ()"
  x = f(x)

for x <- xs 
    if x > 0 // <-- extra indent means it's "inside the ()"
yield 
  x * x

for x <- xs
    y <- ys // <-- extra indent means it's "inside the ()"
  println(x + y)

Maybe too subtle for the if since it's only one space off from the regular code? A subtle-ish fix for that would be to introduce an extra space after the if when there's multi-line logic (yes, that has a host of issues):

if  x < 0 && 
    y > 1  // <-- extra indent means it's "inside the ()"
  -x 
else 
  x

Not sure I like it, but wanted to put it out there as it's not going to get much lighter than this, and I hadn't seen this in any of the significant indentation discussions.

@neontorrent
Copy link

This got me thinking - I feel like we are heading to the wrong direction.

To get Scala more widely used, is changing syntax the correct way? Scala needs a killer use like other popular languages (Rails for Ruby, ReactNative for JS, Android Dev for Kotlin, etc.), and, to me, syntax is not the road blocker for getting people to use it.

@ryanberckmans
Copy link

ryanberckmans commented Sep 11, 2019

@texasbruce

To get Scala more widely used, is changing syntax the correct way?

Yes, this is an important question. There may be a better place to discuss this, but let's start here.

In my opinion the main blocker to Scala's adoption (besides a reputation for brutal compile times) is the pervasive conventions that define idiomatic Scala usage.

I wrote about this https://contributors.scala-lang.org/t/principles-for-implicits-in-scala-3/3072/51

if typeclasses are such a powerful, obvious, necessary part of Scala, why is there no Monoid in the standard library?

I think "Step 1a" to improving mass adoption of Scala is to merge cats into the standard library.

EDIT: I started a reddit thread.

@nafg
Copy link

nafg commented Sep 11, 2019

To me the question is, since cannot predict but only speculate on the future, what is the potential gain, and what is the potential risk.

For example, there could be a scenario where due to changed syntax scala becomes X% more popular, but there could be another conceivable scenario where it results in a total of Y% less popularity. What are values for X and Y that we should think about?

@jackmaney
Copy link

To get Scala more widely used, is changing syntax the correct way?

Looking from the outside in, the Scala community is--to put it as politely as I can--a dumpster fire. Fixing that will have a much better effect than any syntax changes.

@ryanberckmans
Copy link

@jackmaney I'd just like to offer a counter perspective in that, being relatively new to Scala, I've been pleasantly delighted by the language, standard library, 3rd party libraries, helpful Q&A on reddit, community members with whom I've interacted, etc.

I am particularly impressed by the thoughtfulness and execution so far on the transition to Scala 3. The talks from Scala Days 2019 were great. The communication around Dotty is great. The Dotty website is great.

@leobenkel
Copy link

Having the indentation part of the syntax is a terrible idea.

Code style and code meaning should be two distinct things.

Also, I like my curly to eye ball where is the start and end of blocks.
You can put your cursor on one and instantly know where the other end is.
You can copy paste code without breaking its functionality.

Have you even copy paste Python code from one place to another ? It is a nightmare.

@ryantheleach
Copy link

ryantheleach commented Sep 12, 2019

I may be a Scala newbie, but this new-style of syntax would cause me to not migrate willingly to Scala 3 as a hobbyist.

It's my belief that using keywords and implied grouping, over explicit parenthesis and braces, really hurts readability.

The only saving grace, is that it encourages simpler branches and loops, and breaking the conditions onto a previous line, as it gets unreadable so quickly.

People practicing python have embraced that as 'pythonic'.

In addition, the extra keypresses / screen realestate of the keyword 'then' is irritating.

@philliptaylorpro
Copy link

As a former Perl developer I think this serves no purpose other than to create more syntax to learn before you can be proficient in Scala and both slows you down and knocks your confidence as you read other people's code.

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

Successfully merging this pull request may close these issues.