Feedback sought: Optional Braces

No thank you. I like my delimiters matched.

What is the point of this?! Just

xs.map{ s =>
  val n = s.length
  if n < 5 then s * n
  else s.toUpperCase
}

The only benefit is if you don’t need that closing whatever. That the whatever happens to be a paren instead of a brace is immaterial.

3 Likes

The point was that you can just pass a multiline lambda argument between parens like you pass all other kinds of arguments. So braces are not obligated for multiline lambdas. I personally don’t see the harm of writing it like you just did, but most of the complaints are about this very subject.

I feel far less religiously for this than other people. As I’ve already said in the other thread, optional braces makes sense for a lot of reason, one of which is productivity.

I’ve been embracing the brace-free syntax for about a year now (though I haven’t been coding Scala 3 constantly during that time). While some tooling has sometimes been awkward with the new syntax, I don’t think that’s an issue in the long term.

For exclusively writing code, the new syntax doesn’t bother me at all.

My concern is mostly that I find the current syntax hard to read. The : sort of floats around on the screen for me. I imagine this would suck really bad for someone with dyslexia. I’m also really wondering what the new syntax would mean for blind programmers.

I don’t think it’s serious to close the door on optional braces, but I really don’t see why this is something which must be done now. Shipping Scala 3 with optional braces on by default means it can never be rolled back or changed if it’s discovered to be sub optimal for any reason. The significance of this change should not be underestimated!

This is why I think the best solution by far would be to make it an experimental feature in 3.0 and make it official when it’s matured more.

I tried to expand on this reasoning in the angry sister of this thread:

Other reservations about the syntax: (click to expand)

This seems like a half measure to me. Other languages that’s made this change prints warnings when tab characters are found, for good reason.

end markers solve part of the issue but they are not perfect:

3 Likes

I would’ve preferred the syntax to be more similar to that of F# or Haskell. This is why I promoted where in the other thread. This is also a good argument in favor of waiting with this change. Doing so would leave the door open for alternative syntax. Deciding now makes it very hard to consider alternatives. With all respect for that @odersky and others put a lot of time and energy into this, no harm can come from letting people play around with this more before setting it in stone.

1 Like

What I’d like is for end to be loosely checked, maybe I have an

if something:
  //...
end if

Seeing end if is of close to none help, what would be better is end if my condition, eg everything after a space (or delimiter) is ignored, so we can have end C class and end C object

I think it very depends on the way that someone write code. I will not mention dsl or something similar. In general when I am working with sql I often need to use copy\paste\editor’s macros and python syntax makes work more difficult. When I switch to a new\old framework I prefer to copy snippets from stackoverflow and refactor it.
I wonder how often does a usual developer write code from scratch and how often does a usual developer copy\paste\refactor code from google or a similar project. May be it is one of the reason why someone does not like python while others do like. I am sure in one thing this question open the holywar which will never end. At least I do not see the end in python.

For what it’s worth, I end up writing from scratch quite a lot - which generally means multiple revisions and more moving things around as things solidify than when I start refactoring from existing code (which is generally more stable), so the ability to quickly move a block of code from point A to point B is central to my workflow.

3 Likes

I don’t support the prefernce towards indention based code blocks.

  1. What about the tool/formatting support. How is a tool like Intelij to determine what is intended to be. a block and which ones aren’t.
  2. “Being Python friendly”- Why is this a goal for Scala? Python and Scala have a lot of differences that would turn off an experienced Python coder. They also have different uses. Python has a lot of problems that come with the whitespace intentions (Ever mix up tabs vs spaces, or not having enough/too-few spaces… you’re in trouble)
  3. (More of 2.1) How valid are Python developers who make contributions to Scala if they have to have this kind of crutch? (They’re also likely to use python isms where it wouldn’t be appropate in scala)
  4. Copying and pasting blocks is an issue as that the taget has a different scope
  5. This seems only friendly towards teaching new members to the language rather than experienced users who dig through quite a bit of code.
  6. For the suggestion of having start and ending tags. This is a heck of a lot more verbose than single characters to denote the start and end of the blocks. if/fi & while done is quite a pain, also it adds more noise to the code rather than stating intention.
  7. I was never a fan of the if statement for ternary in scala. However would this indention force those statements to be 3 lines instead of 1?
  8. This encourages mixed bracket and indention code bases. That’s no good for maintainability. If your dependences use bracketless and your code uses brackets, that’s a lot of context switching.
  9. Showing code on html based pages. If you’re not using pre the intendentation may not show up. (Multiple scapes are condensed) With brackets tools are able to fix the formatting, with spaces the copy and pasted bit is not able to be reconstructed.
  10. What is the preferred amount of spaces per indentation? Some people say 3, some say 4, some say 5 (tabs), etc. Having a mix there is inconsistent and prevents sharing of code. Now you have to address the person’s code that is sharing it with you to fix that syntax error. (It moves this from a style issue to a syntax error)

Another goal I wonder about this: Is this trying to make code longer? There’s a lot of harm when you get 10+ lines of code in a function. For languages that can do a lot in a single statement this is just asking for buggy code.

Where I support bracketless usage: single statements. That’s common in C++ & Java

3 Likes

Me too, but I always fix the indenting anyway. Don’t you? If you fix it anyway, why does it matter if you must instead of it just being to make it readable?

3 Likes

The difference is that, with braces, getting everything looking nice again requires very little effort on my part - it’s one hotkey and scalafmt handles it for me - without braces, I have to make sure the whole snippet is selected, then hit tab or shift-tab a bunch of times until the indentation is correct.

It doesn’t sound like much, but it breaks flow and while each one doesn’t take much time, it does add up over the course of a day.

9 Likes

That sounds like a pretty easily fixable tooling issue for IDEs/editors.

1 Like

If it hasn’t been fixable for Python, why would it be fixable for us? It’s not like IntelliJ has a reserve of particularly talented people they’re keeping away from their Python tooling :wink:

7 Likes

+1. Yes once I started using it, I also started to like the new syntax a lot both in src/main/ side of the code and also in writing src/test/ unit tests.

My main concern around the current implementation of the indentation syntax is that it is implemented as an optional indentation, not just optional braces. In other words, it admits code that looks like:

class A:
  val x = 1
    val y = 2 // superfluous indentation

class B:
  println("foo")
    println("bar") // superfluous indentation

class C:
  def addY(y: Int) = 
    val x = 10
      x // superfluous indentation
    + y

If we are going to associate some parsing logic to whitespaces, I think we need to be unambiguous what that meaning is, and reject any superfluous indentations within indentation-based blocks. https://github.com/lampepfl/dotty/issues/10671. Draft PR for the implementation is https://github.com/lampepfl/dotty/pull/10691.

Python for reference would reject these as “unexpected indent”:

>>> def addY(y):
...   x = 10
...     x
  File "<stdin>", line 3
    x
IndentationError: unexpected indent

For cross building purpose, we should continue to admit superfluous indentation within Scala 2 style curly-brace syntax.

class C {
  def addY(y: Int) = {
    val x = 10
      x // ok
    + y
  }
}

This allows the users to opt-in to the strict “off-side rule” when they switch over to indent-based syntax.

5 Likes

I should’ve included the actual superfluous indentation that bit me while trying MUnit tests:

class Test:
  test("hello")
    print("hmm")
  def test(name: String)(body: => Any) = ()
new Test

In the above, print("hmm") is not passed into test(...)(...), but still compiles. I’m not sure if 58% of the Scala users would see that coming if they see it out of puzzler context: https://twitter.com/eed3si9n/status/1336011029717934080

8 Likes

No, it’s not, really.

The notion that tooling and language design are two separate concerns is a naive one: languages with fewer and more regular features are easier write tools for and the opposite is true. Go has excellent tooling support, since the language is very simple, and Scala has historically had poor tooling also because of some particularly complex features (from a tooling perspective). Think of macros, implicits, relative imports, to name a few.

We’re historically in a golden age for Scala tools: most tools are reaching maturity and adding new great features at a pace it was unthinkable 3/4 years ago.

I won’t comment on the feature from a user perspective, since I didn’t try it first-hand, but I’ll ask a question, for @odersky, mostly: have you considered the impact it will have on tooling, specifically code formatters like IntelliJ’s one and Scalafmt?

This is not something we can (yet) just try, since the lack of support for it, so I would at least expect an analytic approach where language authors and tool authors sit at a table and discuss the impact of a drastic change like this one.

The lack of good tooling has hindered Scala adoption since its inception, and now that we’re finally enjoying some amazing tools it would be foolish to set them back a few years because of a lack inclusion during the decision making process.

This stands in general for all features and all tools, but code formatting for significant indentation syntax is one where we have a shred of evidence from Python that it’s a complicated and largely unsolved issue even in a popular language where tooling is quite extensive.

Bottom line, I want to like this proposed feature, but I would be much much more re-assured if tool authors would be included more directly into the decision process.

23 Likes

The feature you asked for is already a relatively easy macro in Sublime Text:

I just don’t buy that a random answer on Stack Overflow contains something that’s beyond the capacity of teams of programmers at JetBrains or wherever. Mechanically it’s very simple. (That doesn’t do the auto-indent, but spamming tab a few times isn’t hard…the selecting is the hard part.)

Well, today I wrote them like that, which makes the comma very explicit:

fold(
    i => if (i < 189571) bippy(i/2)
         else            whatchamacallit(i, i + 1) < 893475

  , j => if (lovely(j)) checkIfFlower(j)
         else           false
)

And that’s example is even valid scala2.
Actually, leading comma make everything easier to parse/skim through in most cases.

1 Like

I am fairly agnostic about the indentation feature at this point, as I haven’t used it yet, nor have I used Python or other languages with significant indentation (except I had some limited exposure to CoffeeScript, see below). But I’d like to keep an open mind and I have followed this proposed feature’s saga since the beginning with interest.

First, looking over the examples pointed by Martin, one aspect that I think is a definite plus is the ability to name end blocks, like:

end PatternValDef

You can, of course, do this with comments right now, like:

} // end PatternValDef

But having native syntax (which tools can easily deal with when refactoring/renaming) seems like a plus for readability.

Second, I find that, in our Scala 2 codebase, we already tend to prefer not using brackets when we can. For example our if/else containing a single expression per branch look like this:

if (expr)
  42
else
  43

Similarly, many short function/method definitions look like this:

def foo: Int =
  42

And combined:

def foo: Int =
  if (expr)
    42
  else
    43

So we have already way fewer braces than, say, in a Java or C codebase. I think that this supports the idea that Scala is already its own thing, syntactically (while 10 years ago Scala was still being sold as much closer to Java), and that going one step further is not going to be a big(ger) issue for newcomers and veteran Scala programmers alike.

Now, of course, the approach above has limits with Scala 2, and my example above has to be changed if you add just one line to the expressions:

def foo: Int =
  if (expr) {
    val result = 21 * 2
    result
  } else { // I don't like asymmetric if/else either so brackets here too
    43
  }

This is something that I find annoying today (and Scala shares this with many languages). It’s my understanding that (sometimes) significant indentation would allow for the following:

def foo: Int =
  if (expr)
    val result = 21 * 2
    result
  else
    43

For me, it would help solve the “should I use braces or not” dilemma at least in these scenarios, and I think that I would lean towards not using braces in such cases. To me, in this simple example at least, the code is not less readable than the version with braces. On the contrary, I would say that it feels a little more readable at a glance. How does this scale to a large codebase, I don’t know.

(To be fair, tools like IntelliJ now can help you add the braces automatically when you want to enter a new line in a single-expression if/else branch.)

Overall, I am probably 60-75% convinced that the proposed syntax might be a plus once warts are taken care of.

Some of my remaining uncertainty comes from a small negative experience with CoffeeScript, which removes so much syntax that CoffeeScript code appears to me like a sea of tokens. In such a context, it is very hard to tell apart variable names, function names, and function parameters. I think that going too far in removing syntactic symbols reduces legibility of the code. For this reason, I am all for keeping parentheses around parameters lists, for example (except maybe when writing DSL-style code). Similarly, arrows in pattern matching visually help.

5 Likes

Regarding adjusting pasted code…

Copying and moving code around within my codebase (or from one to another) is something I do quite often.

It was mentioned that with Metals, with braces this is as simple as pasting and running the formatter. I just wanted to mention that in IntelliJ, it’s even easier. It will fix the indentation on pasting it.

Regarding the main topic:

  1. It’s not only important whether people will like it after they try it. It’s also important whether this will attract more people to Scala, or cause more people to shy away from looking into it seriously. One of the most common reactions I see when someone mentions Scala, which seems almost taboo in the first place in some online forums, is that it’s like a bunch of different languages in one. Personally I think that couldn’t be further from the truth but that’s because I understand how Scala thinks about things. Other people don’t. First impressions matter. I’m not saying optional braces will harm Scala’s adoption, I’m saying it might, and we should try to figure out if it would or not.

  2. I strongly agree it should be marked experimental and require a flag, even if everyone uses it. This would be like Scala 2 macros, where everyone understood they could be changed later and chose to opt in anyway.

7 Likes

I don’t see how that is fixable. Your tools have to somehow know what you intend to be in the same block.

  • If you use a pair of braces, your tools can deduce the blocks from the braces and the indentation from the blocks.
  • If you use indentation, your tool needs the indentation to deduce the blocks, so there is not way it can deduce the indentation.
4 Likes