Scala 3 significant indentation

Experimentation is great and it would be terrible for Martin and co to have been burdened by the SIP process every time they want to experiment. I think everyone’s ok with the SIP process not being involved so early. My understanding was that between the experimentation and Scala 3.0 final being released, the SIP process would kick in so that people can both 1) understand the proposed changes, and 2) contribute to the proposal, usually to strengthen it. It doesn’t seem to be the case anymore. Even yourself is expecting that “the SIP process will be observed for most changes within the mainline language path once Scala 3 final is released” which doesn’t make sense to me because once it’s released we become slaves to backwards-compatibility (unless a feature is hidden behind a -Y flag) and major changes either become very slow to implement (eg. waiting 3 years for 3.1) or impossible until 4.0.

According to the more recent comments (kind of like rumour but by people in the in-group which wouldn’t be the case if we have an official SIP), when 3.0 is released significant whitespace is going to be enabled by default, and won’t be considered experimental. Once that happens, a retrospective SIP would be very limited in terms of efficacy. It’s like me building a granny flat for myself in your backyard, all the while saying “don’t worry, once it’s built and I move in, we can have a discussion about it”.

I mean I’d actually be fine if Martin just said “our team gave this a lot of thought, we’re just going to do what we believe is best.” The mixed messages, lack of clarity and (what seems to me to be) contradictory behaviour is the problem. SIPs or no SIPs anymore? SIPs only for some? What’s the “some” criteria? If SIP for this then when? If post 3.0, why and how limited? Shouldn’t the Scala Center being publicly clarifying this because the current state doesn’t seem to match the process last publicly declared by the Scala Center?

6 Likes

The change of the compiler backend has nothing to do with the issue of (sometimes) optional braces. While it is true that the change of the backend inevitably implied some changes, this one was completely independent and could have easily passed through the SIP process.

And how do you know a SIP would not have changed anything?

Even if the outcome was preordained, following the process would have meant transparency and offered a place for feedback.

And why would the SIP process not come to a “non-dictatorial solution”? That’s exactly what a process is for: it is the only way to come to a non-dictatorial conclusion. At the end, it either gets the necessary votes (two thirds of the SIP committee and no veto by Martin) and is adopted, or the status quo prevails.

The only reason I can think of why the outcome is preordained is if the SIP committee is solidly behind it. But if that is so, I don’t see how it can become a long fight.

2 Likes

Well, it’s a bit of a hen and egg problem. We’re here now with two camps arguing for and against the whitespace, with some random examples demonstrating how horrible it could be. But without it being really in the wild, it’s purely hypothetical if anything works out the way one assumes or not. Only now with the milestones released, are people beginning to cross-compile part of their ecosystem, not even talking about “pure” Dotty projects that start from scratch. I think without having a half-ways stable compiler and ecosystem ready, it’s very difficult to experiment/experience with this sort of thing.

In any case, I would be surprised if there wouldn’t be any changes and adaptations necessary between 3.0 and 3.x.

While that’s a fair point, there’s also a ton of prior art to consider so it’s not like we’re navel-gazing. Most of these concerns are based in similar constructs in other languages (Python being the most immediately relevant), and if there’s been an effort to learn from the pain points encountered working in those languages, it been entirely opaque and seems to have missed a lot of really obvious pitfalls.

An experiment of this sort would be much less of a problem if it weren’t shipping as the default mode and influencing other features.

1 Like

This is why IMO the timespan from milestone release to RC is way too short.

In most projects, development towards a new release happens in a somewhat linear fashion, and milestones signal that some identifiable portion of that work has been completed and is ready to be tested. Release candidate means that all the work is believed to be done, subject to discovery of release-blocking issues. For example, if a release takes a year, there might be a milestone every few months, and at the end of the year they put out an RC, which will become final if no bugs are discovered, and they usually are, so that cycle repeats a few times until the RC indeed becomes the release.

In this case however, the whole time it seems like everything was being worked on at once, and anything was subject to revision at any time. That’s fine, something like this needs that sort of experimentation. But when everything is in flux, and everyone knows that because there are no proper milestone releases, the percentage of the community that sinks their teeth into it is limited. Certainly in this case it was no small amount. But there are a lot of people who have not even begun porting yet. Some of those are libraries that are upstream of other projects.

I’m glad we’re in the milestone phase already and I know people want Scala 3 to be released already after so many years, but personally I think we need more time until RC. We need to have a longer time where we signal the community “we’re done experimenting, now is the last chance to give feedback before we go into RC and nothing can change except fixing bugs.”

5 Likes

Thanks for your post @nafg, what you wrote is true and makes sense. We ourselves are trying to find the best balance as each step is made, and as you also said, there are a lot of steps at the same time at the moment (feature discussions, tooling and documentation development, updating education, and so forth).

Last and this week it became apparent that community onboarding will take more time than we estimated, so we owe it to all of you to revisit the timeline.

By the end of this week, we should have enough input to propose something sustainable, please stay tuned.

12 Likes

With a long history of writing in other C style languages, and disliking Python due to its indentation based syntax then I would have thought that I would naturally come down on preferring braces style syntax.

However, when I write Scala 2 code I note that I already generally try and leave out braces when I can, e.g. case statements, single lined methods and expressions. I also note that regardless of whether braces are present or not, I want and expect code to be reasonably formatted anyway.

I also note that Python is a very popular language, particularly for beginners, and perhaps the indentation based grammar plays a part in that.

So, I actually expect to try the new quiet syntax in Scala 3 to see how it works in practice, with the knowledge that I can probably just ignore it (in my code) if I don’t like it. I’m not yet sure about the end block marker, and perhaps I will still use braces for classes, traits, objects and longer method definitions. But I am willing to give it a try, with a reasonably possibility that I may end up liking it.

Probably I would have preferred if this feature was marked as being somewhat experimental for 3.0 with a view to running a longer wide community experiment and then deciding in say 3.1 or 3.2 whether this should really become the default syntax, or whether it is an unpopular experiment and should gradually be deprecated.

5 Likes

Amen. Just chiming in to say that I always thought end Foo was ugly and verbose.

I have always needed braces in order to match them in vi.

However, after transitioning to optional braces, now I appreciate end syntax for its intended purpose. Namely, “sorry this method is too long, let me show you where it ends”.

I’ll add that I think tooling support is required given braceless syntax.

2 Likes

When I work on python I usually spend more time on manual formatting because idea do not understand syntax until I format it correctrly, so “significant indentation” is usual less productive for me for writing. I have no problem with reading code, because code is usually formatted in any case.

5 Likes

I think it’s a shame that the topic of significant indentation is always so heated. I sympathize fully with Martin’s reasons for wanting optional braces. When writing code, it’s a pain to fit in braces every time you need to add an extra statement for debugging or other things.

For writing code, I think it’s exclusively a change for the better once tooling (like IntelliJ which currently refuses to indent how I intend all the time) catches up.

For reading, I’m not so sure. I’ve never understood the religious sentiment against having lines with just braces. For long methods or template definitions where it can be hard to find the beginning of the definition, most editors provide some way of navigating to the start of a brace or parenthesis. Providing support for such navigation is much more complicated in the absence of braces.

When I’ve read the Dotty code base, it’s been extremely useful to be able to easily find the beginning and end of a definition using Ctrl+M in Sublime Text.

end markers only solve part of the problem. It tells me what definition just ended, but that doesn’t really help me if I find myself in the middle of a definition and I want to find the beginning. I welcome an example of where it would be useful, but my spontaneous reaction is that it just adds noise where a } would’ve been clearer. It also makes me think of languages with beginend block syntax, which I personally think is very hard to read. The end marker is also not unambiguous, as shown by this example:

class C:
  //Ridiculous amount of code
end C

object C:
  //Ridiculous amount of code
end C

If i now read end C, how can I know if I’m looking at the class C or it’s companion object?

I’m also not sold on using : for starting a block. : already has different meaning depending on whether it’s used in a context bound, or for giving a type to an expression or definition. It could be argued that it’s easy to distinguish the new meaning from the other ones, but I remember seeing arguments (which I can’t find now) against introducing syntax like forall T. T for polymorphic function types, because it would be confusing to introduce new meaning for ‘.’, which is a reasonable objection.

Personally, I would’ve preferred a where marker:

trait A where
  def f: Int

class C(x: Int) extends A where
  def f = x

object O where
  def f = 3

enum Color where
  case Red, Green, Blue

type T = A where
  def f: Int

given [T](using Ord[T]) as Ord[List[T]] where
  def compare(x: List[T], y: List[T]) = ???

extension (xs: List[Int]) where
  def second: Int = xs.tail.head
  def third: Int = xs.tail.tail.head

new A where
  def f = 3

Or simply drop : without replacement.

There are many cases where braces make code more readable and many where the conciseness of leaving them out makes code easier to write and read. Personally I think optional braces would overall be a good addition. I also completely disagree with the opinion that braces must be optional everywhere or nowhere. That said, there are details in the syntax that could be discussed more considering how big change it is and how permanent it is. It will be really hard to change once it’s been established.

7 Likes

The elephant in the room being that if the tooling to make working with significant indentation easy still isn’t there for a language as popular as Python, it’s a bit hubristic to think the folks behind IntelliJ, VSCode, etc will be willing or able to do so for the much smaller Scala community. I’m not saying it’s impossible to get there, just that there’s already sufficient motivation for it to have happened already, so we should temper expectations appropriately.

1 Like

JetBrains did a lot for the Scala community, and the begin/end versus { } problem won’t exist, as IntelliJ already does the fully semantic parsing instead of mechanic { } detection. The rest will be using Metals in some form, so …

3 Likes

Just want to say that I think where is pretty great. Only for lambdas (where -Yindent-colons uses :) it doesn’t really seem to fit.

2 Likes

rcano: There’s always an exact opposite opinion I’m sure. I certainly agree with that. I just have my perspective I was sharing based on my use.

1 Like

That’s fair, it’s far more likely that it’s a case of “not able” than “not willing”, as they’ve done a ton of work with implicits and similar. I just don’t see how they’re suddenly going to be able to smooth over the pain points because we’re asking for it, when the Python community has been dealing with this for literal decades.

3 Likes

Just wanted to add my 2 cents and say that I don’t think there’s anything wrong with braces, and I don’t know what the hurry is to try to get rid of them. They’re a tried and true solution to demarcating scope that’s simple and elegant, IMO.

If Martin and others want to add the ability for braces to be optional, I think that’s fine, as long as people can continue to use braces if they want to.

2 Likes

Interestingly, I had to actively search for the curly braces to identify which snippet was which.

2 Likes

I read your post, looked at the snippets, and still couldn’t see the braces at first :slight_smile:

Unfortunately, I have’t figured out how to show two code snippets side-by-side, which would probably give a better comparison.

Interestingly, that’s one of the things I like about the braces - they’re there when I need them, and remarkably unobtrusive when I don’t :slight_smile:

1 Like

I think the optional braces needs us to be more careful when merging code.

3 Likes