Feedback sought: Optional Braces

Making } optional is an extremely bad idea. It would be confusing for all users and all tooling while bringing no benefit at all.

7 Likes

Thanks for the feedback!

  • Two space vs more: Three spaces looks nice, four is arguably already too much. But it will be very hard to get the community to shift. I believe the syntax will work for two spaces even if might not be optimal.
  • Multi-line lambdas: Will be worked out next, I hope. It could come in 3.1 or 3.2.
  • Match layout: Note that you can write
     s match
     case A => 
       ...
     case B => 
       ...
     case C => 
       ...
     somethingElse
    
    The syntax allows it. Most people are used to the double indentation however.
2 Likes

Regarding the multiline lambdas. It is already possible to write them without braces or awkward :

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

For some reason xs.map( s => on one line and a single indentation level doesn’t work yet, but I don’t see a fundamental reason why it shouldn’t.

4 Likes

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